Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db980f4a authored by Naomi Musgrave's avatar Naomi Musgrave
Browse files

Add id to SecurityEvent.

Test: runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
Test: runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java
Bug: 63910201
Change-Id: I053ec9d6c8281d637ace5dc89057e5f7b5ad6554
parent 792bbf66
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
+4 −0
Original line number Diff line number Diff line
@@ -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 {
+41 −4
Original line number Diff line number Diff line
@@ -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;
@@ -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.
@@ -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);
        }

@@ -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;
@@ -169,6 +205,7 @@ public class SecurityLog {

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeLong(mId);
            dest.writeByteArray(mEvent.getBytes());
        }

@@ -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
@@ -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;
        }

        /**
@@ -201,7 +238,7 @@ public class SecurityLog {
         */
        @Override
        public int hashCode() {
            return mEvent.hashCode();
            return Objects.hash(mEvent, mId);
        }
    }
    /**
+36 −6
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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.
@@ -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.
@@ -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;

    /**
@@ -112,6 +120,7 @@ class SecurityLogMonitor implements Runnable {
        try {
            if (mMonitorThread == null) {
                mPendingLogs = new ArrayList<>();
                mId = 0;
                mAllowedToRetrieve = false;
                mNextAllowedRetrievalTimeMillis = -1;
                mPaused = false;
@@ -137,6 +146,7 @@ class SecurityLogMonitor implements Runnable {
                }
                // Reset state and clear buffer
                mPendingLogs = new ArrayList<>();
                mId = 0;
                mAllowedToRetrieve = false;
                mNextAllowedRetrievalTimeMillis = -1;
                mPaused = false;
@@ -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) {
@@ -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);
                }
@@ -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.
@@ -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
+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;
    }
}