Allow DO to access bookkeeping information about its own actions

The getLastSecurityLogRetrievalTime(), getLastBugReportRequestTime()
and getLastNetworkLogRetrievalTime() methods are meant to be used by
system code. However, there is no harm in allowing the DO to access the
information they return - because it is information about actions that
the DO itself took.

The advantage of opening up these methods to the DO is that we can
CTS-test them.

Bug: 32692748
Test: DevicePolicyManager unit test + CTS test in separate CL

Change-Id: I1470fca2a82b9955f7aed5e8b50220bea8b56fc9
diff --git a/api/test-current.txt b/api/test-current.txt
index 4f2bea5..b8810c8e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6034,6 +6034,9 @@
     method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
+    method public long getLastBugReportRequestTime();
+    method public long getLastNetworkLogRetrievalTime();
+    method public long getLastSecurityLogRetrievalTime();
     method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
     method public long getMaximumTimeToLock(android.content.ComponentName);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 2fa2318..8c2749f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -24,6 +24,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.Activity;
@@ -6784,9 +6785,12 @@
      *
      * @return the time at which the device owner most recently retrieved security logging entries,
      *         in milliseconds since epoch; -1 if security logging entries were never retrieved.
+     * @throws SecurityException if the caller is not the device owner, does not hold the
+     *         MANAGE_USERS permission and is not the system.
      *
      * @hide
      */
