diff --git a/api/current.txt b/api/current.txt index daad5df1be424..ac0a22e066fda 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3577,6 +3577,7 @@ package android.app.admin { method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int USES_ENCRYPTED_STORAGE = 7; // 0x7 + field public static final int USES_POLICY_DISABLE_CAMERA = 8; // 0x8 field public static final int USES_POLICY_EXPIRE_PASSWORD = 6; // 0x6 field public static final int USES_POLICY_FORCE_LOCK = 3; // 0x3 field public static final int USES_POLICY_LIMIT_PASSWORD = 0; // 0x0 @@ -3610,6 +3611,7 @@ package android.app.admin { public class DevicePolicyManager { method public java.util.List getActiveAdmins(); + method public boolean getCameraDisabled(android.content.ComponentName); method public int getCurrentFailedPasswordAttempts(); method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName); method public long getMaximumTimeToLock(android.content.ComponentName); @@ -3633,6 +3635,7 @@ package android.app.admin { method public void lockNow(); method public void removeActiveAdmin(android.content.ComponentName); method public boolean resetPassword(java.lang.String, int); + method public void setCameraDisabled(android.content.ComponentName, boolean); method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int); method public void setMaximumTimeToLock(android.content.ComponentName, long); method public void setPasswordExpirationTimeout(android.content.ComponentName, long); diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 1c7eb98c93bf3..1c37414032a2e 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -130,6 +130,14 @@ public final class DeviceAdminInfo implements Parcelable { */ public static final int USES_ENCRYPTED_STORAGE = 7; + /** + * A type of policy that this device admin can use: disables use of all device cameras. + * + *

To control this policy, the device admin must have a "disable-camera" + * tag in the "uses-policies" section of its meta-data. + */ + public static final int USES_POLICY_DISABLE_CAMERA = 8; + /** @hide */ public static class PolicyInfo { public final int ident; @@ -174,6 +182,9 @@ public final class DeviceAdminInfo implements Parcelable { sPoliciesDisplayOrder.add(new PolicyInfo(USES_ENCRYPTED_STORAGE, "encrypted-storage", com.android.internal.R.string.policylab_encryptedStorage, com.android.internal.R.string.policydesc_encryptedStorage)); + sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_DISABLE_CAMERA, "disable-camera", + com.android.internal.R.string.policylab_disableCamera, + com.android.internal.R.string.policydesc_disableCamera)); for (int i=0; iThe 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. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param disabled Whether or not the camera should be disabled. + */ + public void setCameraDisabled(ComponentName admin, boolean disabled) { + if (mService != null) { + try { + mService.setCameraDisabled(admin, disabled); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * Determine whether or not the device's cameras have been disabled either by the current + * admin, if specified, or all admins. + * @param admin The name of the admin component to check, or null to check if any admins + * have disabled the camera + */ + public boolean getCameraDisabled(ComponentName admin) { + if (mService != null) { + try { + return mService.getCameraDisabled(admin); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return false; + } + /** * @hide */ diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e8caca153467c..9419a62b7de88 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -79,6 +79,9 @@ interface IDevicePolicyManager { boolean getStorageEncryption(in ComponentName who); int getStorageEncryptionStatus(); + void setCameraDisabled(in ComponentName who, boolean disabled); + boolean getCameraDisabled(in ComponentName who); + void setActiveAdmin(in ComponentName policyReceiver, boolean refreshing); boolean isAdminActive(in ComponentName policyReceiver); List getActiveAdmins(); diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5ea984b849d7d..3736157e3432f 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1479,6 +1479,10 @@ Require that stored application data be encrypted + + Disable cameras + + Prevent use of all device cameras diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 3181a9d061b57..d549308705649 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -172,6 +172,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE; boolean encryptionRequested = false; + boolean disableCamera = false; // TODO: review implementation decisions with frameworks team boolean specifiesGlobalProxy = false; @@ -274,6 +275,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.attribute(null, "value", Boolean.toString(encryptionRequested)); out.endTag(null, "encryption-requested"); } + if (disableCamera) { + out.startTag(null, "disable-camera"); + out.attribute(null, "value", Boolean.toString(disableCamera)); + out.endTag(null, "disable-camera"); + } } void readFromXml(XmlPullParser parser) @@ -339,6 +345,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if ("encryption-requested".equals(tag)) { encryptionRequested = Boolean.parseBoolean( parser.getAttributeValue(null, "value")); + } else if ("disable-camera".equals(tag)) { + disableCamera = Boolean.parseBoolean( + parser.getAttributeValue(null, "value")); } else { Slog.w(TAG, "Unknown admin tag: " + tag); } @@ -393,6 +402,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } pw.print(prefix); pw.print("encryptionRequested="); pw.println(encryptionRequested); + pw.print(prefix); pw.print("disableCamera="); + pw.println(disableCamera); } } @@ -424,6 +435,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (removed) { validatePasswordOwnerLocked(); + syncDeviceCapabilitiesLocked(); saveSettingsLocked(); } } @@ -578,6 +590,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mAdminList.remove(admin); mAdminMap.remove(adminReceiver); validatePasswordOwnerLocked(); + syncDeviceCapabilitiesLocked(); if (doProxyCleanup) { resetGlobalProxy(); } @@ -801,6 +814,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } validatePasswordOwnerLocked(); + syncDeviceCapabilitiesLocked(); long timeMs = getMaximumTimeToLock(null); if (timeMs <= 0) { @@ -844,6 +858,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + /** + * 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() { + // Ensure the status of the camera is synced down to the system. Interested native services + // should monitor this value and act accordingly. + boolean systemState = SystemProperties.getBoolean(SYSTEM_PROP_DISABLE_CAMERA, false); + boolean cameraDisabled = getCameraDisabled(null); + if (cameraDisabled != systemState) { + long token = Binder.clearCallingIdentity(); + try { + String value = cameraDisabled ? "1" : "0"; + Slog.v(TAG, "Change in camera state [" + + SYSTEM_PROP_DISABLE_CAMERA + "] = " + value); + SystemProperties.set(SYSTEM_PROP_DISABLE_CAMERA, value); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + public void systemReady() { synchronized (this) { loadSettingsLocked(); @@ -1982,6 +2018,53 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void setEncryptionRequested(boolean encrypt) { } + /** + * The system property used to share the state of the camera. The native camera service + * is expected to read this property and act accordingly. + */ + public static final String SYSTEM_PROP_DISABLE_CAMERA = "sys.secpolicy.camera.disabled"; + + /** + * Disables all device cameras according to the specified admin. + */ + public void setCameraDisabled(ComponentName who, boolean disabled) { + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA); + if (ap.disableCamera != disabled) { + ap.disableCamera = disabled; + saveSettingsLocked(); + } + syncDeviceCapabilitiesLocked(); + } + } + + /** + * Gets whether or not all device cameras are disabled for a given admin, or disabled for any + * active admins. + */ + public boolean getCameraDisabled(ComponentName who) { + synchronized (this) { + if (who != null) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + return (admin != null) ? admin.disableCamera : false; + } + + // Determine whether or not the device camera is disabled for any active admins. + final int N = mAdminList.size(); + for (int i = 0; i < N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (admin.disableCamera) { + return true; + } + } + return false; + } + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)