Merge "Log additional events in security log."
diff --git a/api/current.txt b/api/current.txt
index ec3c9d3..de3be1f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6724,20 +6724,46 @@
 
   public class SecurityLog {
     ctor public SecurityLog();
+    field public static final int LEVEL_ERROR = 3; // 0x3
+    field public static final int LEVEL_INFO = 1; // 0x1
+    field public static final int LEVEL_WARNING = 2; // 0x2
     field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
     field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
     field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
+    field public static final int TAG_CERT_AUTHORITY_INSTALLED = 210029; // 0x3346d
+    field public static final int TAG_CERT_AUTHORITY_REMOVED = 210030; // 0x3346e
+    field public static final int TAG_KEYGUARD_DISABLED_FEATURES_SET = 210021; // 0x33465
     field public static final int TAG_KEYGUARD_DISMISSED = 210006; // 0x33456
     field public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT = 210007; // 0x33457
     field public static final int TAG_KEYGUARD_SECURED = 210008; // 0x33458
+    field public static final int TAG_KEY_DESTRUCTION = 210026; // 0x3346a
+    field public static final int TAG_KEY_GENERATED = 210024; // 0x33468
+    field public static final int TAG_KEY_IMPORT = 210025; // 0x33469
+    field public static final int TAG_LOGGING_STARTED = 210011; // 0x3345b
+    field public static final int TAG_LOGGING_STOPPED = 210012; // 0x3345c
+    field public static final int TAG_LOG_BUFFER_SIZE_CRITICAL = 210015; // 0x3345f
+    field public static final int TAG_MAX_PASSWORD_ATTEMPTS_SET = 210020; // 0x33464
+    field public static final int TAG_MAX_SCREEN_LOCK_TIMEOUT_SET = 210019; // 0x33463
+    field public static final int TAG_MEDIA_MOUNT = 210013; // 0x3345d
+    field public static final int TAG_MEDIA_UNMOUNT = 210014; // 0x3345e
+    field public static final int TAG_OS_SHUTDOWN = 210010; // 0x3345a
+    field public static final int TAG_OS_STARTUP = 210009; // 0x33459
+    field public static final int TAG_PASSWORD_COMPLEXITY_SET = 210017; // 0x33461
+    field public static final int TAG_PASSWORD_EXPIRATION_SET = 210016; // 0x33460
+    field public static final int TAG_PASSWORD_HISTORY_LENGTH_SET = 210018; // 0x33462
+    field public static final int TAG_REMOTE_LOCK = 210022; // 0x33466
     field public static final int TAG_SYNC_RECV_FILE = 210003; // 0x33453
     field public static final int TAG_SYNC_SEND_FILE = 210004; // 0x33454
+    field public static final int TAG_USER_RESTRICTION_ADDED = 210027; // 0x3346b
+    field public static final int TAG_USER_RESTRICTION_REMOVED = 210028; // 0x3346c
+    field public static final int TAG_WIPE_FAILURE = 210023; // 0x33467
   }
 
   public static final class SecurityLog.SecurityEvent implements android.os.Parcelable {
     method public int describeContents();
     method public java.lang.Object getData();
     method public long getId();
+    method public int getLogLevel();
     method public int getTag();
     method public long getTimeNanos();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index d3b66d0..08effd9 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.TestApi;
+import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemProperties;
@@ -53,64 +54,367 @@
             TAG_APP_PROCESS_START,
             TAG_KEYGUARD_DISMISSED,
             TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
-            TAG_KEYGUARD_SECURED
+            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,
     })
     public @interface SecurityLogTag {}
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "LEVEL_" }, value = {
+            LEVEL_INFO,
+            LEVEL_WARNING,
+            LEVEL_ERROR
+    })
+    public @interface SecurityLogLevel {}
+
     /**
-     * Indicate that an ADB interactive shell was opened via "adb shell".
+     * 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;
+
     /**
-     * Indicate that an shell command was issued over ADB via "adb shell command"
-     * The log entry contains a string data of the shell command, accessible via
-     * {@link SecurityEvent#getData()}
+     * 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;
+
     /**
-     * Indicate that a file was pulled from the device via the adb daemon, for example via
-     * "adb pull". The log entry contains a string data of the path of the pulled file,
-     * accessible via {@link SecurityEvent#getData()}
+     * 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;
+
     /**
-     * Indicate that a file was pushed to the device via the adb daemon, for example via
-     * "adb push". The log entry contains a string data of the destination path of the
-     * pushed file, accessible via {@link SecurityEvent#getData()}
+     * 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;
+
     /**
-     * Indicate that an app process was started. The log entry contains the following
+     * 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()}:
-     * process name (String), exact start time (long), app Uid (integer), app Pid (integer),
-     * seinfo tag (String), SHA-256 hash of the base APK in hexadecimal (String)
+     * <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;
+
     /**
-     * Indicate that keyguard is being dismissed.
+     * Indicates that keyguard has been dismissed.
      * There is no extra payload in the log event.
      */