+    @TestApi
     public long getLastSecurityLogRetrievalTime() {
         try {
             return mService.getLastSecurityLogRetrievalTime();
@@ -6800,9 +6804,12 @@
      *
      * @return the time at which the device owner most recently requested a bug report, in
      *         milliseconds since epoch; -1 if a bug report was never requested.
+     * @throws SecurityException if the caller is not the device owner, does not hold the
+     *         MANAGE_USERS permission and is not the system.
      *
      * @hide
      */
+    @TestApi
     public long getLastBugReportRequestTime() {
         try {
             return mService.getLastBugReportRequestTime();
@@ -6817,9 +6824,12 @@
      *
      * @return the time at which the device owner most recently retrieved network logging events, in
      *         milliseconds since epoch; -1 if network logging events were never retrieved.
+     * @throws SecurityException if the caller is not the device owner, does not hold the
+     *         MANAGE_USERS permission and is not the system.
      *
      * @hide
      */
+    @TestApi
     public long getLastNetworkLogRetrievalTime() {
         try {
             return mService.getLastNetworkLogRetrievalTime();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f7bb190..c32ee3a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6097,6 +6097,11 @@
             admin.userRestrictions = null;
             admin.forceEphemeralUsers = false;
             mUserManagerInternal.setForceEphemeralUsers(admin.forceEphemeralUsers);
+            final DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
+            policyData.mLastSecurityLogRetrievalTime = -1;
+            policyData.mLastBugReportRequestTime = -1;
+            policyData.mLastNetworkLogsRetrievalTime = -1;
+            saveSettingsLocked(UserHandle.USER_SYSTEM);
         }
         clearUserPoliciesLocked(userId);
 
@@ -6581,10 +6586,14 @@
         }
     }
 
-    private void enforceSystemUid() {
-        if (!isCallerWithSystemUid()) {
-            throw new SecurityException("Only the system can call this method.");
+    private void enforceDeviceOwnerOrManageUsers() {
+        synchronized (this) {
+            if (getActiveAdminWithPolicyForUidLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+                    mInjector.binderGetCallingUid()) != null) {
+                return;
+            }
         }
+        enforceManageUsers();
     }
 
     private void ensureCallerPackage(@Nullable String packageName) {
@@ -9852,19 +9861,19 @@
 
     @Override
     public long getLastSecurityLogRetrievalTime() {
-        enforceSystemUid();
+        enforceDeviceOwnerOrManageUsers();
         return getUserData(UserHandle.USER_SYSTEM).mLastSecurityLogRetrievalTime;
      }
 
     @Override
     public long getLastBugReportRequestTime() {
-        enforceSystemUid();
+        enforceDeviceOwnerOrManageUsers();
         return getUserData(UserHandle.USER_SYSTEM).mLastBugReportRequestTime;
      }
 
     @Override
     public long getLastNetworkLogRetrievalTime() {
-        enforceSystemUid();
+        enforceDeviceOwnerOrManageUsers();
         return getUserData(UserHandle.USER_SYSTEM).mLastNetworkLogsRetrievalTime;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e55cafb..cff5b41 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2273,11 +2273,13 @@
         assertFalse(dpms.hasUserSetupCompleted());
     }
 
-    private long getLastSecurityLogRetrievalTime() {
+    private void clearDeviceOwner() throws Exception {
         final long ident = mContext.binder.clearCallingIdentity();
-        final long lastSecurityLogRetrievalTime = dpm.getLastSecurityLogRetrievalTime();
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        doReturn(DpmMockContext.CALLER_SYSTEM_USER_UID).when(mContext.packageManager)
+                .getPackageUidAsUser(eq(admin1.getPackageName()), anyInt());
+        dpm.clearDeviceOwnerApp(admin1.getPackageName());
         mContext.binder.restoreCallingIdentity(ident);
-        return lastSecurityLogRetrievalTime;
     }
 
     public void testGetLastSecurityLogRetrievalTime() throws Exception {
@@ -2288,16 +2290,16 @@
                 .thenReturn(true);
 
         // No logs were retrieved so far.
-        assertEquals(-1, getLastSecurityLogRetrievalTime());
+        assertEquals(-1, dpm.getLastSecurityLogRetrievalTime());
 
         // Enabling logging should not change the timestamp.
         dpm.setSecurityLoggingEnabled(admin1, true);
-        assertEquals(-1, getLastSecurityLogRetrievalTime());
+        assertEquals(-1, dpm.getLastSecurityLogRetrievalTime());
 
         // Retrieving the logs should update the timestamp.
         final long beforeRetrieval = System.currentTimeMillis();
         dpm.retrieveSecurityLogs(admin1);
-        final long firstSecurityLogRetrievalTime = getLastSecurityLogRetrievalTime();
+        final long firstSecurityLogRetrievalTime = dpm.getLastSecurityLogRetrievalTime();
         final long afterRetrieval = System.currentTimeMillis();
         assertTrue(firstSecurityLogRetrievalTime >= beforeRetrieval);
         assertTrue(firstSecurityLogRetrievalTime <= afterRetrieval);
@@ -2305,33 +2307,40 @@
         // Retrieving the pre-boot logs should update the timestamp.
         Thread.sleep(2);
         dpm.retrievePreRebootSecurityLogs(admin1);
-        final long secondSecurityLogRetrievalTime = getLastSecurityLogRetrievalTime();
+        final long secondSecurityLogRetrievalTime = dpm.getLastSecurityLogRetrievalTime();
         assertTrue(secondSecurityLogRetrievalTime > firstSecurityLogRetrievalTime);
 
         // Checking the timestamp again should not change it.
         Thread.sleep(2);
-        assertEquals(secondSecurityLogRetrievalTime, getLastSecurityLogRetrievalTime());
+        assertEquals(secondSecurityLogRetrievalTime, dpm.getLastSecurityLogRetrievalTime());
 
         // Retrieving the logs again should update the timestamp.
         dpm.retrieveSecurityLogs(admin1);
-        final long thirdSecurityLogRetrievalTime = getLastSecurityLogRetrievalTime();
+        final long thirdSecurityLogRetrievalTime = dpm.getLastSecurityLogRetrievalTime();
         assertTrue(thirdSecurityLogRetrievalTime > secondSecurityLogRetrievalTime);
 
         // Disabling logging should not change the timestamp.
         Thread.sleep(2);
         dpm.setSecurityLoggingEnabled(admin1, false);
-        assertEquals(thirdSecurityLogRetrievalTime, getLastSecurityLogRetrievalTime());
+        assertEquals(thirdSecurityLogRetrievalTime, dpm.getLastSecurityLogRetrievalTime());
 
         // Restarting the DPMS should not lose the timestamp.
         initializeDpms();
-        assertEquals(thirdSecurityLogRetrievalTime, getLastSecurityLogRetrievalTime());
-    }
+        assertEquals(thirdSecurityLogRetrievalTime, dpm.getLastSecurityLogRetrievalTime());
 
-    private long getLastBugReportRequestTime() {
-        final long ident = mContext.binder.clearCallingIdentity();
-        final long lastBugRequestTime = dpm.getLastBugReportRequestTime();
-        mContext.binder.restoreCallingIdentity(ident);
-        return lastBugRequestTime;
+        // Any uid holding MANAGE_USERS permission can retrieve the timestamp.
+        mContext.binder.callingUid = 1234567;
+        mContext.callerPermissions.add(permission.MANAGE_USERS);
+        assertEquals(thirdSecurityLogRetrievalTime, dpm.getLastSecurityLogRetrievalTime());
+        mContext.callerPermissions.remove(permission.MANAGE_USERS);
+
+        // System can retrieve the timestamp.
+        mContext.binder.clearCallingIdentity();
+        assertEquals(thirdSecurityLogRetrievalTime, dpm.getLastSecurityLogRetrievalTime());
+
+        // Removing the device owner should clear the timestamp.
+        clearDeviceOwner();
+        assertEquals(-1, dpm.getLastSecurityLogRetrievalTime());
     }
 
     public void testGetLastBugReportRequestTime() throws Exception {
@@ -2346,30 +2355,37 @@
                 anyObject())).thenReturn(Color.WHITE);
 
         // No bug reports were requested so far.
-        assertEquals(-1, getLastSecurityLogRetrievalTime());
+        assertEquals(-1, dpm.getLastBugReportRequestTime());
 
         // Requesting a bug report should update the timestamp.
         final long beforeRequest = System.currentTimeMillis();
         dpm.requestBugreport(admin1);
-        final long bugReportRequestTime = getLastBugReportRequestTime();
+        final long bugReportRequestTime = dpm.getLastBugReportRequestTime();
         final long afterRequest = System.currentTimeMillis();
         assertTrue(bugReportRequestTime >= beforeRequest);
         assertTrue(bugReportRequestTime <= afterRequest);
 
         // Checking the timestamp again should not change it.
         Thread.sleep(2);
-        assertEquals(bugReportRequestTime, getLastBugReportRequestTime());
+        assertEquals(bugReportRequestTime, dpm.getLastBugReportRequestTime());
 
         // Restarting the DPMS should not lose the timestamp.
         initializeDpms();
-        assertEquals(bugReportRequestTime, getLastBugReportRequestTime());
-    }
+        assertEquals(bugReportRequestTime, dpm.getLastBugReportRequestTime());
 
-    private long getLastNetworkLogRetrievalTime() {
-        final long ident = mContext.binder.clearCallingIdentity();
-        final long lastNetworkLogRetrievalTime = dpm.getLastNetworkLogRetrievalTime();
-        mContext.binder.restoreCallingIdentity(ident);
-        return lastNetworkLogRetrievalTime;
+        // Any uid holding MANAGE_USERS permission can retrieve the timestamp.
+        mContext.binder.callingUid = 1234567;
+        mContext.callerPermissions.add(permission.MANAGE_USERS);
+        assertEquals(bugReportRequestTime, dpm.getLastBugReportRequestTime());
+        mContext.callerPermissions.remove(permission.MANAGE_USERS);
+
+        // System can retrieve the timestamp.
+        mContext.binder.clearCallingIdentity();
+        assertEquals(bugReportRequestTime, dpm.getLastBugReportRequestTime());
+
+        // Removing the device owner should clear the timestamp.
+        clearDeviceOwner();
+        assertEquals(-1, dpm.getLastBugReportRequestTime());
     }
 
     public void testGetLastNetworkLogRetrievalTime() throws Exception {
@@ -2380,41 +2396,55 @@
                 .thenReturn(true);
 
         // No logs were retrieved so far.
-        assertEquals(-1, getLastNetworkLogRetrievalTime());
+        assertEquals(-1, dpm.getLastNetworkLogRetrievalTime());
 
         // Attempting to retrieve logs without enabling logging should not change the timestamp.
         dpm.retrieveNetworkLogs(admin1, 0 /* batchToken */);
-        assertEquals(-1, getLastNetworkLogRetrievalTime());
+        assertEquals(-1, dpm.getLastNetworkLogRetrievalTime());
 
         // Enabling logging should not change the timestamp.
         dpm.setNetworkLoggingEnabled(admin1, true);
-        assertEquals(-1, getLastNetworkLogRetrievalTime());
+        assertEquals(-1, dpm.getLastNetworkLogRetrievalTime());
 
         // Retrieving the logs should update the timestamp.
         final long beforeRetrieval = System.currentTimeMillis();
         dpm.retrieveNetworkLogs(admin1, 0 /* batchToken */);
-        final long firstNetworkLogRetrievalTime = getLastNetworkLogRetrievalTime();
+        final long firstNetworkLogRetrievalTime = dpm.getLastNetworkLogRetrievalTime();
         final long afterRetrieval = System.currentTimeMillis();
         assertTrue(firstNetworkLogRetrievalTime >= beforeRetrieval);
         assertTrue(firstNetworkLogRetrievalTime <= afterRetrieval);
 
         // Checking the timestamp again should not change it.
         Thread.sleep(2);
-        assertEquals(firstNetworkLogRetrievalTime, getLastNetworkLogRetrievalTime());
+        assertEquals(firstNetworkLogRetrievalTime, dpm.getLastNetworkLogRetrievalTime());
 
         // Retrieving the logs again should update the timestamp.
         dpm.retrieveNetworkLogs(admin1, 0 /* batchToken */);
-        final long secondNetworkLogRetrievalTime = getLastNetworkLogRetrievalTime();
+        final long secondNetworkLogRetrievalTime = dpm.getLastNetworkLogRetrievalTime();
         assertTrue(secondNetworkLogRetrievalTime > firstNetworkLogRetrievalTime);
 
         // Disabling logging should not change the timestamp.
         Thread.sleep(2);
         dpm.setNetworkLoggingEnabled(admin1, false);
-        assertEquals(secondNetworkLogRetrievalTime, getLastNetworkLogRetrievalTime());
+        assertEquals(secondNetworkLogRetrievalTime, dpm.getLastNetworkLogRetrievalTime());
 
         // Restarting the DPMS should not lose the timestamp.
         initializeDpms();
-        assertEquals(secondNetworkLogRetrievalTime, getLastNetworkLogRetrievalTime());
+        assertEquals(secondNetworkLogRetrievalTime, dpm.getLastNetworkLogRetrievalTime());
+
+        // Any uid holding MANAGE_USERS permission can retrieve the timestamp.
+        mContext.binder.callingUid = 1234567;
+        mContext.callerPermissions.add(permission.MANAGE_USERS);
+        assertEquals(secondNetworkLogRetrievalTime, dpm.getLastNetworkLogRetrievalTime());
+        mContext.callerPermissions.remove(permission.MANAGE_USERS);
+
+        // System can retrieve the timestamp.
+        mContext.binder.clearCallingIdentity();
+        assertEquals(secondNetworkLogRetrievalTime, dpm.getLastNetworkLogRetrievalTime());
+
+        // Removing the device owner should clear the timestamp.
+        clearDeviceOwner();
+        assertEquals(-1, dpm.getLastNetworkLogRetrievalTime());
     }
 
     private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {