Allow DO to disable camera device-wise.

Bug 24538855

Change-Id: I421690f14ee57fa818d2b233fe48a90a0a575a9e
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 09c0a6e..77a9795 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -653,7 +653,7 @@
             null, //WRITE_SETTINGS
             UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW
             null, //ACCESS_NOTIFICATIONS
-            null, //CAMERA
+            UserManager.DISALLOW_CAMERA, //CAMERA
             UserManager.DISALLOW_RECORD_AUDIO, //RECORD_AUDIO
             null, //PLAY_AUDIO
             null, //READ_CLIPBOARD
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9e9d949..0fdf3d3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2271,6 +2271,8 @@
      * on the device, for this user. After setting this, no applications running as this user
      * will be able to access any cameras on the device.
      *
+     * <p>If the caller is device owner, then the restriction will be applied to all users.
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} to be able to call
      * this method; if it has not, a security exception will be thrown.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 17e1a28..6b97cda 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -487,6 +487,16 @@
     public static final String DISALLOW_RECORD_AUDIO = "no_record_audio";
 
     /**
+     * Specifies if a user is not allowed to use the camera.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @hide
+     */
+    public static final String DISALLOW_CAMERA = "no_camera";
+
+    /**
      * Allows apps in the parent profile to handle web links from the managed profile.
      *
      * This user restriction has an effect only in a managed profile.
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4dd7388..e188343 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -742,10 +742,11 @@
             mBaseUserRestrictions.put(userId, newRestrictions);
         }
 
-        mCachedEffectiveUserRestrictions.put(
-                userId, computeEffectiveUserRestrictionsRL(userId));
+        final Bundle effective = computeEffectiveUserRestrictionsRL(userId);
 
-        applyUserRestrictionsRL(userId, mBaseUserRestrictions.get(userId), prevRestrictions);
+        mCachedEffectiveUserRestrictions.put(userId, effective);
+
+        applyUserRestrictionsRL(userId, effective, prevRestrictions);
     }
 
     @GuardedBy("mRestrictionsLock")
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b4c8f96..2e7f609 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1016,6 +1016,7 @@
         }
     }
 
+    // DO NOT call it while taking the "this" lock, which could cause a dead lock.
     private void handlePackagesChanged(String packageName, int userHandle) {
         boolean removed = false;
         if (VERBOSE_LOG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
@@ -1042,7 +1043,6 @@
             }
             if (removed) {
                 validatePasswordOwnerLocked(policy);
-                syncDeviceCapabilitiesLocked(policy);
                 saveSettingsLocked(policy.mUserHandle);
             }
 
@@ -1061,6 +1061,14 @@
                 }
             }
         }
+        if (removed) {
+            synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+                synchronized (DevicePolicyManagerService.this) {
+                    mUserManagerInternal.updateEffectiveUserRestrictionsRL(
+                            userHandle);
+                }
+            }
+        }
     }
 
     /**
@@ -1682,7 +1690,7 @@
         }
     }
 
-    void removeActiveAdminLocked(final ComponentName adminReceiver, int userHandle) {
+    void removeActiveAdminLocked(final ComponentName adminReceiver, final int userHandle) {
         final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
         if (admin != null) {
             synchronized (this) {
@@ -1701,7 +1709,6 @@
                                 policy.mAdminList.remove(admin);
                                 policy.mAdminMap.remove(adminReceiver);
                                 validatePasswordOwnerLocked(policy);
-                                syncDeviceCapabilitiesLocked(policy);
                                 if (doProxyCleanup) {
                                     resetGlobalProxyLocked(getUserData(userHandle));
                                 }
@@ -1709,6 +1716,12 @@
                                 updateMaximumTimeToLockLocked(policy);
                                 policy.mRemovingAdmins.remove(adminReceiver);
                             }
+                            synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+                                synchronized (DevicePolicyManagerService.this) {
+                                    mUserManagerInternal.updateEffectiveUserRestrictionsRL(
+                                            userHandle);
+                                }
+                            }
                         }
                     });
         }
@@ -2022,7 +2035,6 @@
         }
 
         validatePasswordOwnerLocked(policy);
-        syncDeviceCapabilitiesLocked(policy);
         updateMaximumTimeToLockLocked(policy);
         updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
         if (policy.mStatusBarDisabled) {
@@ -2089,31 +2101,6 @@
         }
     }
 
-    /**
-     * Pushes down policy information to the system for any policies related to general device
-     * capabilities that need to be enforced by lower level services (e.g. Camera services).
-     */
-    void syncDeviceCapabilitiesLocked(DevicePolicyData policy) {
-        // Ensure the status of the camera is synced down to the system. Interested native services
-        // should monitor this value and act accordingly.
-        String cameraPropertyForUser = SYSTEM_PROP_DISABLE_CAMERA_PREFIX + policy.mUserHandle;
-        boolean systemState = mInjector.systemPropertiesGetBoolean(cameraPropertyForUser, false);
-        boolean cameraDisabled = getCameraDisabled(null, policy.mUserHandle);
-        if (cameraDisabled != systemState) {
-            long token = mInjector.binderClearCallingIdentity();
-            try {
-                String value = cameraDisabled ? "1" : "0";
-                if (VERBOSE_LOG) {
-                    Slog.v(LOG_TAG, "Change in camera state ["
-                            + cameraPropertyForUser + "] = " + value);
-                }
-                mInjector.systemPropertiesSet(cameraPropertyForUser, value);
-            } finally {
-                mInjector.binderRestoreCallingIdentity(token);
-            }
-        }
-    }
-
     @VisibleForTesting
     void systemReady(int phase) {
         if (!mHasFeature) {
@@ -4329,13 +4316,6 @@
     }
 
     /**
-     * The system property used to share the state of the camera. The native camera service
-     * is expected to read this property and act accordingly. The userId should be appended
-     * to this key.
-     */
-    public static final String SYSTEM_PROP_DISABLE_CAMERA_PREFIX = "sys.secpolicy.camera.off_";
-
-    /**
      * Disables all device cameras according to the specified admin.
      */
     @Override
@@ -4352,7 +4332,16 @@
                 ap.disableCamera = disabled;
                 saveSettingsLocked(userHandle);
             }