-    public static final int TAG_KEYGUARD_DISMISSED =
-            SecurityLogTags.SECURITY_KEYGUARD_DISMISSED;
+    public static final int TAG_KEYGUARD_DISMISSED = SecurityLogTags.SECURITY_KEYGUARD_DISMISSED;
+
     /**
-     * Indicate 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()}:
-     * attempt result (integer, 1 for successful, 0 for unsuccessful), strength of auth method
-     * (integer, 1 if strong auth method was used, 0 otherwise)
+     * 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;
+
     /**
-     * Indicate that the device has been locked, either by user or by timeout.
-     * There is no extra payload in the log event.
+     * 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#setMaximumTimeToLock(ComponentName, long)
+     */
+    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}).
+     */
+    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 oot 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;
+
+    /**
+     * 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}.
@@ -198,6 +502,60 @@
             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:
+                    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:
+                    return LEVEL_ERROR;
+                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;
@@ -263,8 +621,8 @@
             throws IOException;
 
     /**
-     * Retrieve all security logs whose timestamp (in nanosceonds) is equal to or greater than the
-     * given timestamp. This method will block until either the last log earlier than the given
+     * 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
      */
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index 39371c7..be62678 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -10,3 +10,28 @@
 210006 security_keyguard_dismissed
 210007 security_keyguard_dismiss_auth_attempt   (success|1),(method_strength|1)
 210008 security_keyguard_secured
+
+# Additional event types for NIAP MDFPP 3.1 compliant audit logging.
+
+210009 security_os_startup                      (boot_state|3),(verity_mode|3)
+210010 security_os_shutdown
+210011 security_logging_started
+210012 security_logging_stopped
+210013 security_media_mounted                   (path|3),(label|3)
+210014 security_media_unmounted                 (path|3),(label|3)
+210015 security_log_buffer_size_critical
+210016 security_password_expiration_set         (package|3),(admin_user|1),(target_user|1),(timeout|2|3)
+210017 security_password_complexity_set         (package|3),(admin_user|1),(target_user|1),(length|1),(quality|1),(num_letters|1),(num_non_letters|1),(num_numeric|1),(num_uppercase|1),(num_lowercase|1),(num_symbols|1)
+210018 security_password_history_length_set     (package|3),(admin_user|1),(target_user|1),(length|1)
+210019 security_max_screen_lock_timeout_set     (package|3),(admin_user|1),(target_user|1),(timeout|2|3)
+210020 security_max_password_attempts_set       (package|3),(admin_user|1),(target_user|1),(num_failures|1)
+210021 security_keyguard_disabled_features_set  (package|3),(admin_user|1),(target_user|1),(features|1)
+210022 security_remote_lock                     (package|3),(admin_user|1),(target_user|1)
+210023 security_wipe_failed                     (package|3),(admin_user|1)
+210024 security_key_generated                   (success|1),(key_id|3),(uid|1)
+210025 security_key_imported                    (success|1),(key_id|3),(uid|1)
+210026 security_key_destroyed                   (success|1),(key_id|3),(uid|1)
+210027 security_user_restriction_added          (package|3),(admin_user|1),(restriction|3)
+210028 security_user_restriction_removed        (package|3),(admin_user|1),(restriction|3)
+210029 security_cert_authority_installed        (success|1),(subject|3)
+210030 security_cert_authority_removed          (success|1),(subject|3)
\ No newline at end of file
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 8ec4ef6..84b93e3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -36,6 +36,7 @@
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.KeyguardManager;
+import android.app.admin.SecurityLog;
 import android.app.usage.StorageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -50,7 +51,6 @@
 import android.content.res.Configuration;
 import android.content.res.ObbInfo;
 import android.database.ContentObserver;
