forceUpdateUserSetupComplete should have no effect in user build

Bug:30031808
Change-Id: I908a495633e03ace770a8ec19196841d19dfd41d
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ae9b169..9005d3e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6467,7 +6467,10 @@
 
     /**
      * @hide
-     * Force update user setup completed status
+     * Force update user setup completed status. This API has no effect on user build.
+     * @throws {@link SecurityException} if the caller has no
+     *         {@link android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS} or the caller is
+     *         not {@link UserHandle.SYSTEM_USER}
      */
     public void forceUpdateUserSetupComplete() {
         try {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b28843a..f87f723 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1392,6 +1392,10 @@
             return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
         }
 
+        boolean isBuildDebuggable() {
+            return Build.IS_DEBUGGABLE;
+        }
+
         LockPatternUtils newLockPatternUtils() {
             return new LockPatternUtils(mContext);
         }
@@ -8978,6 +8982,14 @@
                 android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
     }
 
+    private void enforceCallerSystemUserHandle() {
+        final int callingUid = mInjector.binderGetCallingUid();
+        final int userId = UserHandle.getUserId(callingUid);
+        if (userId != UserHandle.USER_SYSTEM) {
+            throw new SecurityException("Caller has to be in user 0");
+        }
+    }
+
     @Override
     public boolean isUninstallInQueue(final String packageName) {
         enforceCanManageDeviceAdmin();
@@ -9142,17 +9154,18 @@
     @Override
     public void forceUpdateUserSetupComplete() {
         enforceCanManageProfileAndDeviceOwners();
-        List<UserInfo> users = mUserManager.getUsers(true);
-        final int N = users.size();
-        for (int i = 0; i < N; i++) {
-            int userHandle = users.get(i).id;
-            boolean isUserCompleted = mInjector.settingsSecureGetIntForUser(
-                    Settings.Secure.USER_SETUP_COMPLETE, 0, userHandle) != 0;
-            DevicePolicyData policy = getUserData(userHandle);
-            policy.mUserSetupComplete = isUserCompleted;
-            synchronized (this) {
-                saveSettingsLocked(userHandle);
-            }
+        enforceCallerSystemUserHandle();
+        // no effect if it's called from user build
+        if (!mInjector.isBuildDebuggable()) {
+            return;
+        }
+        final int userId = UserHandle.USER_SYSTEM;
+        boolean isUserCompleted = mInjector.settingsSecureGetIntForUser(
+                Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0;
+        DevicePolicyData policy = getUserData(userId);
+        policy.mUserSetupComplete = isUserCompleted;
+        synchronized (this) {
+            saveSettingsLocked(userId);
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 6cb4a82..80be62b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -357,5 +357,10 @@
         TelephonyManager getTelephonyManager() {
             return context.telephonyManager;
         }
+
+        @Override
+        boolean isBuildDebuggable() {
+            return context.buildMock.isDebuggable;
+        }
     }
 }
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 2d96bff..0fd1286 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2117,6 +2117,76 @@
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
     }
 
+    public void testForceUpdateUserSetupComplete_permission() {
+        // GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted
+        try {
+            dpm.forceUpdateUserSetupComplete();
+            fail("Didn't throw SecurityException");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testForceUpdateUserSetupComplete_systemUser() {
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        // GIVEN calling from user 20
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        try {
+            dpm.forceUpdateUserSetupComplete();
+            fail("Didn't throw SecurityException");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testForceUpdateUserSetupComplete_userbuild() {
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+        final int userId = UserHandle.USER_SYSTEM;
+        // GIVEN userComplete is false in SettingsProvider
+        setUserSetupCompleteForUser(false, userId);
+
+        // GIVEN userComplete is true in DPM
+        DevicePolicyManagerService.DevicePolicyData userData =
+                new DevicePolicyManagerService.DevicePolicyData(userId);
+        userData.mUserSetupComplete = true;
+        dpms.mUserData.put(UserHandle.USER_SYSTEM, userData);
+
+        // GIVEN it's user build
+        mContext.buildMock.isDebuggable = false;
+
+        assertTrue(dpms.hasUserSetupCompleted());
+
+        dpm.forceUpdateUserSetupComplete();
+
+        // THEN the state in dpms is not changed
+        assertTrue(dpms.hasUserSetupCompleted());
+    }
+
+    public void testForceUpdateUserSetupComplete_userDebugbuild() {
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+        final int userId = UserHandle.USER_SYSTEM;
+        // GIVEN userComplete is false in SettingsProvider
+        setUserSetupCompleteForUser(false, userId);
+
+        // GIVEN userComplete is true in DPM
+        DevicePolicyManagerService.DevicePolicyData userData =
+                new DevicePolicyManagerService.DevicePolicyData(userId);
+        userData.mUserSetupComplete = true;
+        dpms.mUserData.put(UserHandle.USER_SYSTEM, userData);
+
+        // GIVEN it's userdebug build
+        mContext.buildMock.isDebuggable = true;
+
+        assertTrue(dpms.hasUserSetupCompleted());
+
+        dpm.forceUpdateUserSetupComplete();
+
+        // THEN the state in dpms is not changed
+        assertFalse(dpms.hasUserSetupCompleted());
+    }
+
     private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
         when(mContext.settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
                 userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 0783afc..37430ad 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -132,6 +132,10 @@
         }
     }
 
+    public static class BuildMock {
+        public boolean isDebuggable = true;
+    }
+
     public static class PowerManagerForMock {
         public WakeLock newWakeLock(int levelAndFlags, String tag) {
             return null;
@@ -272,6 +276,8 @@
 
     private final ArrayList<UserInfo> mUserInfos = new ArrayList<>();
 
+    public final BuildMock buildMock = new BuildMock();
+
     public DpmMockContext(Context context, File dataDir) {
         realTestContext = context;