From d09cff08882e553afce919865a2cc60b657d4659 Mon Sep 17 00:00:00 2001 From: Ben Komalo Date: Fri, 6 May 2011 14:57:47 -0700 Subject: [PATCH] Make "don't allow camera" a supported policy. This sends the bit to the DPM. Separate changes have been/will be made to change the provision parser and support it in the DPM. Bug: 4185316 Change-Id: I44872ceb095a28539b047a0641cc499c7186a9b3 --- .../android/emailcommon/provider/Policy.java | 4 +- src/com/android/email/SecurityPolicy.java | 25 ++++++-- .../email/activity/setup/AccountSecurity.java | 2 +- .../android/email/SecurityPolicyTests.java | 58 +++++++++++-------- 4 files changed, 57 insertions(+), 32 deletions(-) diff --git a/emailcommon/src/com/android/emailcommon/provider/Policy.java b/emailcommon/src/com/android/emailcommon/provider/Policy.java index 332eb5533..ab0ae921c 100644 --- a/emailcommon/src/com/android/emailcommon/provider/Policy.java +++ b/emailcommon/src/com/android/emailcommon/provider/Policy.java @@ -140,6 +140,7 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol } // We override this method to insure that we never write invalid policy data to the provider + @Override public Uri save(Context context) { normalize(); return super.save(context); @@ -407,9 +408,10 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol sb.append(" "); } + @Override public String toString() { StringBuilder sb = new StringBuilder("["); - if (this.equals(NO_POLICY)) { + if (equals(NO_POLICY)) { sb.append("No policies]"); } else { if (mPasswordMode == PASSWORD_MODE_NONE) { diff --git a/src/com/android/email/SecurityPolicy.java b/src/com/android/email/SecurityPolicy.java index d53c2e0c1..44fac0846 100644 --- a/src/com/android/email/SecurityPolicy.java +++ b/src/com/android/email/SecurityPolicy.java @@ -47,7 +47,7 @@ public class SecurityPolicy { private static SecurityPolicy sInstance = null; private Context mContext; private DevicePolicyManager mDPM; - private ComponentName mAdminName; + private final ComponentName mAdminName; private Policy mAggregatePolicy; // Messages used for DevicePolicyManager callbacks @@ -104,7 +104,8 @@ public class SecurityPolicy { * @return a policy representing the strongest aggregate. If no policy sets are defined, * a lightweight "nothing required" policy will be returned. Never null. */ - /*package*/ Policy computeAggregatePolicy() { + @VisibleForTesting + Policy computeAggregatePolicy() { boolean policiesFound = false; Policy aggregate = new Policy(); aggregate.mPasswordMinLength = Integer.MIN_VALUE; @@ -153,6 +154,7 @@ public class SecurityPolicy { aggregate.mRequireRemoteWipe |= policy.mRequireRemoteWipe; aggregate.mRequireEncryption |= policy.mRequireEncryption; aggregate.mRequireEncryptionExternal |= policy.mRequireEncryptionExternal; + aggregate.mDontAllowCamera |= policy.mDontAllowCamera; policiesFound = true; } } finally { @@ -225,7 +227,7 @@ public class SecurityPolicy { /** * API: Query if the proposed set of policies are supported on the device. * - * @param policies the polices that were requested + * @param policy the polices that were requested * @return boolean if supported */ public boolean isSupported(Policy policy) { @@ -245,6 +247,10 @@ public class SecurityPolicy { if (Environment.isExternalStorageRemovable()) return false; if (!Environment.isExternalStorageEmulated()) return false; } + + // If we ever support devices that can't disable cameras for any reason, we should + // indicate as such in the mDontAllowCamera policy + return true; } @@ -254,7 +260,7 @@ public class SecurityPolicy { * This is used when we have a set of polices that have been requested, but the server * is willing to allow unsupported policies to be considered optional. * - * @param policies the polices that were requested + * @param policy the polices that were requested * @return the same PolicySet if all are supported; A replacement PolicySet if any * unsupported policies were removed */ @@ -276,6 +282,10 @@ public class SecurityPolicy { policy.mRequireEncryptionExternal = false; } } + + // If we ever support devices that can't disable cameras for any reason, we should + // clear the mDontAllowCamera policy + return policy; } @@ -283,7 +293,7 @@ public class SecurityPolicy { * API: Query used to determine if a given policy is "active" (the device is operating at * the required security level). * - * @param policies the policies requested, or null to check aggregate stored policies + * @param policy the policies requested, or null to check aggregate stored policies * @return true if the requested policies are active, false if not. */ public boolean isActive(Policy policy) { @@ -348,7 +358,7 @@ public class SecurityPolicy { * the expiration. In other words, all accounts (that require expiration) will run/stop * based on the requirements of the account with the shortest interval. * - * @param policies the policies requested, or null to check aggregate stored policies + * @param policy the policies requested, or null to check aggregate stored policies * @return zero if the requested policies are active, non-zero bits indicates that more work * is needed (typically, by the user) before the required security polices are fully active. */ @@ -470,6 +480,9 @@ public class SecurityPolicy { dpm.setPasswordMinimumSymbols(mAdminName, 0); dpm.setPasswordMinimumNumeric(mAdminName, 0); dpm.setPasswordMinimumNonLetter(mAdminName, aggregatePolicy.mPasswordComplexChars); + // Device capabilities + dpm.setCameraDisabled(mAdminName, aggregatePolicy.mDontAllowCamera); + // encryption required dpm.setStorageEncryption(mAdminName, aggregatePolicy.mRequireEncryption); // TODO: If we ever support external storage encryption as a first-class feature, diff --git a/src/com/android/email/activity/setup/AccountSecurity.java b/src/com/android/email/activity/setup/AccountSecurity.java index 0738bc6c4..7692d9c2d 100644 --- a/src/com/android/email/activity/setup/AccountSecurity.java +++ b/src/com/android/email/activity/setup/AccountSecurity.java @@ -272,7 +272,7 @@ public class AccountSecurity extends Activity { if (Email.DEBUG) { Log.d(TAG, "Encryption needed; request it via DPM"); } - mTriedSetEncryption = true; + mTriedSetEncryption = true; // launch the activity to start up encryption. Intent intent = new Intent(DevicePolicyManager.ACTION_START_ENCRYPTION); startActivityForResult(intent, REQUEST_ENCRYPTION); diff --git a/tests/src/com/android/email/SecurityPolicyTests.java b/tests/src/com/android/email/SecurityPolicyTests.java index 4dcb73f02..e06967e7a 100644 --- a/tests/src/com/android/email/SecurityPolicyTests.java +++ b/tests/src/com/android/email/SecurityPolicyTests.java @@ -105,7 +105,8 @@ public class SecurityPolicyTests extends ProviderTestCase2 { private Policy setupPolicy(int minPasswordLength, int passwordMode, int maxPasswordFails, int maxScreenLockTime, boolean requireRemoteWipe, int passwordExpirationDays, int passwordHistory, int passwordComplexChars, boolean requireEncryption, - boolean requireEncryptionExternal) throws IllegalArgumentException { + boolean requireEncryptionExternal, boolean dontAllowCamera) + throws IllegalArgumentException { Policy policy = new Policy(); policy.mPasswordMinLength = minPasswordLength; policy.mPasswordMode = passwordMode; @@ -117,6 +118,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { policy.mPasswordComplexChars = passwordComplexChars; policy.mRequireEncryption = requireEncryption; policy.mRequireEncryptionExternal = requireEncryptionExternal; + policy.mDontAllowCamera = dontAllowCamera; return policy; } @@ -138,7 +140,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // first test with partially-populated policies Account a3 = ProviderTestUtils.setupAccount("sec-3", true, mMockContext); Policy p3ain = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0, - false, false); + false, false, false); Policy.setAccountPolicy(mMockContext, a3, p3ain, null); Policy p3aout = mSecurityPolicy.computeAggregatePolicy(); assertNotNull(p3aout); @@ -146,7 +148,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // Repeat that test with fully-populated policies Policy p3bin = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 15, 16, false, 6, 2, 3, - false, false); + false, false, false); Policy.setAccountPolicy(mMockContext, a3, p3bin, null); Policy p3bout = mSecurityPolicy.computeAggregatePolicy(); assertNotNull(p3bout); @@ -160,8 +162,9 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // max complex chars - max logic - will change // encryption required - OR logic - will *not* change here because false // encryption external req'd - OR logic - will *not* change here because false + // don't allow camera - OR logic - will change here because it's true Policy p4in = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 0, 5, 7, - false, false); + false, false, true); Account a4 = ProviderTestUtils.setupAccount("sec-4", true, mMockContext); Policy.setAccountPolicy(mMockContext, a4, p4in, null); Policy p4out = mSecurityPolicy.computeAggregatePolicy(); @@ -176,6 +179,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertFalse(p4out.mRequireRemoteWipe); assertFalse(p4out.mRequireEncryption); assertFalse(p4out.mRequireEncryptionExternal); + assertTrue(p4out.mDontAllowCamera); // add another account which mixes it up (the remaining fields will change) // pw length and pw mode - max logic - will *not* change because smaller #s here @@ -185,8 +189,9 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // history & complex chars - will not change because 0 (unspecified) // encryption required - OR logic - will change here because true // encryption external req'd - OR logic - will *not* change here because false + // don't allow camera - OR logic - will *not* change here because it's already true Policy p5in = setupPolicy(4, Policy.PASSWORD_MODE_SIMPLE, 5, 6, true, 1, 0, 0, - true, false); + true, false, false); Account a5 = ProviderTestUtils.setupAccount("sec-5", true, mMockContext); Policy.setAccountPolicy(mMockContext, a5, p5in, null); Policy p5out = mSecurityPolicy.computeAggregatePolicy(); @@ -200,11 +205,12 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(7, p5out.mPasswordComplexChars); assertTrue(p5out.mRequireRemoteWipe); assertFalse(p5out.mRequireEncryptionExternal); + assertTrue(p5out.mDontAllowCamera); // add another account that continues to mutate fields // encryption external req'd - OR logic - will change here because true Policy p6in = setupPolicy(0, Policy.PASSWORD_MODE_NONE, 0, 0, false, 0, 0, 0, - false, true); + false, true, false); Account a6 = ProviderTestUtils.setupAccount("sec-6", true, mMockContext); Policy.setAccountPolicy(mMockContext, a6, p6in, null); Policy p6out = mSecurityPolicy.computeAggregatePolicy(); @@ -219,13 +225,16 @@ public class SecurityPolicyTests extends ProviderTestCase2 { @SmallTest public void testEquals() { Policy p1 = - setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, false); + setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, false, false); Policy p2 = - setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, false); + setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, false, false); Policy p3 = - setupPolicy(2, Policy.PASSWORD_MODE_SIMPLE, 5, 6, true, 7, 8, 9, false, false); + setupPolicy(2, Policy.PASSWORD_MODE_SIMPLE, 5, 6, true, 7, 8, 9, false, false, false); + Policy p4 = + setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, false, true); assertTrue(p1.equals(p2)); assertFalse(p2.equals(p3)); + assertFalse(p1.equals(p4)); } /** @@ -261,6 +270,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { super(context); } + @Override protected void backupAccounts(Context context) { // For testing, we don't want to back up our accounts } @@ -272,12 +282,12 @@ public class SecurityPolicyTests extends ProviderTestCase2 { public void testDisableAdmin() { Account a1 = ProviderTestUtils.setupAccount("disable-1", true, mMockContext); Policy p1 = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0, - false, false); + false, false, false); Policy.setAccountPolicy(mMockContext, a1, p1, "security-sync-key-1"); Account a2 = ProviderTestUtils.setupAccount("disable-2", true, mMockContext); Policy p2 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 0, 0, 0, - false, false); + false, false, false); Policy.setAccountPolicy(mMockContext, a2, p2, "security-sync-key-2"); Account a3 = ProviderTestUtils.setupAccount("disable-3", true, mMockContext); @@ -333,7 +343,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { Account a2 = ProviderTestUtils.setupAccount("expiring-2", true, mMockContext); Policy p2 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 30, 0, 0, - false, false); + false, false, true); Policy.setAccountPolicy(mMockContext, a2, p2, null); // The expiring account should be returned @@ -343,7 +353,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // Add an account with a longer expiration Account a3 = ProviderTestUtils.setupAccount("expiring-3", true, mMockContext); Policy p3 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 60, 0, 0, - false, false); + false, false, true); Policy.setAccountPolicy(mMockContext, a3, p3, null); // The original expiring account (a2) should be returned @@ -353,7 +363,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // Add an account with a shorter expiration Account a4 = ProviderTestUtils.setupAccount("expiring-4", true, mMockContext); Policy p4 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 15, 0, 0, - false, false); + false, false, true); Policy.setAccountPolicy(mMockContext, a4, p4, null); // The new expiring account (a4) should be returned @@ -383,7 +393,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { Account a1 = ProviderTestUtils.setupAccount("expired-1", true, mMockContext); Account a2 = ProviderTestUtils.setupAccount("expired-2", true, mMockContext); Policy p2 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 0, 0, 0, - false, false); + false, false, true); Policy.setAccountPolicy(mMockContext, a2, p2, null); // Add a mailbox & messages to each account @@ -409,7 +419,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // Add 3rd account that really expires Account a3 = ProviderTestUtils.setupAccount("expired-3", true, mMockContext); Policy p3 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 30, 0, 0, - false, false); + false, false, true); Policy.setAccountPolicy(mMockContext, a3, p3, null); // Add mailbox & messages to 3rd account @@ -447,11 +457,11 @@ public class SecurityPolicyTests extends ProviderTestCase2 { */ public void testClearUnsupportedPolicies() { Policy p1 = - setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, false); + setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, false, false); Policy p2 = - setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, true, false); + setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, true, false, false); Policy p3 = - setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, true); + setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, true, false); mSecurityPolicy = SecurityPolicy.getInstance(mMockContext); DevicePolicyManager dpm = mSecurityPolicy.getDPM(); @@ -474,7 +484,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // If encryption is unsupported, encryption policy bits are cleared Policy policyExpect = setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, - false); + false, false); assertEquals(policyExpect, p2Result); assertEquals(policyExpect, p3Result); } @@ -486,25 +496,25 @@ public class SecurityPolicyTests extends ProviderTestCase2 { public void testGetDPManagerPasswordQuality() { // Policy.PASSWORD_MODE_NONE -> DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED Policy p1 = setupPolicy(0, Policy.PASSWORD_MODE_NONE, - 0, 0, false, 0, 0, 0, false, false); + 0, 0, false, 0, 0, 0, false, false, false); assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, p1.getDPManagerPasswordQuality()); // PASSWORD_MODE_SIMPLE -> PASSWORD_QUALITY_NUMERIC Policy p2 = setupPolicy(4, Policy.PASSWORD_MODE_SIMPLE, - 0, 0, false, 0, 0, 0, false, false); + 0, 0, false, 0, 0, 0, false, false, false); assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, p2.getDPManagerPasswordQuality()); // PASSWORD_MODE_STRONG -> PASSWORD_QUALITY_ALPHANUMERIC Policy p3 = setupPolicy(4, Policy.PASSWORD_MODE_STRONG, - 0, 0, false, 0, 0, 0, false, false); + 0, 0, false, 0, 0, 0, false, false, false); assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, p3.getDPManagerPasswordQuality()); // PASSWORD_MODE_STRONG + complex chars -> PASSWORD_QUALITY_COMPLEX Policy p4 = setupPolicy(4, Policy.PASSWORD_MODE_STRONG, - 0, 0, false, 0, 0 , 2, false, false); + 0, 0, false, 0, 0 , 2, false, false, false); assertEquals(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, p4.getDPManagerPasswordQuality()); }