-import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.DropBoxManager;
@@ -150,7 +150,6 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -1275,6 +1274,29 @@
             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
                     OBB_FLUSH_MOUNT_STATE, vol.path));
         }
+        maybeLogMediaMount(vol, newState);
+    }
+
+    private void maybeLogMediaMount(VolumeInfo vol, int newState) {
+        if (!SecurityLog.isLoggingEnabled()) {
+            return;
+        }
+
+        final DiskInfo disk = vol.getDisk();
+        if (disk == null || (disk.flags & (DiskInfo.FLAG_SD | DiskInfo.FLAG_USB)) == 0) {
+            return;
+        }
+
+        // Sometimes there is a newline character.
+        final String label = disk.label != null ? disk.label.trim() : "";
+
+        if (newState == VolumeInfo.STATE_MOUNTED
+                || newState == VolumeInfo.STATE_MOUNTED_READ_ONLY) {
+            SecurityLog.writeEvent(SecurityLog.TAG_MEDIA_MOUNT, vol.path, label);
+        } else if (newState == VolumeInfo.STATE_UNMOUNTED
+                || newState == VolumeInfo.STATE_BAD_REMOVAL) {
+            SecurityLog.writeEvent(SecurityLog.TAG_MEDIA_UNMOUNT, vol.path, label);
+        }
     }
 
     private void onMoveStatusLocked(int status) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index b986e04..eed3102 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -21,6 +21,7 @@
 import android.app.Dialog;
 import android.app.IActivityManager;
 import android.app.ProgressDialog;
+import android.app.admin.SecurityLog;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -390,6 +391,10 @@
             }
         }
 
+        if (SecurityLog.isLoggingEnabled()) {
+            SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
+        }
+
         // start the thread that initiates shutdown
         sInstance.mHandler = new Handler() {
         };
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 918a355..0589790 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3313,6 +3313,7 @@
         cleanUpOldUsers();
         maybeSetDefaultProfileOwnerUserRestrictions();
         handleStartUser(UserHandle.USER_SYSTEM);
+        maybeLogStart();
 
         // Register an observer for watching for user setup complete and settings changes.
         mSetupContentObserver.register();
@@ -3368,6 +3369,16 @@
         updateSystemUpdateFreezePeriodsRecord(/* saveIfChanged */ true);
     }
 
+    private void maybeLogStart() {
+        if (!SecurityLog.isLoggingEnabled()) {
+            return;
+        }
+        final String verifiedBootState =
+                mInjector.systemPropertiesGet("ro.boot.verifiedbootstate");
+        final String verityMode = mInjector.systemPropertiesGet("ro.boot.veritymode");
+        SecurityLog.writeEvent(SecurityLog.TAG_OS_STARTUP, verifiedBootState, verityMode);
+    }
+
     private void ensureDeviceOwnerUserStarted() {
         final int userId;
         synchronized (this) {
@@ -3874,14 +3885,17 @@
         Preconditions.checkNotNull(who, "ComponentName is null");
         validateQualityConstant(quality);
 
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
-            if (ap.minimumPasswordMetrics.quality != quality) {
-                ap.minimumPasswordMetrics.quality = quality;
-                updatePasswordValidityCheckpointLocked(mInjector.userHandleGetCallingUserId());
-                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
+            if (metrics.quality != quality) {
+                metrics.quality = quality;
+                updatePasswordValidityCheckpointLocked(userId);
+                saveSettingsLocked(userId);
             }
+            maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
     }
 
@@ -3974,14 +3988,17 @@
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
-            if (ap.minimumPasswordMetrics.length != length) {
-                ap.minimumPasswordMetrics.length = length;
-                updatePasswordValidityCheckpointLocked(mInjector.userHandleGetCallingUserId());
-                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
+            if (metrics.length != length) {
+                metrics.length = length;
+                updatePasswordValidityCheckpointLocked(userId);
+                saveSettingsLocked(userId);
             }
+            maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
     }
 
@@ -3997,15 +4014,21 @@
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
             if (ap.passwordHistoryLength != length) {
                 ap.passwordHistoryLength = length;
-                updatePasswordValidityCheckpointLocked(mInjector.userHandleGetCallingUserId());
-                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+                updatePasswordValidityCheckpointLocked(userId);
+                saveSettingsLocked(userId);
             }
         }
+        if (SecurityLog.isLoggingEnabled()) {
+            final int affectedUserId = parent ? getProfileParentId(userId) : userId;
+            SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_HISTORY_LENGTH_SET,
+                    who.getPackageName(), userId, affectedUserId, length);
+        }
     }
 
     @Override