-            syncDeviceCapabilitiesLocked(getUserData(userHandle));
+        }
+        // Tell the user manager that the restrictions have changed.
+        synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
+            synchronized (this) {
+                if (isDeviceOwner(who)) {
+                    mUserManagerInternal.updateEffectiveUserRestrictionsForAllUsersRL();
+                } else {
+                    mUserManagerInternal.updateEffectiveUserRestrictionsRL(userHandle);
+                }
+            }
         }
     }
 
@@ -4370,7 +4359,13 @@
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
                 return (admin != null) ? admin.disableCamera : false;
             }
+            // First, see if DO has set it.  If so, it's device-wide.
+            final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+            if (deviceOwner != null && deviceOwner.disableCamera) {
+                return true;
+            }
 
+            // Then check each device admin on the user.
             DevicePolicyData policy = getUserData(userHandle);
             // Determine whether or not the device camera is disabled for any active admins.
             final int N = policy.mAdminList.size();
@@ -4404,7 +4399,6 @@
                 ap.disabledKeyguardFeatures = which;
                 saveSettingsLocked(userHandle);
             }
-            syncDeviceCapabilitiesLocked(getUserData(userHandle));
         }
     }
 
@@ -5036,7 +5030,6 @@
                     DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
             ap.trustAgentInfos.put(agent.flattenToString(), new TrustAgentInfo(args));
             saveSettingsLocked(userHandle);
-            syncDeviceCapabilitiesLocked(getUserData(userHandle));
         }
     }
 
@@ -5602,6 +5595,7 @@
         }
     }
 
+    // DO NOT call it while taking the "this" lock, which could cause a dead lock.
     @Override
     public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner) {
         Preconditions.checkNotNull(who, "ComponentName is null");
@@ -5612,7 +5606,7 @@
                 ActiveAdmin activeAdmin =
                         getActiveAdminForCallerLocked(who,
                                 DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-                boolean isDeviceOwner = isDeviceOwner(who);
+                final boolean isDeviceOwner = isDeviceOwner(who);
                 if (!isDeviceOwner && userHandle != UserHandle.USER_SYSTEM
                         && DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
                     throw new SecurityException(
@@ -6463,8 +6457,10 @@
                         deviceOwner == null ? null : deviceOwner.userRestrictions;
                 final Bundle profileOwnerRestrictions =
                         profileOwner == null ? null : profileOwner.userRestrictions;
+                final boolean cameraDisabled = getCameraDisabled(null, userId);
 
-                if (deviceOwnerRestrictions == null && profileOwnerRestrictions == null) {
+                if (deviceOwnerRestrictions == null && profileOwnerRestrictions == null
+                        && !cameraDisabled) {
                     // No restrictions to merge.
                     return inBundle;
                 }
@@ -6473,6 +6469,11 @@
                 UserRestrictionsUtils.merge(composed, deviceOwnerRestrictions);
                 UserRestrictionsUtils.merge(composed, profileOwnerRestrictions);
 
+                // Also merge in the camera restriction.
+                if (cameraDisabled) {
+                    composed.putBoolean(UserManager.DISALLOW_CAMERA, true);
+                }
+
                 return composed;
             }
         }