From 8d76e72251eb6f83580cd8b70000d6904fe24096 Mon Sep 17 00:00:00 2001 From: Bartosz Fabianowski Date: Fri, 25 Nov 2016 12:36:20 +0100 Subject: [PATCH] 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 --- api/test-current.txt | 3 + .../app/admin/DevicePolicyManager.java | 10 ++ .../DevicePolicyManagerService.java | 21 ++-- .../devicepolicy/DevicePolicyManagerTest.java | 100 ++++++++++++------ 4 files changed, 93 insertions(+), 41 deletions(-) diff --git a/api/test-current.txt b/api/test-current.txt index 4f2bea5956a0c..b8810c8e0f763 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -6034,6 +6034,9 @@ package android.app.admin { method public java.lang.CharSequence getDeviceOwnerLockScreenInfo(); method public java.util.List 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 2fa2318638101..8c2749f5f2d12 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -24,6 +24,7 @@ import android.annotation.RequiresPermission; 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 @@ public class DevicePolicyManager { * * @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 @@ public class DevicePolicyManager { * * @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 @@ public class DevicePolicyManager { * * @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 f7bb190990b00..c32ee3a042d6a 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @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 e55cafb6730bb..cff5b416f03b3 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { .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 @@ public class DevicePolicyManagerTest extends DpmTestBase { // 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { .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) {