@@ -4039,6 +4062,11 @@
             // in case this is the first one, set the alarm on the appropriate user.
             setExpirationAlarmCheckLocked(mContext, userHandle, parent);
         }
+        if (SecurityLog.isLoggingEnabled()) {
+            final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
+            SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_EXPIRATION_SET, who.getPackageName(),
+                    userHandle, affectedUserId, timeout);
+        }
     }
 
     /**
@@ -4187,14 +4215,17 @@
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
-            ActiveAdmin ap = getActiveAdminForCallerLocked(
+            final ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
-            if (ap.minimumPasswordMetrics.upperCase != length) {
-                ap.minimumPasswordMetrics.upperCase = length;
-                updatePasswordValidityCheckpointLocked(mInjector.userHandleGetCallingUserId());
-                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
+            if (metrics.upperCase != length) {
+                metrics.upperCase = length;
+                updatePasswordValidityCheckpointLocked(userId);
+                saveSettingsLocked(userId);
             }
+            maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
     }
 
@@ -4207,14 +4238,17 @@
     @Override
     public void setPasswordMinimumLowerCase(ComponentName who, int length, boolean parent) {
         Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
-            if (ap.minimumPasswordMetrics.lowerCase != length) {
-                ap.minimumPasswordMetrics.lowerCase = length;
-                updatePasswordValidityCheckpointLocked(mInjector.userHandleGetCallingUserId());
-                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
+            if (metrics.lowerCase != length) {
+                metrics.lowerCase = length;
+                updatePasswordValidityCheckpointLocked(userId);
+                saveSettingsLocked(userId);
             }
+            maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
     }
 
@@ -4230,14 +4264,17 @@
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
-            if (ap.minimumPasswordMetrics.letters != length) {
-                ap.minimumPasswordMetrics.letters = length;
-                updatePasswordValidityCheckpointLocked(mInjector.userHandleGetCallingUserId());
-                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
+            if (metrics.letters != length) {
+                metrics.letters = length;
+                updatePasswordValidityCheckpointLocked(userId);
+                saveSettingsLocked(userId);
             }
+            maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
     }
 
@@ -4253,14 +4290,17 @@
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
-            if (ap.minimumPasswordMetrics.numeric != length) {
-                ap.minimumPasswordMetrics.numeric = length;
-                updatePasswordValidityCheckpointLocked(mInjector.userHandleGetCallingUserId());
-                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
+            if (metrics.numeric != length) {
+                metrics.numeric = length;
+                updatePasswordValidityCheckpointLocked(userId);
+                saveSettingsLocked(userId);
             }
+            maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
     }
 
@@ -4268,7 +4308,7 @@
     public int getPasswordMinimumNumeric(ComponentName who, int userHandle, boolean parent) {
         return getStrictestPasswordRequirement(who, userHandle, parent,
                 admin -> admin.minimumPasswordMetrics.numeric, PASSWORD_QUALITY_COMPLEX);
-   }
+    }
 
     @Override
     public void setPasswordMinimumSymbols(ComponentName who, int length, boolean parent) {
@@ -4276,14 +4316,17 @@
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
-            if (ap.minimumPasswordMetrics.symbols != length) {
+            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
+            if (metrics.symbols != length) {
                 ap.minimumPasswordMetrics.symbols = length;
-                updatePasswordValidityCheckpointLocked(mInjector.userHandleGetCallingUserId());
-                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+                updatePasswordValidityCheckpointLocked(userId);
+                saveSettingsLocked(userId);
             }
+            maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
     }
 
@@ -4299,14 +4342,17 @@
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
-            if (ap.minimumPasswordMetrics.nonLetter != length) {
+            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
+            if (metrics.nonLetter != length) {
                 ap.minimumPasswordMetrics.nonLetter = length;
-                updatePasswordValidityCheckpointLocked(mInjector.userHandleGetCallingUserId());
-                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+                updatePasswordValidityCheckpointLocked(userId);
+                saveSettingsLocked(userId);
             }
+            maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
     }
 
@@ -4593,6 +4639,7 @@
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
@@ -4602,9 +4649,14 @@
                     who, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
             if (ap.maximumFailedPasswordsForWipe != num) {
                 ap.maximumFailedPasswordsForWipe = num;
-                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+                saveSettingsLocked(userId);
             }
         }
+        if (SecurityLog.isLoggingEnabled()) {
+            final int affectedUserId = parent ? getProfileParentId(userId) : userId;
+            SecurityLog.writeEvent(SecurityLog.TAG_MAX_PASSWORD_ATTEMPTS_SET, who.getPackageName(),
+                    userId, affectedUserId, num);
+        }
     }
 
     @Override
@@ -4702,7 +4754,6 @@
             return false;
         }
     }
-
     @Override
     public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
         final int callingUid = mInjector.binderGetCallingUid();
@@ -4958,6 +5009,11 @@
                 updateMaximumTimeToLockLocked(userHandle);
             }
         }
+        if (SecurityLog.isLoggingEnabled()) {
+            final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
+            SecurityLog.writeEvent(SecurityLog.TAG_MAX_SCREEN_LOCK_TIMEOUT_SET,
+                    who.getPackageName(), userHandle, affectedUserId, timeMs);
+        }
     }
 
     private void updateMaximumTimeToLockLocked(@UserIdInt int userId) {
@@ -5127,11 +5183,12 @@
 
             final long ident = mInjector.binderClearCallingIdentity();
             try {
+                final ComponentName adminComponent = admin.info.getComponent();
                 // Evict key
                 if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
                     enforceManagedProfile(
                             callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
-                    if (!isProfileOwner(admin.info.getComponent(), callingUserId)) {
+                    if (!isProfileOwner(adminComponent, callingUserId)) {
                         throw new SecurityException("Only profile owner admins can set "
                                 + "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
                     }
@@ -5161,6 +5218,13 @@
                 } else {
                     mInjector.getTrustManager().setDeviceLockedForUser(userToLock, true);
                 }
+
+                if (SecurityLog.isLoggingEnabled()) {
+                    final int affectedUserId =
+                            parent ? getProfileParentId(callingUserId) : callingUserId;
+                    SecurityLog.writeEvent(SecurityLog.TAG_REMOTE_LOCK,
+                            adminComponent.getPackageName(), callingUserId, affectedUserId);
+                }
             } catch (RemoteException e) {
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
@@ -6191,8 +6255,7 @@
 
         if (mInjector.securityLogIsLoggingEnabled()) {
             SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
-                    /*result*/ 0,
-                    /*method strength*/ 1);
+                    /*result*/ 0, /*method strength*/ 1);
         }
     }
 
