content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.app.admin;
  
  import android.annotation.IntDef;
  import android.annotation.TestApi;
  import android.annotation.UnsupportedAppUsage;
  import android.content.ComponentName;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.os.SystemProperties;
  import android.util.EventLog.Event;
  
  import java.io.IOException;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.Collection;
  import java.util.Objects;
  
  /**
   * Definitions for working with security logs.
   *
   * <p>Device owner apps can control the logging with
   * {@link DevicePolicyManager#setSecurityLoggingEnabled}. When security logs are enabled, device
   * owner apps receive periodic callbacks from {@link DeviceAdminReceiver#onSecurityLogsAvailable},
   * at which time new batch of logs can be collected via
   * {@link DevicePolicyManager#retrieveSecurityLogs}. {@link SecurityEvent} describes the type and
   * format of security logs being collected.
   */
  public class SecurityLog {
  
      private static final String PROPERTY_LOGGING_ENABLED = "persist.logd.security";
  
      /** @hide */
      @Retention(RetentionPolicy.SOURCE)
      @IntDef(prefix = { "TAG_" }, value = {
              TAG_ADB_SHELL_INTERACTIVE,
              TAG_ADB_SHELL_CMD,
              TAG_SYNC_RECV_FILE,
              TAG_SYNC_SEND_FILE,
              TAG_APP_PROCESS_START,
              TAG_KEYGUARD_DISMISSED,
              TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
              TAG_KEYGUARD_SECURED,
              TAG_OS_STARTUP,
              TAG_OS_SHUTDOWN,
              TAG_LOGGING_STARTED,
              TAG_LOGGING_STOPPED,
              TAG_MEDIA_MOUNT,
              TAG_MEDIA_UNMOUNT,
              TAG_LOG_BUFFER_SIZE_CRITICAL,
              TAG_PASSWORD_EXPIRATION_SET,
              TAG_PASSWORD_COMPLEXITY_SET,
              TAG_PASSWORD_HISTORY_LENGTH_SET,
              TAG_MAX_SCREEN_LOCK_TIMEOUT_SET,
              TAG_MAX_PASSWORD_ATTEMPTS_SET,
              TAG_KEYGUARD_DISABLED_FEATURES_SET,
              TAG_REMOTE_LOCK,
              TAG_USER_RESTRICTION_ADDED,
              TAG_USER_RESTRICTION_REMOVED,
              TAG_WIPE_FAILURE,
              TAG_KEY_GENERATED,
              TAG_KEY_IMPORT,
              TAG_KEY_DESTRUCTION,
              TAG_CERT_AUTHORITY_INSTALLED,
              TAG_CERT_AUTHORITY_REMOVED,
              TAG_CRYPTO_SELF_TEST_COMPLETED,
              TAG_KEY_INTEGRITY_VIOLATION,
              TAG_CERT_VALIDATION_FAILURE,
      })
      public @interface SecurityLogTag {}
  
      /** @hide */
      @Retention(RetentionPolicy.SOURCE)
      @IntDef(prefix = { "LEVEL_" }, value = {
              LEVEL_INFO,
              LEVEL_WARNING,
              LEVEL_ERROR
      })
      public @interface SecurityLogLevel {}
  
      /**
       * Indicates that an ADB interactive shell was opened via "adb shell".
       * There is no extra payload in the log event.
       */
      public static final int TAG_ADB_SHELL_INTERACTIVE =
              SecurityLogTags.SECURITY_ADB_SHELL_INTERACTIVE;
  
      /**
       * Indicates that a shell command was issued over ADB via {@code adb shell <command>}
       * The log entry contains a {@code String} payload containing the shell command, accessible
       * via {@link SecurityEvent#getData()}.
       */
      public static final int TAG_ADB_SHELL_CMD = SecurityLogTags.SECURITY_ADB_SHELL_COMMAND;
  
      /**
       * Indicates that a file was pulled from the device via the adb daemon, for example via
       * {@code adb pull}. The log entry contains a {@code String} payload containing the path of the
       * pulled file on the device, accessible via {@link SecurityEvent#getData()}.
       */
      public static final int TAG_SYNC_RECV_FILE = SecurityLogTags.SECURITY_ADB_SYNC_RECV;
  
      /**
       * Indicates that a file was pushed to the device via the adb daemon, for example via
       * {@code adb push}. The log entry contains a {@code String} payload containing the destination
       * path of the pushed file, accessible via {@link SecurityEvent#getData()}.
       */
      public static final int TAG_SYNC_SEND_FILE = SecurityLogTags.SECURITY_ADB_SYNC_SEND;
  
      /**
       * Indicates that an app process was started. The log entry contains the following
       * information about the process encapsulated in an {@link Object} array, accessible via
       * {@link SecurityEvent#getData()}:
       * <li> [0] process name ({@code String})
       * <li> [1] exact start time in milliseconds according to {@code System.currentTimeMillis()}
       *      ({@code Long})
       * <li> [2] app uid ({@code Integer})
       * <li> [3] app pid ({@code Integer})
       * <li> [4] seinfo tag ({@code String})
       * <li> [5] SHA-256 hash of the base APK in hexadecimal ({@code String})
       */
      public static final int TAG_APP_PROCESS_START = SecurityLogTags.SECURITY_APP_PROCESS_START;
  
      /**
       * Indicates that keyguard has been dismissed. This event is only logged if the device
       * has a secure keyguard. It is logged regardless of how keyguard is dismissed, including
       * via PIN/pattern/password, biometrics or via a trust agent.
       * There is no extra payload in the log event.
       * @see #TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT
       */
      public static final int TAG_KEYGUARD_DISMISSED = SecurityLogTags.SECURITY_KEYGUARD_DISMISSED;
  
      /**
       * Indicates that there has been an authentication attempt to dismiss the keyguard. The log
       * entry contains the following information about the attempt encapsulated in an {@link Object}
       * array, accessible via {@link SecurityEvent#getData()}:
       * <li> [0] attempt result ({@code Integer}, 1 for successful, 0 for unsuccessful)
       * <li> [1] strength of authentication method ({@code Integer}, 1 if strong authentication
       *      method was used, 0 otherwise)
       */
      public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT =
              SecurityLogTags.SECURITY_KEYGUARD_DISMISS_AUTH_ATTEMPT;
  
      /**
       * Indicates that the device has been locked, either by the user or by a timeout. There is no
       * extra payload in the log event.
       */
      public static final int TAG_KEYGUARD_SECURED = SecurityLogTags.SECURITY_KEYGUARD_SECURED;
  
      /**
       * Indicates that the Android OS has started. The log entry contains the following information
       * about the startup time software integrity check encapsulated in an {@link Object} array,
       * accessible via {@link SecurityEvent#getData()}:
       * <li> [0] Verified Boot state ({@code String})
       * <li> [1] dm-verity mode ({@code String}).
       * <p>Verified Boot state can be one of the following:
       * <li> {@code green} indicates that there is a full chain of trust extending from the
       * bootloader to verified partitions including the bootloader, boot partition, and all verified
       * partitions.
       * <li> {@code yellow} indicates that the boot partition has been verified using the embedded
       * certificate and the signature is valid.
       * <li> {@code orange} indicates that the device may be freely modified. Device integrity is
       * left to the user to verify out-of-band.
       * <p>dm-verity mode can be one of the following:
       * <li> {@code enforcing} indicates that the device will be restarted when corruption is
       * detected.
       * <li> {@code eio} indicates that an I/O error will be returned for an attempt to read
       * corrupted data blocks.
       * For details see Verified Boot documentation.
       */
      public static final int TAG_OS_STARTUP = SecurityLogTags.SECURITY_OS_STARTUP;
  
      /**
       * Indicates that the Android OS has shutdown. There is no extra payload in the log event.
       */
      public static final int TAG_OS_SHUTDOWN = SecurityLogTags.SECURITY_OS_SHUTDOWN;
  
      /**
       * Indicates start-up of audit logging. There is no extra payload in the log event.
       */
      public static final int TAG_LOGGING_STARTED = SecurityLogTags.SECURITY_LOGGING_STARTED;
  
      /**
       * Indicates shutdown of audit logging. There is no extra payload in the log event.
       */
      public static final int TAG_LOGGING_STOPPED = SecurityLogTags.SECURITY_LOGGING_STOPPED;
  
      /**
       * Indicates that removable media has been mounted on the device. The log entry contains the
       * following information about the event, encapsulated in an {@link Object} array and
       * accessible via {@link SecurityEvent#getData()}:
       * <li> [0] mount point ({@code String})
       * <li> [1] volume label ({@code String}).
       */
      public static final int TAG_MEDIA_MOUNT = SecurityLogTags.SECURITY_MEDIA_MOUNTED;
  
      /**
       * Indicates that removable media was unmounted from the device. The log entry contains the
       * following information about the event, encapsulated in an {@link Object} array and
       * accessible via {@link SecurityEvent#getData()}:
       * <li> [0] mount point ({@code String})
       * <li> [1] volume label ({@code String}).
       */
      public static final int TAG_MEDIA_UNMOUNT = SecurityLogTags.SECURITY_MEDIA_UNMOUNTED;
  
      /**
       * Indicates that the audit log buffer has reached 90% of its capacity. There is no extra
       * payload in the log event.
       */
      public static final int TAG_LOG_BUFFER_SIZE_CRITICAL =
              SecurityLogTags.SECURITY_LOG_BUFFER_SIZE_CRITICAL;
  
      /**
       * Indicates that an admin has set a password expiration timeout. The log entry contains the
       * following information about the event, encapsulated in an {@link Object} array and accessible
       * via {@link SecurityEvent#getData()}:
       * <li> [0] admin package name ({@code String})
       * <li> [1] admin user ID ({@code Integer})
       * <li> [2] target user ID ({@code Integer})
       * <li> [3] new password expiration timeout in milliseconds ({@code Long}).
       * @see DevicePolicyManager#setPasswordExpirationTimeout(ComponentName, long)
       */
      public static final int TAG_PASSWORD_EXPIRATION_SET =
              SecurityLogTags.SECURITY_PASSWORD_EXPIRATION_SET;
  
      /**
       * Indicates that an admin has set a requirement for password complexity. The log entry contains
       * the following information about the event, encapsulated in an {@link Object} array and
       * accessible via {@link SecurityEvent#getData()}:
       * <li> [0] admin package name ({@code String})
       * <li> [1] admin user ID ({@code Integer})
       * <li> [2] target user ID ({@code Integer})
       * <li> [3] minimum password length ({@code Integer})
       * <li> [4] password quality constraint ({@code Integer})
       * <li> [5] minimum number of letters ({@code Integer})
       * <li> [6] minimum number of non-letters ({@code Integer})
       * <li> [7] minimum number of digits ({@code Integer})
       * <li> [8] minimum number of uppercase letters ({@code Integer})
       * <li> [9] minimum number of lowercase letters ({@code Integer})
       * <li> [10] minimum number of symbols ({@code Integer})
       *
       * @see DevicePolicyManager#setPasswordMinimumLength(ComponentName, int)
       * @see DevicePolicyManager#setPasswordQuality(ComponentName, int)
       * @see DevicePolicyManager#setPasswordMinimumLetters(ComponentName, int)
       * @see DevicePolicyManager#setPasswordMinimumNonLetter(ComponentName, int)
       * @see DevicePolicyManager#setPasswordMinimumLowerCase(ComponentName, int)
       * @see DevicePolicyManager#setPasswordMinimumUpperCase(ComponentName, int)
       * @see DevicePolicyManager#setPasswordMinimumNumeric(ComponentName, int)
       * @see DevicePolicyManager#setPasswordMinimumSymbols(ComponentName, int)
       */
      public static final int TAG_PASSWORD_COMPLEXITY_SET =
              SecurityLogTags.SECURITY_PASSWORD_COMPLEXITY_SET;
  
      /**
       * Indicates that an admin has set a password history length. The log entry contains the
       * following information about the event encapsulated in an {@link Object} array, accessible
       * via {@link SecurityEvent#getData()}:
       * <li> [0] admin package name ({@code String})
       * <li> [1] admin user ID ({@code Integer})
       * <li> [2] target user ID ({@code Integer})
       * <li> [3] new password history length value ({@code Integer})
       * @see DevicePolicyManager#setPasswordHistoryLength(ComponentName, int)
       */
      public static final int TAG_PASSWORD_HISTORY_LENGTH_SET =
              SecurityLogTags.SECURITY_PASSWORD_HISTORY_LENGTH_SET;
  
      /**
       * Indicates that an admin has set a maximum screen lock timeout. The log entry contains the
       * following information about the event encapsulated in an {@link Object} array, accessible
       * via {@link SecurityEvent#getData()}:
       * <li> [0] admin package name ({@code String})
       * <li> [1] admin user ID ({@code Integer})
       * <li> [2] target user ID ({@code Integer})
       * <li> [3] new screen lock timeout in milliseconds ({@code Long})
       * @see DevicePolicyManager#setMaximumTimeToLock(ComponentName, long)
       */
      public static final int TAG_MAX_SCREEN_LOCK_TIMEOUT_SET =
              SecurityLogTags.SECURITY_MAX_SCREEN_LOCK_TIMEOUT_SET;
  
      /**
       * Indicates that an admin has set a maximum number of failed password attempts before wiping
       * data. The log entry contains the following information about the event encapsulated in an
       * {@link Object} array, accessible via {@link SecurityEvent#getData()}:
       * <li> [0] admin package name ({@code String})
       * <li> [1] admin user ID ({@code Integer})
       * <li> [2] target user ID ({@code Integer})
       * <li> [3] new maximum number of failed password attempts ({@code Integer})
       * @see DevicePolicyManager#setMaximumFailedPasswordsForWipe(ComponentName, int)
       */
      public static final int TAG_MAX_PASSWORD_ATTEMPTS_SET =
              SecurityLogTags.SECURITY_MAX_PASSWORD_ATTEMPTS_SET;
  
      /**
       * Indicates that an admin has set disabled keyguard features. The log entry contains the
       * following information about the event encapsulated in an {@link Object} array, accessible via
       * {@link SecurityEvent#getData()}:
       * <li> [0] admin package name ({@code String})
       * <li> [1] admin user ID ({@code Integer})
       * <li> [2] target user ID ({@code Integer})
       * <li> [3] disabled keyguard feature mask ({@code Integer}).
       * @see DevicePolicyManager#setKeyguardDisabledFeatures(ComponentName, int)
       */
      public static final int TAG_KEYGUARD_DISABLED_FEATURES_SET =
              SecurityLogTags.SECURITY_KEYGUARD_DISABLED_FEATURES_SET;
  
      /**
       * Indicates that an admin remotely locked the device or profile. The log entry contains the
       * following information about the event encapsulated in an {@link Object} array, accessible via
       * {@link SecurityEvent#getData()}:
       * <li> [0] admin package name ({@code String}),
       * <li> [1] admin user ID ({@code Integer}).
       * <li> [2] target user ID ({@code Integer})
       */
      public static final int TAG_REMOTE_LOCK = SecurityLogTags.SECURITY_REMOTE_LOCK;
  
      /**
       * Indicates a failure to wipe device or user data. There is no extra payload in the log event.
       */
      public static final int TAG_WIPE_FAILURE = SecurityLogTags.SECURITY_WIPE_FAILED;
  
      /**
       * Indicates that an authentication key was generated. The log entry contains the following
       * information about the event, encapsulated in an {@link Object} array and accessible via
       * {@link SecurityEvent#getData()}:
       * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
       * <li> [1] alias of the key ({@code String})
       * <li> [2] requesting process uid ({@code Integer}).
       */
      public static final int TAG_KEY_GENERATED =
              SecurityLogTags.SECURITY_KEY_GENERATED;
  
      /**
       * Indicates that a cryptographic key was imported. The log entry contains the following
       * information about the event, encapsulated in an {@link Object} array and accessible via
       * {@link SecurityEvent#getData()}:
       * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
       * <li> [1] alias of the key ({@code String})
       * <li> [2] requesting process uid ({@code Integer}).
       */
      public static final int TAG_KEY_IMPORT = SecurityLogTags.SECURITY_KEY_IMPORTED;
  
      /**
       * Indicates that a cryptographic key was destroyed. The log entry contains the following
       * information about the event, encapsulated in an {@link Object} array and accessible via
       * {@link SecurityEvent#getData()}:
       * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
       * <li> [1] alias of the key ({@code String})
       * <li> [2] requesting process uid ({@code Integer}).
       */
      public static final int TAG_KEY_DESTRUCTION = SecurityLogTags.SECURITY_KEY_DESTROYED;
  
      /**
       * Indicates that a new root certificate has been installed into system's trusted credential
       * storage. The log entry contains the following information about the event, encapsulated in an
       * {@link Object} array and accessible via {@link SecurityEvent#getData()}:
       * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
       * <li> [1] subject of the certificate ({@code String}).
       */
      public static final int TAG_CERT_AUTHORITY_INSTALLED =
              SecurityLogTags.SECURITY_CERT_AUTHORITY_INSTALLED;
  
      /**
       * Indicates that a new root certificate has been removed from system's trusted credential
       * storage. The log entry contains the following information about the event, encapsulated in an
       * {@link Object} array and accessible via {@link SecurityEvent#getData()}:
       * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
       * <li> [1] subject of the certificate ({@code String}).
       */
      public static final int TAG_CERT_AUTHORITY_REMOVED =
              SecurityLogTags.SECURITY_CERT_AUTHORITY_REMOVED;
  
      /**
       * Indicates that an admin has set a user restriction. The log entry contains the following
       * information about the event, encapsulated in an {@link Object} array and accessible via
       * {@link SecurityEvent#getData()}:
       * <li> [0] admin package name ({@code String})
       * <li> [1] admin user ID ({@code Integer})
       * <li> [2] user restriction ({@code String})
       * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
       */
      public static final int TAG_USER_RESTRICTION_ADDED =
              SecurityLogTags.SECURITY_USER_RESTRICTION_ADDED;
  
      /**
       * Indicates that an admin has removed a user restriction. The log entry contains the following
       * information about the event, encapsulated in an {@link Object} array and accessible via
       * {@link SecurityEvent#getData()}:
       * <li> [0] admin package name ({@code String})
       * <li> [1] admin user ID ({@code Integer})
       * <li> [2] user restriction ({@code String})
       * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
       */
      public static final int TAG_USER_RESTRICTION_REMOVED =
              SecurityLogTags.SECURITY_USER_RESTRICTION_REMOVED;
  
      /**
       * Indicates that cryptographic functionality self test has completed. The log entry contains an
       * {@code Integer} payload, indicating the result of the test (0 if the test failed, 1 if
       * succeeded) and accessible via {@link SecurityEvent#getData()}.
       */
      public static final int TAG_CRYPTO_SELF_TEST_COMPLETED =
              SecurityLogTags.SECURITY_CRYPTO_SELF_TEST_COMPLETED;
  
      /**
       * Indicates a failed cryptographic key integrity check. The log entry contains the following
       * information about the event, encapsulated in an {@link Object} array and accessible via
       * {@link SecurityEvent#getData()}:
       * <li> [0] alias of the key ({@code String})
       * <li> [1] owner application uid ({@code Integer}).
       */
      public static final int TAG_KEY_INTEGRITY_VIOLATION =
              SecurityLogTags.SECURITY_KEY_INTEGRITY_VIOLATION;
  
      /**
       * Indicates a failure to validate X.509v3 certificate. The log entry contains a {@code String}
       * payload indicating the failure reason, accessible via {@link SecurityEvent#getData()}.
       */
      public static final int TAG_CERT_VALIDATION_FAILURE =
              SecurityLogTags.SECURITY_CERT_VALIDATION_FAILURE;
  
      /**
       * Event severity level indicating that the event corresponds to normal workflow.
       */
      public static final int LEVEL_INFO = 1;
  
      /**
       * Event severity level indicating that the event may require admin attention.
       */
      public static final int LEVEL_WARNING = 2;
  
      /**
       * Event severity level indicating that the event requires urgent admin action.
       */
      public static final int LEVEL_ERROR = 3;
  
      /**
       * Returns if security logging is enabled. Log producers should only write new logs if this is
       * true. Under the hood this is the logical AND of whether device owner exists and whether
       * it enables logging by setting the system property {@link #PROPERTY_LOGGING_ENABLED}.
       * @hide
       */
      public static native boolean isLoggingEnabled();
  
      /**
       * @hide
       */
      public static void setLoggingEnabledProperty(boolean enabled) {
          SystemProperties.set(PROPERTY_LOGGING_ENABLED, enabled ? "true" : "false");
      }
  
      /**
       * @hide
       */
      public static boolean getLoggingEnabledProperty() {
          return SystemProperties.getBoolean(PROPERTY_LOGGING_ENABLED, false);
      }
  
      /**
       * A class representing a security event log entry.
       */
      public static final class SecurityEvent implements Parcelable {
          private Event mEvent;
          private long mId;
  
          /**
           * Constructor used by native classes to generate SecurityEvent instances.
           * @hide
           */
          @UnsupportedAppUsage
          /* package */ SecurityEvent(byte[] data) {
              this(0, data);
          }
  
          /**
           * Constructor used by Parcelable.Creator to generate SecurityEvent instances.
           * @hide
           */
          /* package */ SecurityEvent(Parcel source) {
              this(source.readLong(), source.createByteArray());
          }
  
          /** @hide */
          @TestApi
          public SecurityEvent(long id, byte[] data) {
              mId = id;
              mEvent = Event.fromBytes(data);
          }
  
          /**
           * Returns the timestamp in nano seconds when this event was logged.
           */
          public long getTimeNanos() {
              return mEvent.getTimeNanos();
          }
  
          /**
           * Returns the tag of this log entry, which specifies entry's semantics.
           */
          public @SecurityLogTag int getTag() {
              return mEvent.getTag();
          }
  
          /**
           * Returns the payload contained in this log entry or {@code null} if there is no payload.
           */
          public Object getData() {
              return mEvent.getData();
          }
  
          /**
           * @hide
           */
          public void setId(long id) {
              this.mId = id;
          }
  
          /**
           * Returns the id of the event, where the id monotonically increases for each event. The id
           * is reset when the device reboots, and when security logging is enabled.
           */
          public long getId() {
              return mId;
          }
  
          /**
           * Returns severity level for the event.
           */
          public @SecurityLogLevel int getLogLevel() {
              switch (mEvent.getTag()) {
                  case TAG_ADB_SHELL_INTERACTIVE:
                  case TAG_ADB_SHELL_CMD:
                  case TAG_SYNC_RECV_FILE:
                  case TAG_SYNC_SEND_FILE:
                  case TAG_APP_PROCESS_START:
                  case TAG_KEYGUARD_DISMISSED:
                  case TAG_KEYGUARD_SECURED:
                  case TAG_OS_STARTUP:
                  case TAG_OS_SHUTDOWN:
                  case TAG_LOGGING_STARTED:
                  case TAG_LOGGING_STOPPED:
                  case TAG_MEDIA_MOUNT:
                  case TAG_MEDIA_UNMOUNT:
                  case TAG_PASSWORD_EXPIRATION_SET:
                  case TAG_PASSWORD_COMPLEXITY_SET:
                  case TAG_PASSWORD_HISTORY_LENGTH_SET:
                  case TAG_MAX_SCREEN_LOCK_TIMEOUT_SET:
                  case TAG_MAX_PASSWORD_ATTEMPTS_SET:
                  case TAG_USER_RESTRICTION_ADDED:
                  case TAG_USER_RESTRICTION_REMOVED:
                      return LEVEL_INFO;
                  case TAG_CERT_AUTHORITY_REMOVED:
                  case TAG_CRYPTO_SELF_TEST_COMPLETED:
                      return getSuccess() ? LEVEL_INFO : LEVEL_ERROR;
                  case TAG_CERT_AUTHORITY_INSTALLED:
                  case TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT:
                  case TAG_KEY_IMPORT:
                  case TAG_KEY_DESTRUCTION:
                  case TAG_KEY_GENERATED:
                      return getSuccess() ? LEVEL_INFO : LEVEL_WARNING;
                  case TAG_LOG_BUFFER_SIZE_CRITICAL:
                  case TAG_WIPE_FAILURE:
                  case TAG_KEY_INTEGRITY_VIOLATION:
                      return LEVEL_ERROR;
                  case TAG_CERT_VALIDATION_FAILURE:
                      return LEVEL_WARNING;
                  default:
                      return LEVEL_INFO;
              }
          }
  
          // Success/failure if present is encoded as an integer in the first (0th) element of data.
          private boolean getSuccess() {
              final Object data = getData();
              if (data == null || !(data instanceof Object[])) {
                  return false;
              }
  
              final Object[] array = (Object[]) data;
              return array.length >= 1 && array[0] instanceof Integer && (Integer) array[0] != 0;
          }
  
  
          @Override
          public int describeContents() {
              return 0;
          }
  
          @Override
          public void writeToParcel(Parcel dest, int flags) {
              dest.writeLong(mId);
              dest.writeByteArray(mEvent.getBytes());
          }
  
          public static final @android.annotation.NonNull Parcelable.Creator<SecurityEvent> CREATOR =
                  new Parcelable.Creator<SecurityEvent>() {
              @Override
              public SecurityEvent createFromParcel(Parcel source) {
                  return new SecurityEvent(source);
              }
  
              @Override
              public SecurityEvent[] newArray(int size) {
                  return new SecurityEvent[size];
              }
          };
  
          /**
           * @hide
           */
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              SecurityEvent other = (SecurityEvent) o;
              return mEvent.equals(other.mEvent) && mId == other.mId;
          }
  
          /**
           * @hide
           */
          @Override
          public int hashCode() {
              return Objects.hash(mEvent, mId);
          }
  
          /** @hide */
          public boolean eventEquals(SecurityEvent other) {
              return other != null && mEvent.equals(other.mEvent);
          }
      }
      /**
       * Retrieve all security logs and return immediately.
       * @hide
       */
      public static native void readEvents(Collection<SecurityEvent> output) throws IOException;
  
      /**
       * Retrieve all security logs since the given timestamp in nanoseconds and return immediately.
       * @hide
       */
      public static native void readEventsSince(long timestamp, Collection<SecurityEvent> output)
              throws IOException;
  
      /**
       * Retrieve all security logs before the last reboot. May return corrupted data due to
       * unreliable pstore.
       * @hide
       */
      public static native void readPreviousEvents(Collection<SecurityEvent> output)
              throws IOException;
  
      /**
       * Retrieve all security logs whose timestamp is equal to or greater than the given timestamp in
       * nanoseconds. This method will block until either the last log earlier than the given
       * timestamp is about to be pruned, or after a 2-hour timeout has passed.
       * @hide
       */
      public static native void readEventsOnWrapping(long timestamp, Collection<SecurityEvent> output)
              throws IOException;
  
      /**
       * Write a log entry to the underlying storage, with a string payload.
       * @hide
       */
      public static native int writeEvent(int tag, String str);
  
      /**
       * Write a log entry to the underlying storage, with several payloads.
       * Supported types of payload are: integer, long, float, string plus array of supported types.
       * @hide
       */
      public static native int writeEvent(int tag, Object... payloads);
  }
  