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

Commit d7dd779e authored by Naomi Musgrave's avatar Naomi Musgrave Committed by Android (Google) Code Review
Browse files

Merge "Add id to SecurityEvent."

parents 54a70178 db980f4a
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;
    }
}