@@ -7039,6 +7102,11 @@
                 saveSettingsLocked(userHandle);
             }
         }
+        if (SecurityLog.isLoggingEnabled()) {
+            final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
+            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISABLED_FEATURES_SET,
+                    who.getPackageName(), userHandle, affectedUserId, which);
+        }
     }
 
     /**
@@ -9186,6 +9254,12 @@
             }
             saveUserRestrictionsLocked(userHandle);
         }
+        if (SecurityLog.isLoggingEnabled()) {
+            final int eventTag = enabledFromThisOwner
+                    ? SecurityLog.TAG_USER_RESTRICTION_ADDED
+                    : SecurityLog.TAG_USER_RESTRICTION_REMOVED;
+            SecurityLog.writeEvent(eventTag, who.getPackageName(), userHandle, key);
+        }
     }
 
     private void saveUserRestrictionsLocked(int userId) {
@@ -12959,4 +13033,15 @@
                 TRANSFER_OWNERSHIP_PARAMETERS_XML);
         parametersFile.delete();
     }
+
+    private void maybeLogPasswordComplexitySet(ComponentName who, int userId, boolean parent,
+            PasswordMetrics metrics) {
+        if (SecurityLog.isLoggingEnabled()) {
+            final int affectedUserId = parent ? getProfileParentId(userId) : userId;
+            SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_COMPLEXITY_SET, who.getPackageName(),
+                    userId, affectedUserId, metrics.length, metrics.quality, metrics.letters,
+                    metrics.nonLetter, metrics.numeric, metrics.upperCase, metrics.lowerCase,
+                    metrics.symbols);
+        }
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index a9fd8e5..3277adf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -71,6 +71,10 @@
      */
     private static final int BUFFER_ENTRIES_MAXIMUM_LEVEL = BUFFER_ENTRIES_NOTIFICATION_LEVEL * 10;
     /**
+     * Critical log buffer level, 90% of capacity.
+     */
+    private static final int BUFFER_ENTRIES_CRITICAL_LEVEL = BUFFER_ENTRIES_MAXIMUM_LEVEL * 9 / 10;
+    /**
      * How often should Device Owner be notified under normal circumstances.
      */
     private static final long RATE_LIMIT_INTERVAL_MILLISECONDS = TimeUnit.HOURS.toMillis(2);
