Loading core/java/android/app/admin/DevicePolicyManager.java +33 −23 Original line number Diff line number Diff line Loading @@ -9932,20 +9932,27 @@ public class DevicePolicyManager { } /** * Called by device owner to control the security logging feature. * Called by device owner or a profile owner of an organization-owned managed profile to * control the security logging feature. * * <p> Security logs contain various information intended for security auditing purposes. * See {@link SecurityEvent} for details. * When security logging is enabled by a profile owner of * an organization-owned managed profile, certain security logs are not visible (for example * personal app launch events) or they will be redacted (for example, details of the physical * volume mount events). Please see {@link SecurityEvent} for details. * * <p><strong>Note:</strong> The device owner won't be able to retrieve security logs if there * are unaffiliated secondary users or profiles on the device, regardless of whether the * feature is enabled. Logs will be discarded if the internal buffer fills up while waiting for * all users to become affiliated. Therefore it's recommended that affiliation ids are set for * new users as soon as possible after provisioning via {@link #setAffiliationIds}. * new users as soon as possible after provisioning via {@link #setAffiliationIds}. Profile * owner of organization-owned managed profile is not subject to this restriction since all * privacy-sensitive events happening outside the managed profile would have been redacted * already. * * @param admin Which device owner this request is associated with. * @param admin Which device admin this request is associated with. * @param enabled whether security logging should be enabled or not. * @throws SecurityException if {@code admin} is not a device owner. * @throws SecurityException if {@code admin} is not allowed to control security logging. * @see #setAffiliationIds * @see #retrieveSecurityLogs */ Loading @@ -9959,14 +9966,14 @@ public class DevicePolicyManager { } /** * Return whether security logging is enabled or not by the device owner. * Return whether security logging is enabled or not by the admin. * * <p>Can only be called by the device owner, otherwise a {@link SecurityException} will be * thrown. * <p>Can only be called by the device owner or a profile owner of an organization-owned * managed profile, otherwise a {@link SecurityException} will be thrown. * * @param admin Which device owner this request is associated with. * @param admin Which device admin this request is associated with. * @return {@code true} if security logging is enabled by device owner, {@code false} otherwise. * @throws SecurityException if {@code admin} is not a device owner. * @throws SecurityException if {@code admin} is not allowed to control security logging. */ public boolean isSecurityLoggingEnabled(@Nullable ComponentName admin) { throwIfParentInstance("isSecurityLoggingEnabled"); Loading @@ -9978,20 +9985,21 @@ public class DevicePolicyManager { } /** * Called by device owner to retrieve all new security logging entries since the last call to * this API after device boots. * Called by device owner or profile owner of an organization-owned managed profile to retrieve * all new security logging entries since the last call to this API after device boots. * * <p> Access to the logs is rate limited and it will only return new logs after the device * owner has been notified via {@link DeviceAdminReceiver#onSecurityLogsAvailable}. * * <p>If there is any other user or profile on the device, it must be affiliated with the * device. Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}. * <p> When called by a device owner, if there is any other user or profile on the device, * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown. * See {@link #isAffiliatedUser}. * * @param admin Which device owner this request is associated with. * @param admin Which device admin this request is associated with. * @return the new batch of security logs which is a list of {@link SecurityEvent}, * or {@code null} if rate limitation is exceeded or if logging is currently disabled. * @throws SecurityException if {@code admin} is not a device owner, or there is at least one * profile or secondary user that is not affiliated with the device. * @throws SecurityException if {@code admin} is not allowed to access security logging, * or there is at least one profile or secondary user that is not affiliated with the device. * @see #isAffiliatedUser * @see DeviceAdminReceiver#onSecurityLogsAvailable */ Loading Loading @@ -10124,21 +10132,23 @@ public class DevicePolicyManager { } /** * Called by device owners to retrieve device logs from before the device's last reboot. * Called by device owner or profile owner of an organization-owned managed profile to retrieve * device logs from before the device's last reboot. * <p> * <strong> This API is not supported on all devices. Calling this API on unsupported devices * will result in {@code null} being returned. The device logs are retrieved from a RAM region * which is not guaranteed to be corruption-free during power cycles, as a result be cautious * about data corruption when parsing. </strong> * * <p>If there is any other user or profile on the device, it must be affiliated with the * device. Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}. * <p> When called by a device owner, if there is any other user or profile on the device, * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown. * See {@link #isAffiliatedUser}. * * @param admin Which device owner this request is associated with. * @param admin Which device admin this request is associated with. * @return Device logs from before the latest reboot of the system, or {@code null} if this API * is not supported on the device. * @throws SecurityException if {@code admin} is not a device owner, or there is at least one * profile or secondary user that is not affiliated with the device. * @throws SecurityException if {@code admin} is not allowed to access security logging, or * there is at least one profile or secondary user that is not affiliated with the device. * @see #isAffiliatedUser * @see #retrieveSecurityLogs */ Loading core/java/android/app/admin/SecurityLog.java +136 −4 Original line number Diff line number Diff line Loading @@ -23,11 +23,13 @@ import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemProperties; import android.os.UserHandle; import android.util.EventLog.Event; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.Objects; Loading Loading @@ -104,7 +106,8 @@ public class SecurityLog { /** * 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()}. * via {@link SecurityEvent#getData()}. If security logging is enabled on organization-owned * managed profile devices, the shell command will be redacted to an empty string. */ public static final int TAG_ADB_SHELL_CMD = SecurityLogTags.SECURITY_ADB_SHELL_COMMAND; Loading Loading @@ -133,6 +136,8 @@ public class SecurityLog { * <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}) * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_APP_PROCESS_START = SecurityLogTags.SECURITY_APP_PROCESS_START; Loading Loading @@ -205,7 +210,8 @@ public class SecurityLog { * 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}). * <li> [1] volume label ({@code String}). Redacted to empty string on organization-owned * managed profile devices. */ public static final int TAG_MEDIA_MOUNT = SecurityLogTags.SECURITY_MEDIA_MOUNTED; Loading @@ -214,7 +220,8 @@ public class SecurityLog { * 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}). * <li> [1] volume label ({@code String}). Redacted to empty string on organization-owned * managed profile devices. */ public static final int TAG_MEDIA_UNMOUNT = SecurityLogTags.SECURITY_MEDIA_UNMOUNTED; Loading Loading @@ -340,6 +347,9 @@ public class SecurityLog { * <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}). * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_KEY_GENERATED = SecurityLogTags.SECURITY_KEY_GENERATED; Loading @@ -351,6 +361,9 @@ public class SecurityLog { * <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}). * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_KEY_IMPORT = SecurityLogTags.SECURITY_KEY_IMPORTED; Loading @@ -361,6 +374,9 @@ public class SecurityLog { * <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}). * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_KEY_DESTRUCTION = SecurityLogTags.SECURITY_KEY_DESTROYED; Loading @@ -370,6 +386,11 @@ public class SecurityLog { * {@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}). * <li> [2] which user the certificate is installed for ({@code Integer}), only available from * version {@link android.os.Build.VERSION_CODES#R}. * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_CERT_AUTHORITY_INSTALLED = SecurityLogTags.SECURITY_CERT_AUTHORITY_INSTALLED; Loading @@ -380,6 +401,11 @@ public class SecurityLog { * {@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}). * <li> [2] which user the certificate is removed from ({@code Integer}), only available from * version {@link android.os.Build.VERSION_CODES#R}. * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_CERT_AUTHORITY_REMOVED = SecurityLogTags.SECURITY_CERT_AUTHORITY_REMOVED; Loading Loading @@ -422,6 +448,9 @@ public class SecurityLog { * {@link SecurityEvent#getData()}: * <li> [0] alias of the key ({@code String}) * <li> [1] owner application uid ({@code Integer}). * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_KEY_INTEGRITY_VIOLATION = SecurityLogTags.SECURITY_KEY_INTEGRITY_VIOLATION; Loading Loading @@ -535,6 +564,16 @@ public class SecurityLog { return mEvent.getData(); } /** @hide */ public int getIntegerData(int index) { return (Integer) ((Object[]) mEvent.getData())[index]; } /** @hide */ public String getStringData(int index) { return (String) ((Object[]) mEvent.getData())[index]; } /** * @hide */ Loading @@ -554,7 +593,7 @@ public class SecurityLog { * Returns severity level for the event. */ public @SecurityLogLevel int getLogLevel() { switch (mEvent.getTag()) { switch (getTag()) { case TAG_ADB_SHELL_INTERACTIVE: case TAG_ADB_SHELL_CMD: case TAG_SYNC_RECV_FILE: Loading Loading @@ -608,6 +647,75 @@ public class SecurityLog { return array.length >= 1 && array[0] instanceof Integer && (Integer) array[0] != 0; } /** * Returns a copy of the security event suitable to be consumed by the provided user. * This method will either return the original event itself if the event does not contain * any sensitive data; or a copy of itself but with sensitive information redacted; or * {@code null} if the entire event should not be accessed by the given user. * * @param accessingUser which user this security event is to be accessed, must be a * concrete user id. * @hide */ public SecurityEvent redact(int accessingUser) { // Which user the event is associated with, for the purpose of log redaction. final int userId; switch (getTag()) { case SecurityLog.TAG_ADB_SHELL_CMD: return new SecurityEvent(getId(), mEvent.withNewData("").getBytes()); case SecurityLog.TAG_MEDIA_MOUNT: case SecurityLog.TAG_MEDIA_UNMOUNT: // Partial redaction String mountPoint; try { mountPoint = getStringData(0); } catch (Exception e) { return null; } return new SecurityEvent(getId(), mEvent.withNewData(new Object[] {mountPoint, ""}).getBytes()); case SecurityLog.TAG_APP_PROCESS_START: try { userId = UserHandle.getUserId(getIntegerData(2)); } catch (Exception e) { return null; } break; case SecurityLog.TAG_CERT_AUTHORITY_INSTALLED: case SecurityLog.TAG_CERT_AUTHORITY_REMOVED: try { userId = getIntegerData(2); } catch (Exception e) { return null; } break; case SecurityLog.TAG_KEY_GENERATED: case SecurityLog.TAG_KEY_IMPORT: case SecurityLog.TAG_KEY_DESTRUCTION: try { userId = UserHandle.getUserId(getIntegerData(2)); } catch (Exception e) { return null; } break; case SecurityLog.TAG_KEY_INTEGRITY_VIOLATION: try { userId = UserHandle.getUserId(getIntegerData(1)); } catch (Exception e) { return null; } break; default: userId = UserHandle.USER_NULL; } // If the event is not user-specific, or matches the accessing user, return it // unmodified, else redact by returning null if (userId == UserHandle.USER_NULL || accessingUser == userId) { return this; } else { return null; } } @Override public int describeContents() { Loading Loading @@ -657,6 +765,30 @@ public class SecurityLog { return other != null && mEvent.equals(other.mEvent); } } /** * Redacts events in-place according to which user will consume the events. * * @param accessingUser which user will consume the redacted events, or UserHandle.USER_ALL if * redaction should be skipped. * @hide */ public static void redactEvents(ArrayList<SecurityEvent> logList, int accessingUser) { if (accessingUser == UserHandle.USER_ALL) return; int end = 0; for (int i = 0; i < logList.size(); i++) { SecurityEvent event = logList.get(i); event = event.redact(accessingUser); if (event != null) { logList.set(end, event); end++; } } for (int i = logList.size() - 1; i >= end; i--) { logList.remove(i); } } /** * Retrieve all security logs and return immediately. * @hide Loading core/java/android/app/admin/SecurityLogTags.logtags +2 −2 Original line number Diff line number Diff line Loading @@ -33,8 +33,8 @@ option java_package android.app.admin 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) 210029 security_cert_authority_installed (success|1),(subject|3),(target_user|1) 210030 security_cert_authority_removed (success|1),(subject|3),(target_user|1) 210031 security_crypto_self_test_completed (success|1) 210032 security_key_integrity_violation (key_id|3),(uid|1) 210033 security_cert_validation_failure (reason|3) Loading core/java/android/util/EventLog.java +97 −13 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.util; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; Loading Loading @@ -62,7 +64,7 @@ public class EventLog { private Exception mLastWtf; // Layout of event log entry received from Android logger. // see system/core/include/log/log.h // see system/core/liblog/include/log/log_read.h private static final int LENGTH_OFFSET = 0; private static final int HEADER_SIZE_OFFSET = 2; private static final int PROCESS_OFFSET = 4; Loading @@ -73,7 +75,7 @@ public class EventLog { // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET private static final int V1_PAYLOAD_START = 20; private static final int DATA_OFFSET = 4; private static final int TAG_LENGTH = 4; // Value types private static final byte INT_TYPE = 0; Loading Loading @@ -121,26 +123,26 @@ public class EventLog { /** @return the type tag code of the entry */ public int getTag() { int offset = mBuffer.getShort(HEADER_SIZE_OFFSET); if (offset == 0) { offset = V1_PAYLOAD_START; } return mBuffer.getInt(offset); return mBuffer.getInt(getHeaderSize()); } private int getHeaderSize() { int length = mBuffer.getShort(HEADER_SIZE_OFFSET); if (length != 0) { return length; } return V1_PAYLOAD_START; } /** @return one of Integer, Long, Float, String, null, or Object[] of same. */ public synchronized Object getData() { try { int offset = mBuffer.getShort(HEADER_SIZE_OFFSET); if (offset == 0) { offset = V1_PAYLOAD_START; } int offset = getHeaderSize(); mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET)); if ((offset + DATA_OFFSET) >= mBuffer.limit()) { if ((offset + TAG_LENGTH) >= mBuffer.limit()) { // no payload return null; } mBuffer.position(offset + DATA_OFFSET); // Just after the tag. mBuffer.position(offset + TAG_LENGTH); // Just after the tag. return decodeObject(); } catch (IllegalArgumentException e) { Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e); Loading @@ -153,6 +155,28 @@ public class EventLog { } } /** * Construct a new EventLog object from the current object, copying all log metadata * but replacing the actual payload with the content provided. * @hide */ public Event withNewData(@Nullable Object object) { byte[] payload = encodeObject(object); if (payload.length > 65535 - TAG_LENGTH) { throw new IllegalArgumentException("Payload too long"); } int headerLength = getHeaderSize(); byte[] newBytes = new byte[headerLength + TAG_LENGTH + payload.length]; // Copy header (including the 4 bytes of tag integer at the beginning of payload) System.arraycopy(mBuffer.array(), 0, newBytes, 0, headerLength + TAG_LENGTH); // Fill in encoded objects System.arraycopy(payload, 0, newBytes, headerLength + TAG_LENGTH, payload.length); Event result = new Event(newBytes); // Patch payload length in header result.mBuffer.putShort(LENGTH_OFFSET, (short) (payload.length + TAG_LENGTH)); return result; } /** @return the loggable item at the current position in mBuffer. */ private Object decodeObject() { byte type = mBuffer.get(); Loading Loading @@ -190,6 +214,66 @@ public class EventLog { } } private static @NonNull byte[] encodeObject(@Nullable Object object) { if (object == null) { return new byte[0]; } if (object instanceof Integer) { return ByteBuffer.allocate(1 + 4) .order(ByteOrder.nativeOrder()) .put(INT_TYPE) .putInt((Integer) object) .array(); } else if (object instanceof Long) { return ByteBuffer.allocate(1 + 8) .order(ByteOrder.nativeOrder()) .put(LONG_TYPE) .putLong((Long) object) .array(); } else if (object instanceof Float) { return ByteBuffer.allocate(1 + 4) .order(ByteOrder.nativeOrder()) .put(FLOAT_TYPE) .putFloat((Float) object) .array(); } else if (object instanceof String) { String string = (String) object; byte[] bytes; try { bytes = string.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { bytes = new byte[0]; } return ByteBuffer.allocate(1 + 4 + bytes.length) .order(ByteOrder.nativeOrder()) .put(STRING_TYPE) .putInt(bytes.length) .put(bytes) .array(); } else if (object instanceof Object[]) { Object[] objects = (Object[]) object; if (objects.length > 255) { throw new IllegalArgumentException("Object array too long"); } byte[][] bytes = new byte[objects.length][]; int totalLength = 0; for (int i = 0; i < objects.length; i++) { bytes[i] = encodeObject(objects[i]); totalLength += bytes[i].length; } ByteBuffer buffer = ByteBuffer.allocate(1 + 1 + totalLength) .order(ByteOrder.nativeOrder()) .put(LIST_TYPE) .put((byte) objects.length); for (int i = 0; i < objects.length; i++) { buffer.put(bytes[i]); } return buffer.array(); } else { throw new IllegalArgumentException("Unknown object type " + object); } } /** @hide */ public static Event fromBytes(byte[] data) { return new Event(data); Loading core/tests/coretests/src/android/util/EventLogTest.java 0 → 100644 +74 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/app/admin/DevicePolicyManager.java +33 −23 Original line number Diff line number Diff line Loading @@ -9932,20 +9932,27 @@ public class DevicePolicyManager { } /** * Called by device owner to control the security logging feature. * Called by device owner or a profile owner of an organization-owned managed profile to * control the security logging feature. * * <p> Security logs contain various information intended for security auditing purposes. * See {@link SecurityEvent} for details. * When security logging is enabled by a profile owner of * an organization-owned managed profile, certain security logs are not visible (for example * personal app launch events) or they will be redacted (for example, details of the physical * volume mount events). Please see {@link SecurityEvent} for details. * * <p><strong>Note:</strong> The device owner won't be able to retrieve security logs if there * are unaffiliated secondary users or profiles on the device, regardless of whether the * feature is enabled. Logs will be discarded if the internal buffer fills up while waiting for * all users to become affiliated. Therefore it's recommended that affiliation ids are set for * new users as soon as possible after provisioning via {@link #setAffiliationIds}. * new users as soon as possible after provisioning via {@link #setAffiliationIds}. Profile * owner of organization-owned managed profile is not subject to this restriction since all * privacy-sensitive events happening outside the managed profile would have been redacted * already. * * @param admin Which device owner this request is associated with. * @param admin Which device admin this request is associated with. * @param enabled whether security logging should be enabled or not. * @throws SecurityException if {@code admin} is not a device owner. * @throws SecurityException if {@code admin} is not allowed to control security logging. * @see #setAffiliationIds * @see #retrieveSecurityLogs */ Loading @@ -9959,14 +9966,14 @@ public class DevicePolicyManager { } /** * Return whether security logging is enabled or not by the device owner. * Return whether security logging is enabled or not by the admin. * * <p>Can only be called by the device owner, otherwise a {@link SecurityException} will be * thrown. * <p>Can only be called by the device owner or a profile owner of an organization-owned * managed profile, otherwise a {@link SecurityException} will be thrown. * * @param admin Which device owner this request is associated with. * @param admin Which device admin this request is associated with. * @return {@code true} if security logging is enabled by device owner, {@code false} otherwise. * @throws SecurityException if {@code admin} is not a device owner. * @throws SecurityException if {@code admin} is not allowed to control security logging. */ public boolean isSecurityLoggingEnabled(@Nullable ComponentName admin) { throwIfParentInstance("isSecurityLoggingEnabled"); Loading @@ -9978,20 +9985,21 @@ public class DevicePolicyManager { } /** * Called by device owner to retrieve all new security logging entries since the last call to * this API after device boots. * Called by device owner or profile owner of an organization-owned managed profile to retrieve * all new security logging entries since the last call to this API after device boots. * * <p> Access to the logs is rate limited and it will only return new logs after the device * owner has been notified via {@link DeviceAdminReceiver#onSecurityLogsAvailable}. * * <p>If there is any other user or profile on the device, it must be affiliated with the * device. Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}. * <p> When called by a device owner, if there is any other user or profile on the device, * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown. * See {@link #isAffiliatedUser}. * * @param admin Which device owner this request is associated with. * @param admin Which device admin this request is associated with. * @return the new batch of security logs which is a list of {@link SecurityEvent}, * or {@code null} if rate limitation is exceeded or if logging is currently disabled. * @throws SecurityException if {@code admin} is not a device owner, or there is at least one * profile or secondary user that is not affiliated with the device. * @throws SecurityException if {@code admin} is not allowed to access security logging, * or there is at least one profile or secondary user that is not affiliated with the device. * @see #isAffiliatedUser * @see DeviceAdminReceiver#onSecurityLogsAvailable */ Loading Loading @@ -10124,21 +10132,23 @@ public class DevicePolicyManager { } /** * Called by device owners to retrieve device logs from before the device's last reboot. * Called by device owner or profile owner of an organization-owned managed profile to retrieve * device logs from before the device's last reboot. * <p> * <strong> This API is not supported on all devices. Calling this API on unsupported devices * will result in {@code null} being returned. The device logs are retrieved from a RAM region * which is not guaranteed to be corruption-free during power cycles, as a result be cautious * about data corruption when parsing. </strong> * * <p>If there is any other user or profile on the device, it must be affiliated with the * device. Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}. * <p> When called by a device owner, if there is any other user or profile on the device, * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown. * See {@link #isAffiliatedUser}. * * @param admin Which device owner this request is associated with. * @param admin Which device admin this request is associated with. * @return Device logs from before the latest reboot of the system, or {@code null} if this API * is not supported on the device. * @throws SecurityException if {@code admin} is not a device owner, or there is at least one * profile or secondary user that is not affiliated with the device. * @throws SecurityException if {@code admin} is not allowed to access security logging, or * there is at least one profile or secondary user that is not affiliated with the device. * @see #isAffiliatedUser * @see #retrieveSecurityLogs */ Loading
core/java/android/app/admin/SecurityLog.java +136 −4 Original line number Diff line number Diff line Loading @@ -23,11 +23,13 @@ import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemProperties; import android.os.UserHandle; import android.util.EventLog.Event; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.Objects; Loading Loading @@ -104,7 +106,8 @@ public class SecurityLog { /** * 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()}. * via {@link SecurityEvent#getData()}. If security logging is enabled on organization-owned * managed profile devices, the shell command will be redacted to an empty string. */ public static final int TAG_ADB_SHELL_CMD = SecurityLogTags.SECURITY_ADB_SHELL_COMMAND; Loading Loading @@ -133,6 +136,8 @@ public class SecurityLog { * <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}) * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_APP_PROCESS_START = SecurityLogTags.SECURITY_APP_PROCESS_START; Loading Loading @@ -205,7 +210,8 @@ public class SecurityLog { * 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}). * <li> [1] volume label ({@code String}). Redacted to empty string on organization-owned * managed profile devices. */ public static final int TAG_MEDIA_MOUNT = SecurityLogTags.SECURITY_MEDIA_MOUNTED; Loading @@ -214,7 +220,8 @@ public class SecurityLog { * 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}). * <li> [1] volume label ({@code String}). Redacted to empty string on organization-owned * managed profile devices. */ public static final int TAG_MEDIA_UNMOUNT = SecurityLogTags.SECURITY_MEDIA_UNMOUNTED; Loading Loading @@ -340,6 +347,9 @@ public class SecurityLog { * <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}). * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_KEY_GENERATED = SecurityLogTags.SECURITY_KEY_GENERATED; Loading @@ -351,6 +361,9 @@ public class SecurityLog { * <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}). * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_KEY_IMPORT = SecurityLogTags.SECURITY_KEY_IMPORTED; Loading @@ -361,6 +374,9 @@ public class SecurityLog { * <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}). * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_KEY_DESTRUCTION = SecurityLogTags.SECURITY_KEY_DESTROYED; Loading @@ -370,6 +386,11 @@ public class SecurityLog { * {@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}). * <li> [2] which user the certificate is installed for ({@code Integer}), only available from * version {@link android.os.Build.VERSION_CODES#R}. * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_CERT_AUTHORITY_INSTALLED = SecurityLogTags.SECURITY_CERT_AUTHORITY_INSTALLED; Loading @@ -380,6 +401,11 @@ public class SecurityLog { * {@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}). * <li> [2] which user the certificate is removed from ({@code Integer}), only available from * version {@link android.os.Build.VERSION_CODES#R}. * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_CERT_AUTHORITY_REMOVED = SecurityLogTags.SECURITY_CERT_AUTHORITY_REMOVED; Loading Loading @@ -422,6 +448,9 @@ public class SecurityLog { * {@link SecurityEvent#getData()}: * <li> [0] alias of the key ({@code String}) * <li> [1] owner application uid ({@code Integer}). * * If security logging is enabled on organization-owned managed profile devices, only events * happening inside the managed profile will be visible. */ public static final int TAG_KEY_INTEGRITY_VIOLATION = SecurityLogTags.SECURITY_KEY_INTEGRITY_VIOLATION; Loading Loading @@ -535,6 +564,16 @@ public class SecurityLog { return mEvent.getData(); } /** @hide */ public int getIntegerData(int index) { return (Integer) ((Object[]) mEvent.getData())[index]; } /** @hide */ public String getStringData(int index) { return (String) ((Object[]) mEvent.getData())[index]; } /** * @hide */ Loading @@ -554,7 +593,7 @@ public class SecurityLog { * Returns severity level for the event. */ public @SecurityLogLevel int getLogLevel() { switch (mEvent.getTag()) { switch (getTag()) { case TAG_ADB_SHELL_INTERACTIVE: case TAG_ADB_SHELL_CMD: case TAG_SYNC_RECV_FILE: Loading Loading @@ -608,6 +647,75 @@ public class SecurityLog { return array.length >= 1 && array[0] instanceof Integer && (Integer) array[0] != 0; } /** * Returns a copy of the security event suitable to be consumed by the provided user. * This method will either return the original event itself if the event does not contain * any sensitive data; or a copy of itself but with sensitive information redacted; or * {@code null} if the entire event should not be accessed by the given user. * * @param accessingUser which user this security event is to be accessed, must be a * concrete user id. * @hide */ public SecurityEvent redact(int accessingUser) { // Which user the event is associated with, for the purpose of log redaction. final int userId; switch (getTag()) { case SecurityLog.TAG_ADB_SHELL_CMD: return new SecurityEvent(getId(), mEvent.withNewData("").getBytes()); case SecurityLog.TAG_MEDIA_MOUNT: case SecurityLog.TAG_MEDIA_UNMOUNT: // Partial redaction String mountPoint; try { mountPoint = getStringData(0); } catch (Exception e) { return null; } return new SecurityEvent(getId(), mEvent.withNewData(new Object[] {mountPoint, ""}).getBytes()); case SecurityLog.TAG_APP_PROCESS_START: try { userId = UserHandle.getUserId(getIntegerData(2)); } catch (Exception e) { return null; } break; case SecurityLog.TAG_CERT_AUTHORITY_INSTALLED: case SecurityLog.TAG_CERT_AUTHORITY_REMOVED: try { userId = getIntegerData(2); } catch (Exception e) { return null; } break; case SecurityLog.TAG_KEY_GENERATED: case SecurityLog.TAG_KEY_IMPORT: case SecurityLog.TAG_KEY_DESTRUCTION: try { userId = UserHandle.getUserId(getIntegerData(2)); } catch (Exception e) { return null; } break; case SecurityLog.TAG_KEY_INTEGRITY_VIOLATION: try { userId = UserHandle.getUserId(getIntegerData(1)); } catch (Exception e) { return null; } break; default: userId = UserHandle.USER_NULL; } // If the event is not user-specific, or matches the accessing user, return it // unmodified, else redact by returning null if (userId == UserHandle.USER_NULL || accessingUser == userId) { return this; } else { return null; } } @Override public int describeContents() { Loading Loading @@ -657,6 +765,30 @@ public class SecurityLog { return other != null && mEvent.equals(other.mEvent); } } /** * Redacts events in-place according to which user will consume the events. * * @param accessingUser which user will consume the redacted events, or UserHandle.USER_ALL if * redaction should be skipped. * @hide */ public static void redactEvents(ArrayList<SecurityEvent> logList, int accessingUser) { if (accessingUser == UserHandle.USER_ALL) return; int end = 0; for (int i = 0; i < logList.size(); i++) { SecurityEvent event = logList.get(i); event = event.redact(accessingUser); if (event != null) { logList.set(end, event); end++; } } for (int i = logList.size() - 1; i >= end; i--) { logList.remove(i); } } /** * Retrieve all security logs and return immediately. * @hide Loading
core/java/android/app/admin/SecurityLogTags.logtags +2 −2 Original line number Diff line number Diff line Loading @@ -33,8 +33,8 @@ option java_package android.app.admin 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) 210029 security_cert_authority_installed (success|1),(subject|3),(target_user|1) 210030 security_cert_authority_removed (success|1),(subject|3),(target_user|1) 210031 security_crypto_self_test_completed (success|1) 210032 security_key_integrity_violation (key_id|3),(uid|1) 210033 security_cert_validation_failure (reason|3) Loading
core/java/android/util/EventLog.java +97 −13 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.util; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; Loading Loading @@ -62,7 +64,7 @@ public class EventLog { private Exception mLastWtf; // Layout of event log entry received from Android logger. // see system/core/include/log/log.h // see system/core/liblog/include/log/log_read.h private static final int LENGTH_OFFSET = 0; private static final int HEADER_SIZE_OFFSET = 2; private static final int PROCESS_OFFSET = 4; Loading @@ -73,7 +75,7 @@ public class EventLog { // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET private static final int V1_PAYLOAD_START = 20; private static final int DATA_OFFSET = 4; private static final int TAG_LENGTH = 4; // Value types private static final byte INT_TYPE = 0; Loading Loading @@ -121,26 +123,26 @@ public class EventLog { /** @return the type tag code of the entry */ public int getTag() { int offset = mBuffer.getShort(HEADER_SIZE_OFFSET); if (offset == 0) { offset = V1_PAYLOAD_START; } return mBuffer.getInt(offset); return mBuffer.getInt(getHeaderSize()); } private int getHeaderSize() { int length = mBuffer.getShort(HEADER_SIZE_OFFSET); if (length != 0) { return length; } return V1_PAYLOAD_START; } /** @return one of Integer, Long, Float, String, null, or Object[] of same. */ public synchronized Object getData() { try { int offset = mBuffer.getShort(HEADER_SIZE_OFFSET); if (offset == 0) { offset = V1_PAYLOAD_START; } int offset = getHeaderSize(); mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET)); if ((offset + DATA_OFFSET) >= mBuffer.limit()) { if ((offset + TAG_LENGTH) >= mBuffer.limit()) { // no payload return null; } mBuffer.position(offset + DATA_OFFSET); // Just after the tag. mBuffer.position(offset + TAG_LENGTH); // Just after the tag. return decodeObject(); } catch (IllegalArgumentException e) { Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e); Loading @@ -153,6 +155,28 @@ public class EventLog { } } /** * Construct a new EventLog object from the current object, copying all log metadata * but replacing the actual payload with the content provided. * @hide */ public Event withNewData(@Nullable Object object) { byte[] payload = encodeObject(object); if (payload.length > 65535 - TAG_LENGTH) { throw new IllegalArgumentException("Payload too long"); } int headerLength = getHeaderSize(); byte[] newBytes = new byte[headerLength + TAG_LENGTH + payload.length]; // Copy header (including the 4 bytes of tag integer at the beginning of payload) System.arraycopy(mBuffer.array(), 0, newBytes, 0, headerLength + TAG_LENGTH); // Fill in encoded objects System.arraycopy(payload, 0, newBytes, headerLength + TAG_LENGTH, payload.length); Event result = new Event(newBytes); // Patch payload length in header result.mBuffer.putShort(LENGTH_OFFSET, (short) (payload.length + TAG_LENGTH)); return result; } /** @return the loggable item at the current position in mBuffer. */ private Object decodeObject() { byte type = mBuffer.get(); Loading Loading @@ -190,6 +214,66 @@ public class EventLog { } } private static @NonNull byte[] encodeObject(@Nullable Object object) { if (object == null) { return new byte[0]; } if (object instanceof Integer) { return ByteBuffer.allocate(1 + 4) .order(ByteOrder.nativeOrder()) .put(INT_TYPE) .putInt((Integer) object) .array(); } else if (object instanceof Long) { return ByteBuffer.allocate(1 + 8) .order(ByteOrder.nativeOrder()) .put(LONG_TYPE) .putLong((Long) object) .array(); } else if (object instanceof Float) { return ByteBuffer.allocate(1 + 4) .order(ByteOrder.nativeOrder()) .put(FLOAT_TYPE) .putFloat((Float) object) .array(); } else if (object instanceof String) { String string = (String) object; byte[] bytes; try { bytes = string.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { bytes = new byte[0]; } return ByteBuffer.allocate(1 + 4 + bytes.length) .order(ByteOrder.nativeOrder()) .put(STRING_TYPE) .putInt(bytes.length) .put(bytes) .array(); } else if (object instanceof Object[]) { Object[] objects = (Object[]) object; if (objects.length > 255) { throw new IllegalArgumentException("Object array too long"); } byte[][] bytes = new byte[objects.length][]; int totalLength = 0; for (int i = 0; i < objects.length; i++) { bytes[i] = encodeObject(objects[i]); totalLength += bytes[i].length; } ByteBuffer buffer = ByteBuffer.allocate(1 + 1 + totalLength) .order(ByteOrder.nativeOrder()) .put(LIST_TYPE) .put((byte) objects.length); for (int i = 0; i < objects.length; i++) { buffer.put(bytes[i]); } return buffer.array(); } else { throw new IllegalArgumentException("Unknown object type " + object); } } /** @hide */ public static Event fromBytes(byte[] data) { return new Event(data); Loading
core/tests/coretests/src/android/util/EventLogTest.java 0 → 100644 +74 −0 File added.Preview size limit exceeded, changes collapsed. Show changes