Loading api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -6648,6 +6648,7 @@ package android.app.admin { 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 getTag(); method public long getTimeNanos(); method public void writeToParcel(android.os.Parcel, int); api/test-current.txt +4 −0 Original line number Diff line number Diff line Loading @@ -114,6 +114,10 @@ package android.app.admin { field public static final java.lang.String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION"; } public static final class SecurityLog.SecurityEvent implements android.os.Parcelable { ctor public SecurityLog.SecurityEvent(long, byte[]); } } package android.app.usage { Loading core/java/android/app/admin/SecurityLog.java +41 −4 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.app.admin; import android.annotation.IntDef; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemProperties; Loading @@ -26,6 +27,7 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collection; import java.util.Objects; /** * Definitions for working with security logs. Loading Loading @@ -135,9 +137,28 @@ public class SecurityLog { */ public static final class SecurityEvent implements Parcelable { private Event mEvent; private long mId; /** @hide */ /** * Constructor used by native classes to generate SecurityEvent instances. * @hide */ /* package */ SecurityEvent(byte[] data) { this(0, data); } /** * Constructor used by Parcelable.Creator to generate SecurityEvent instances. * @hide */ /* package */ SecurityEvent(Parcel source) { this(source.readLong(), source.createByteArray()); } /** @hide */ @TestApi public SecurityEvent(long id, byte[] data) { mId = id; mEvent = Event.fromBytes(data); } Loading @@ -162,6 +183,21 @@ public class SecurityLog { return mEvent.getData(); } /** * @hide */ public void setId(long id) { this.mId = id; } /** * Returns the id of the event, where the id monotonically increases for each event. The id * is reset when the device reboots, and when security logging is enabled. */ public long getId() { return mId; } @Override public int describeContents() { return 0; Loading @@ -169,6 +205,7 @@ public class SecurityLog { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mId); dest.writeByteArray(mEvent.getBytes()); } Loading @@ -176,7 +213,7 @@ public class SecurityLog { new Parcelable.Creator<SecurityEvent>() { @Override public SecurityEvent createFromParcel(Parcel source) { return new SecurityEvent(source.createByteArray()); return new SecurityEvent(source); } @Override Loading @@ -193,7 +230,7 @@ public class SecurityLog { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SecurityEvent other = (SecurityEvent) o; return mEvent.equals(other.mEvent); return mEvent.equals(other.mEvent) && mId == other.mId; } /** Loading @@ -201,7 +238,7 @@ public class SecurityLog { */ @Override public int hashCode() { return mEvent.hashCode(); return Objects.hash(mEvent, mId); } } /** Loading services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java +36 −6 Original line number Diff line number Diff line Loading @@ -19,11 +19,13 @@ package com.android.server.devicepolicy; import android.app.admin.DeviceAdminReceiver; import android.app.admin.SecurityLog; import android.app.admin.SecurityLog.SecurityEvent; import android.os.Process; import android.os.SystemClock; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; import java.util.ArrayList; Loading @@ -32,8 +34,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import android.os.Process; /** * A class managing access to the security logs. It maintains an internal buffer of pending * logs to be retrieved by the device owner. The logs are retrieved from the logd daemon via Loading @@ -48,7 +48,13 @@ class SecurityLogMonitor implements Runnable { private final Lock mLock = new ReentrantLock(); SecurityLogMonitor(DevicePolicyManagerService service) { mService = service; this(service, 0 /* id */); } @VisibleForTesting SecurityLogMonitor(DevicePolicyManagerService service, long id) { this.mService = service; this.mId = id; } private static final boolean DEBUG = false; // STOPSHIP if true. Loading @@ -58,7 +64,7 @@ class SecurityLogMonitor implements Runnable { * it should be less than 100 bytes), setting 1024 entries as the threshold to notify Device * Owner. */ private static final int BUFFER_ENTRIES_NOTIFICATION_LEVEL = 1024; @VisibleForTesting static final int BUFFER_ENTRIES_NOTIFICATION_LEVEL = 1024; /** * The maximum number of entries we should store before dropping earlier logs, to limit the * memory usage. Loading Loading @@ -87,6 +93,8 @@ class SecurityLogMonitor implements Runnable { @GuardedBy("mLock") private ArrayList<SecurityEvent> mPendingLogs = new ArrayList<>(); @GuardedBy("mLock") private long mId; @GuardedBy("mLock") private boolean mAllowedToRetrieve = false; /** Loading @@ -112,6 +120,7 @@ class SecurityLogMonitor implements Runnable { try { if (mMonitorThread == null) { mPendingLogs = new ArrayList<>(); mId = 0; mAllowedToRetrieve = false; mNextAllowedRetrievalTimeMillis = -1; mPaused = false; Loading @@ -137,6 +146,7 @@ class SecurityLogMonitor implements Runnable { } // Reset state and clear buffer mPendingLogs = new ArrayList<>(); mId = 0; mAllowedToRetrieve = false; mNextAllowedRetrievalTimeMillis = -1; mPaused = false; Loading Loading @@ -305,6 +315,7 @@ class SecurityLogMonitor implements Runnable { if (lastNanos > currentNanos) { // New event older than the last we've seen so far, must be due to reordering. if (DEBUG) Slog.d(TAG, "New event in the overlap: " + currentNanos); assignLogId(curEvent); mPendingLogs.add(curEvent); curPos++; } else if (lastNanos < currentNanos) { Loading @@ -317,6 +328,7 @@ class SecurityLogMonitor implements Runnable { if (DEBUG) Slog.d(TAG, "Skipped dup event with timestamp: " + lastNanos); } else { // Wow, what a coincidence, or probably the clock is too coarse. assignLogId(curEvent); mPendingLogs.add(curEvent); if (DEBUG) Slog.d(TAG, "Event timestamp collision: " + lastNanos); } Loading @@ -324,8 +336,13 @@ class SecurityLogMonitor implements Runnable { curPos++; } } // Assign an id to the new logs, after the overlap with mLastEvents. List<SecurityEvent> idLogs = newLogs.subList(curPos, newLogs.size()); for (SecurityEvent event : idLogs) { assignLogId(event); } // Save the rest of the new batch. mPendingLogs.addAll(newLogs.subList(curPos, newLogs.size())); mPendingLogs.addAll(idLogs); if (mPendingLogs.size() > BUFFER_ENTRIES_MAXIMUM_LEVEL) { // Truncate buffer down to half of BUFFER_ENTRIES_MAXIMUM_LEVEL. Loading @@ -334,7 +351,20 @@ class SecurityLogMonitor implements Runnable { mPendingLogs.size())); Slog.i(TAG, "Pending logs buffer full. Discarding old logs."); } if (DEBUG) Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging"); if (DEBUG) Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging," + " with ids " + mPendingLogs.get(0).getId() + " to " + mPendingLogs.get(mPendingLogs.size() - 1).getId()); } @GuardedBy("mLock") private void assignLogId(SecurityEvent event) { event.setId(mId); if (mId == Long.MAX_VALUE) { Slog.i(TAG, "Reached maximum id value; wrapping around."); mId = 0; } else { mId++; } } @Override Loading services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java 0 → 100644 +61 −0 Original line number Diff line number Diff line package com.android.server.devicepolicy; import static android.app.admin.SecurityLog.TAG_ADB_SHELL_CMD; import android.app.admin.SecurityLog.SecurityEvent; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; import android.util.EventLog; import java.io.IOException; import java.util.ArrayList; import java.util.List; @SmallTest public class SecurityEventTest extends DpmTestBase { private static long ID = 549; private static String DATA = "adb shell some_command"; public void testSecurityEventId() { SecurityEvent event = buildSecurityEvents(1 /* generate a single event */, ID).get(0); assertEquals(ID, event.getId()); event.setId(20); assertEquals(20, event.getId()); } public void testSecurityEventParceling() { // GIVEN an event. SecurityEvent event = buildSecurityEvents(1 /* generate a single event */, ID).get(0); // WHEN parceling the event. Parcel p = Parcel.obtain(); p.writeParcelable(event, 0); p.setDataPosition(0); SecurityEvent unparceledEvent = p.readParcelable(SecurityEventTest.class.getClassLoader()); p.recycle(); // THEN the event state is preserved. assertEquals(event.getTag(), unparceledEvent.getTag()); assertEquals(event.getData(), unparceledEvent.getData()); assertEquals(event.getTimeNanos(), unparceledEvent.getTimeNanos()); assertEquals(event.getId(), unparceledEvent.getId()); } private List<SecurityEvent> buildSecurityEvents(int numEvents, long id) { // Write an event to the EventLog. for (int i = 0; i < numEvents; i++) { EventLog.writeEvent(TAG_ADB_SHELL_CMD, DATA + "_" + i); } List<EventLog.Event> events = new ArrayList<>(); try { EventLog.readEvents(new int[]{TAG_ADB_SHELL_CMD}, events); } catch (IOException e) { fail("Reading a test event from storage failed: " + e); } assertTrue("Unexpected number of events read from the log.", events.size() >= numEvents); // Read events generated by test, from the end of the log. List<SecurityEvent> securityEvents = new ArrayList<>(); for (int i = events.size() - numEvents; i < events.size(); i++) { securityEvents.add(new SecurityEvent(id++, events.get(i).getBytes())); } return securityEvents; } } Loading
api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -6648,6 +6648,7 @@ package android.app.admin { 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 getTag(); method public long getTimeNanos(); method public void writeToParcel(android.os.Parcel, int);
api/test-current.txt +4 −0 Original line number Diff line number Diff line Loading @@ -114,6 +114,10 @@ package android.app.admin { field public static final java.lang.String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION"; } public static final class SecurityLog.SecurityEvent implements android.os.Parcelable { ctor public SecurityLog.SecurityEvent(long, byte[]); } } package android.app.usage { Loading
core/java/android/app/admin/SecurityLog.java +41 −4 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.app.admin; import android.annotation.IntDef; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemProperties; Loading @@ -26,6 +27,7 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collection; import java.util.Objects; /** * Definitions for working with security logs. Loading Loading @@ -135,9 +137,28 @@ public class SecurityLog { */ public static final class SecurityEvent implements Parcelable { private Event mEvent; private long mId; /** @hide */ /** * Constructor used by native classes to generate SecurityEvent instances. * @hide */ /* package */ SecurityEvent(byte[] data) { this(0, data); } /** * Constructor used by Parcelable.Creator to generate SecurityEvent instances. * @hide */ /* package */ SecurityEvent(Parcel source) { this(source.readLong(), source.createByteArray()); } /** @hide */ @TestApi public SecurityEvent(long id, byte[] data) { mId = id; mEvent = Event.fromBytes(data); } Loading @@ -162,6 +183,21 @@ public class SecurityLog { return mEvent.getData(); } /** * @hide */ public void setId(long id) { this.mId = id; } /** * Returns the id of the event, where the id monotonically increases for each event. The id * is reset when the device reboots, and when security logging is enabled. */ public long getId() { return mId; } @Override public int describeContents() { return 0; Loading @@ -169,6 +205,7 @@ public class SecurityLog { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mId); dest.writeByteArray(mEvent.getBytes()); } Loading @@ -176,7 +213,7 @@ public class SecurityLog { new Parcelable.Creator<SecurityEvent>() { @Override public SecurityEvent createFromParcel(Parcel source) { return new SecurityEvent(source.createByteArray()); return new SecurityEvent(source); } @Override Loading @@ -193,7 +230,7 @@ public class SecurityLog { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SecurityEvent other = (SecurityEvent) o; return mEvent.equals(other.mEvent); return mEvent.equals(other.mEvent) && mId == other.mId; } /** Loading @@ -201,7 +238,7 @@ public class SecurityLog { */ @Override public int hashCode() { return mEvent.hashCode(); return Objects.hash(mEvent, mId); } } /** Loading
services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java +36 −6 Original line number Diff line number Diff line Loading @@ -19,11 +19,13 @@ package com.android.server.devicepolicy; import android.app.admin.DeviceAdminReceiver; import android.app.admin.SecurityLog; import android.app.admin.SecurityLog.SecurityEvent; import android.os.Process; import android.os.SystemClock; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; import java.util.ArrayList; Loading @@ -32,8 +34,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import android.os.Process; /** * A class managing access to the security logs. It maintains an internal buffer of pending * logs to be retrieved by the device owner. The logs are retrieved from the logd daemon via Loading @@ -48,7 +48,13 @@ class SecurityLogMonitor implements Runnable { private final Lock mLock = new ReentrantLock(); SecurityLogMonitor(DevicePolicyManagerService service) { mService = service; this(service, 0 /* id */); } @VisibleForTesting SecurityLogMonitor(DevicePolicyManagerService service, long id) { this.mService = service; this.mId = id; } private static final boolean DEBUG = false; // STOPSHIP if true. Loading @@ -58,7 +64,7 @@ class SecurityLogMonitor implements Runnable { * it should be less than 100 bytes), setting 1024 entries as the threshold to notify Device * Owner. */ private static final int BUFFER_ENTRIES_NOTIFICATION_LEVEL = 1024; @VisibleForTesting static final int BUFFER_ENTRIES_NOTIFICATION_LEVEL = 1024; /** * The maximum number of entries we should store before dropping earlier logs, to limit the * memory usage. Loading Loading @@ -87,6 +93,8 @@ class SecurityLogMonitor implements Runnable { @GuardedBy("mLock") private ArrayList<SecurityEvent> mPendingLogs = new ArrayList<>(); @GuardedBy("mLock") private long mId; @GuardedBy("mLock") private boolean mAllowedToRetrieve = false; /** Loading @@ -112,6 +120,7 @@ class SecurityLogMonitor implements Runnable { try { if (mMonitorThread == null) { mPendingLogs = new ArrayList<>(); mId = 0; mAllowedToRetrieve = false; mNextAllowedRetrievalTimeMillis = -1; mPaused = false; Loading @@ -137,6 +146,7 @@ class SecurityLogMonitor implements Runnable { } // Reset state and clear buffer mPendingLogs = new ArrayList<>(); mId = 0; mAllowedToRetrieve = false; mNextAllowedRetrievalTimeMillis = -1; mPaused = false; Loading Loading @@ -305,6 +315,7 @@ class SecurityLogMonitor implements Runnable { if (lastNanos > currentNanos) { // New event older than the last we've seen so far, must be due to reordering. if (DEBUG) Slog.d(TAG, "New event in the overlap: " + currentNanos); assignLogId(curEvent); mPendingLogs.add(curEvent); curPos++; } else if (lastNanos < currentNanos) { Loading @@ -317,6 +328,7 @@ class SecurityLogMonitor implements Runnable { if (DEBUG) Slog.d(TAG, "Skipped dup event with timestamp: " + lastNanos); } else { // Wow, what a coincidence, or probably the clock is too coarse. assignLogId(curEvent); mPendingLogs.add(curEvent); if (DEBUG) Slog.d(TAG, "Event timestamp collision: " + lastNanos); } Loading @@ -324,8 +336,13 @@ class SecurityLogMonitor implements Runnable { curPos++; } } // Assign an id to the new logs, after the overlap with mLastEvents. List<SecurityEvent> idLogs = newLogs.subList(curPos, newLogs.size()); for (SecurityEvent event : idLogs) { assignLogId(event); } // Save the rest of the new batch. mPendingLogs.addAll(newLogs.subList(curPos, newLogs.size())); mPendingLogs.addAll(idLogs); if (mPendingLogs.size() > BUFFER_ENTRIES_MAXIMUM_LEVEL) { // Truncate buffer down to half of BUFFER_ENTRIES_MAXIMUM_LEVEL. Loading @@ -334,7 +351,20 @@ class SecurityLogMonitor implements Runnable { mPendingLogs.size())); Slog.i(TAG, "Pending logs buffer full. Discarding old logs."); } if (DEBUG) Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging"); if (DEBUG) Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging," + " with ids " + mPendingLogs.get(0).getId() + " to " + mPendingLogs.get(mPendingLogs.size() - 1).getId()); } @GuardedBy("mLock") private void assignLogId(SecurityEvent event) { event.setId(mId); if (mId == Long.MAX_VALUE) { Slog.i(TAG, "Reached maximum id value; wrapping around."); mId = 0; } else { mId++; } } @Override Loading
services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java 0 → 100644 +61 −0 Original line number Diff line number Diff line package com.android.server.devicepolicy; import static android.app.admin.SecurityLog.TAG_ADB_SHELL_CMD; import android.app.admin.SecurityLog.SecurityEvent; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; import android.util.EventLog; import java.io.IOException; import java.util.ArrayList; import java.util.List; @SmallTest public class SecurityEventTest extends DpmTestBase { private static long ID = 549; private static String DATA = "adb shell some_command"; public void testSecurityEventId() { SecurityEvent event = buildSecurityEvents(1 /* generate a single event */, ID).get(0); assertEquals(ID, event.getId()); event.setId(20); assertEquals(20, event.getId()); } public void testSecurityEventParceling() { // GIVEN an event. SecurityEvent event = buildSecurityEvents(1 /* generate a single event */, ID).get(0); // WHEN parceling the event. Parcel p = Parcel.obtain(); p.writeParcelable(event, 0); p.setDataPosition(0); SecurityEvent unparceledEvent = p.readParcelable(SecurityEventTest.class.getClassLoader()); p.recycle(); // THEN the event state is preserved. assertEquals(event.getTag(), unparceledEvent.getTag()); assertEquals(event.getData(), unparceledEvent.getData()); assertEquals(event.getTimeNanos(), unparceledEvent.getTimeNanos()); assertEquals(event.getId(), unparceledEvent.getId()); } private List<SecurityEvent> buildSecurityEvents(int numEvents, long id) { // Write an event to the EventLog. for (int i = 0; i < numEvents; i++) { EventLog.writeEvent(TAG_ADB_SHELL_CMD, DATA + "_" + i); } List<EventLog.Event> events = new ArrayList<>(); try { EventLog.readEvents(new int[]{TAG_ADB_SHELL_CMD}, events); } catch (IOException e) { fail("Reading a test event from storage failed: " + e); } assertTrue("Unexpected number of events read from the log.", events.size() >= numEvents); // Read events generated by test, from the end of the log. List<SecurityEvent> securityEvents = new ArrayList<>(); for (int i = events.size() - numEvents; i < events.size(); i++) { securityEvents.add(new SecurityEvent(id++, events.get(i).getBytes())); } return securityEvents; } }