@@ -97,6 +101,10 @@
     @GuardedBy("mLock")
     private boolean mAllowedToRetrieve = false;
 
+    // Whether we have already logged the fact that log buffer reached 90%, to avoid dupes.
+    @GuardedBy("mLock")
+    private boolean mCriticalLevelLogged = false;
+
     /**
      * Last events fetched from log to check for overlap between batches. We can leave it empty if
      * we are sure there will be no overlap anymore, e.g. when we get empty batch.
@@ -116,10 +124,12 @@
 
     void start() {
         Slog.i(TAG, "Starting security logging.");
+        SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STARTED);
         mLock.lock();
         try {
             if (mMonitorThread == null) {
                 mPendingLogs = new ArrayList<>();
+                mCriticalLevelLogged = false;
                 mId = 0;
                 mAllowedToRetrieve = false;
                 mNextAllowedRetrievalTimeMillis = -1;
@@ -135,6 +145,7 @@
 
     void stop() {
         Slog.i(TAG, "Stopping security logging.");
+        SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STOPPED);
         mLock.lock();
         try {
             if (mMonitorThread != null) {
@@ -205,6 +216,7 @@
         mLock.lock();
         mAllowedToRetrieve = false;
         mPendingLogs = new ArrayList<>();
+        mCriticalLevelLogged = false;
         mLock.unlock();
         Slog.i(TAG, "Discarded all logs.");
     }
@@ -222,6 +234,7 @@
                         + RATE_LIMIT_INTERVAL_MILLISECONDS;
                 List<SecurityEvent> result = mPendingLogs;
                 mPendingLogs = new ArrayList<>();
+                mCriticalLevelLogged = false;
                 return result;
             } else {
                 return null;
@@ -344,11 +357,14 @@
         // Save the rest of the new batch.
         mPendingLogs.addAll(idLogs);
 
+        checkCriticalLevel();
+
         if (mPendingLogs.size() > BUFFER_ENTRIES_MAXIMUM_LEVEL) {
             // Truncate buffer down to half of BUFFER_ENTRIES_MAXIMUM_LEVEL.
             mPendingLogs = new ArrayList<>(mPendingLogs.subList(
                     mPendingLogs.size() - (BUFFER_ENTRIES_MAXIMUM_LEVEL / 2),
                     mPendingLogs.size()));
+            mCriticalLevelLogged = false;
             Slog.i(TAG, "Pending logs buffer full. Discarding old logs.");
         }
         if (DEBUG) Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging,"
@@ -357,6 +373,20 @@
     }
 
     @GuardedBy("mLock")
+    private void checkCriticalLevel() {
+        if (!SecurityLog.isLoggingEnabled()) {
+            return;
+        }
+
+        if (mPendingLogs.size() >= BUFFER_ENTRIES_CRITICAL_LEVEL) {
+            if (!mCriticalLevelLogged) {
+                mCriticalLevelLogged = true;
+                SecurityLog.writeEvent(SecurityLog.TAG_LOG_BUFFER_SIZE_CRITICAL);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
     private void assignLogId(SecurityEvent event) {
         event.setId(mId);
         if (mId == Long.MAX_VALUE) {