diff --git a/res/xml/device_admin.xml b/res/xml/device_admin.xml index a2f413f34..085ab0fb7 100644 --- a/res/xml/device_admin.xml +++ b/res/xml/device_admin.xml @@ -22,5 +22,6 @@ + diff --git a/src/com/android/email/SecurityPolicy.java b/src/com/android/email/SecurityPolicy.java index 40d93d79d..47880d52a 100644 --- a/src/com/android/email/SecurityPolicy.java +++ b/src/com/android/email/SecurityPolicy.java @@ -48,7 +48,7 @@ public class SecurityPolicy { private PolicySet mAggregatePolicy; /* package */ static final PolicySet NO_POLICY_SET = - new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false, 0, 0, 0); + new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false, 0, 0, 0, false); /** * This projection on Account is for scanning/reading @@ -104,6 +104,7 @@ public class SecurityPolicy { * password history take the max (strongest mode) * password expiration take the min (strongest mode) * password complex chars take the max (strongest mode) + * encryption take the max (logical or) * * @return a policy representing the strongest aggregate. If no policy sets are defined, * a lightweight "nothing required" policy will be returned. Never null. @@ -119,6 +120,7 @@ public class SecurityPolicy { int passwordHistory = Integer.MIN_VALUE; int passwordExpirationDays = Integer.MAX_VALUE; int passwordComplexChars = Integer.MIN_VALUE; + boolean requireEncryption = false; Cursor c = mContext.getContentResolver().query(Account.CONTENT_URI, ACCOUNT_SECURITY_PROJECTION, Account.SECURITY_NONZERO_SELECTION, null, null); @@ -147,6 +149,7 @@ public class SecurityPolicy { passwordComplexChars); } requireRemoteWipe |= p.mRequireRemoteWipe; + requireEncryption |= p.mRequireEncryption; policiesFound = true; } } @@ -165,7 +168,7 @@ public class SecurityPolicy { return new PolicySet(minPasswordLength, passwordMode, maxPasswordFails, maxScreenLockTime, requireRemoteWipe, passwordExpirationDays, passwordHistory, - passwordComplexChars); + passwordComplexChars, requireEncryption); } else { return NO_POLICY_SET; } @@ -210,6 +213,57 @@ public class SecurityPolicy { setActivePolicies(); } + /** + * API: Query if the proposed set of policies are supported on the device. + * + * @param policies requested + * @return boolean if supported + */ + public boolean isSupported(PolicySet policies) { + // IMPLEMENTATION: At this time, the only policy which might not be supported is + // encryption (which requires low-level systems support). Other policies are fully + // supported by the framework and do not need to be checked. + if (policies.mRequireEncryption) { + int encryptionStatus = getDPM().getStorageEncryption(null); + if (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) { + return false; + } + } + return true; + } + + /** + * 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 + * @return true if the requested policies are active, false if not. + */ + public boolean isActive(PolicySet policies) { + int reasons = getInactiveReasons(policies); + return reasons == 0; + } + + /** + * Return bits from isActive: Device Policy Manager has not been activated + */ + public final static int INACTIVE_NEED_ACTIVATION = 1; + + /** + * Return bits from isActive: Some required configuration is not correct (no user action). + */ + public final static int INACTIVE_NEED_CONFIGURATION = 2; + + /** + * Return bits from isActive: Password needs to be set or updated + */ + public final static int INACTIVE_NEED_PASSWORD = 4; + + /** + * Return bits from isActive: Encryption has not be enabled + */ + public final static int INACTIVE_NEED_ENCRYPTION = 8; + /** * API: Query used to determine if a given policy is "active" (the device is operating at * the required security level). @@ -227,37 +281,39 @@ public class SecurityPolicy { * based on the requirements of the account with the shortest interval. * * @param policies the policies requested, or null to check aggregate stored policies - * @return true if the policies are active, false if not active + * @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. */ - public boolean isActive(PolicySet policies) { + public int getInactiveReasons(PolicySet policies) { // select aggregate set if needed if (policies == null) { policies = getAggregatePolicy(); } // quick check for the "empty set" of no policies if (policies == NO_POLICY_SET) { - return true; + return 0; } + int reasons = 0; DevicePolicyManager dpm = getDPM(); if (isActiveAdmin()) { // check each policy explicitly if (policies.mMinPasswordLength > 0) { if (dpm.getPasswordMinimumLength(mAdminName) < policies.mMinPasswordLength) { - return false; + reasons |= INACTIVE_NEED_PASSWORD; } } if (policies.mPasswordMode > 0) { if (dpm.getPasswordQuality(mAdminName) < policies.getDPManagerPasswordQuality()) { - return false; + reasons |= INACTIVE_NEED_PASSWORD; } if (!dpm.isActivePasswordSufficient()) { - return false; + reasons |= INACTIVE_NEED_PASSWORD; } } if (policies.mMaxScreenLockTime > 0) { // Note, we use seconds, dpm uses milliseconds if (dpm.getMaximumTimeToLock(mAdminName) > policies.mMaxScreenLockTime * 1000) { - return false; + reasons |= INACTIVE_NEED_CONFIGURATION; } } if (policies.mPasswordExpirationDays > 0) { @@ -265,34 +321,40 @@ public class SecurityPolicy { long currentTimeout = dpm.getPasswordExpirationTimeout(mAdminName); if (currentTimeout == 0 || currentTimeout > policies.getDPManagerPasswordExpirationTimeout()) { - return false; + reasons |= INACTIVE_NEED_PASSWORD; } // confirm that the current password hasn't expired long expirationDate = dpm.getPasswordExpiration(mAdminName); long timeUntilExpiration = expirationDate - System.currentTimeMillis(); boolean expired = timeUntilExpiration < 0; if (expired) { - return false; + reasons |= INACTIVE_NEED_PASSWORD; } } if (policies.mPasswordHistory > 0) { if (dpm.getPasswordHistoryLength(mAdminName) < policies.mPasswordHistory) { - return false; + reasons |= INACTIVE_NEED_PASSWORD; } } if (policies.mPasswordComplexChars > 0) { if (dpm.getPasswordMinimumNonLetter(mAdminName) < policies.mPasswordComplexChars) { - return false; + reasons |= INACTIVE_NEED_PASSWORD; + } + } + if (policies.mRequireEncryption) { + int encryptionStatus = getDPM().getStorageEncryption(null); + if (encryptionStatus != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) { + reasons |= INACTIVE_NEED_ENCRYPTION; } } // password failures are counted locally - no test required here // no check required for remote wipe (it's supported, if we're the admin) - // making it this far means we passed! - return true; + // If we made it all the way, reasons == 0 here. Otherwise it's a list of grievances. + return reasons; } // return false, not active - return false; + return INACTIVE_NEED_ACTIVATION; } /** @@ -323,6 +385,8 @@ public class SecurityPolicy { dpm.setPasswordHistoryLength(mAdminName, policies.mPasswordHistory); // password minimum complex characters dpm.setPasswordMinimumNonLetter(mAdminName, policies.mPasswordComplexChars); + // encryption required + dpm.setStorageEncryption(mAdminName, policies.mRequireEncryption); } } @@ -422,14 +486,16 @@ public class SecurityPolicy { private static final int PASSWORD_EXPIRATION_SHIFT = 26; private static final long PASSWORD_EXPIRATION_MASK = 1023L << PASSWORD_EXPIRATION_SHIFT; public static final int PASSWORD_EXPIRATION_MAX = 1023; - // bit 35..42: password history (length; 0=not required) + // bit 36..43: password history (length; 0=not required) private static final int PASSWORD_HISTORY_SHIFT = 36; private static final long PASSWORD_HISTORY_MASK = 255L << PASSWORD_HISTORY_SHIFT; public static final int PASSWORD_HISTORY_MAX = 255; - // bit 42..46: min complex characters (0=not required) + // bit 44..48: min complex characters (0=not required) private static final int PASSWORD_COMPLEX_CHARS_SHIFT = 44; private static final long PASSWORD_COMPLEX_CHARS_MASK = 31L << PASSWORD_COMPLEX_CHARS_SHIFT; public static final int PASSWORD_COMPLEX_CHARS_MAX = 31; + // bit 49: requires device encryption + private static final long REQUIRE_ENCRYPTION = 1L << 49; /* Convert days to mSec (used for password expiration) */ private static final long DAYS_TO_MSEC = 24 * 60 * 60 * 1000; @@ -444,6 +510,7 @@ public class SecurityPolicy { /*package*/ final int mPasswordExpirationDays; /*package*/ final int mPasswordHistory; /*package*/ final int mPasswordComplexChars; + /*package*/ final boolean mRequireEncryption; public int getMinPasswordLengthForTest() { return mMinPasswordLength; @@ -465,6 +532,10 @@ public class SecurityPolicy { return mRequireRemoteWipe; } + public boolean isRequireEncryptionForTest() { + return mRequireEncryption; + } + /** * Create from raw values. * @param minPasswordLength (0=not enforced) @@ -479,7 +550,8 @@ public class SecurityPolicy { */ public PolicySet(int minPasswordLength, int passwordMode, int maxPasswordFails, int maxScreenLockTime, boolean requireRemoteWipe, int passwordExpirationDays, - int passwordHistory, int passwordComplexChars) throws IllegalArgumentException { + int passwordHistory, int passwordComplexChars, boolean requireEncryption) + throws IllegalArgumentException { // If we're not enforcing passwords, make sure we clean up related values, since EAS // can send non-zero values for any or all of these if (passwordMode == PASSWORD_MODE_NONE) { @@ -529,6 +601,7 @@ public class SecurityPolicy { mPasswordExpirationDays = passwordExpirationDays; mPasswordHistory = passwordHistory; mPasswordComplexChars = passwordComplexChars; + mRequireEncryption = requireEncryption; } /** @@ -558,6 +631,7 @@ public class SecurityPolicy { (int) ((flags & PASSWORD_HISTORY_MASK) >> PASSWORD_HISTORY_SHIFT); mPasswordComplexChars = (int) ((flags & PASSWORD_COMPLEX_CHARS_MASK) >> PASSWORD_COMPLEX_CHARS_SHIFT); + mRequireEncryption = 0 != (flags & REQUIRE_ENCRYPTION); } /** @@ -661,6 +735,7 @@ public class SecurityPolicy { dest.writeInt(mPasswordExpirationDays); dest.writeInt(mPasswordHistory); dest.writeInt(mPasswordComplexChars); + dest.writeInt(mRequireEncryption ? 1 : 0); } /** @@ -675,6 +750,7 @@ public class SecurityPolicy { mPasswordExpirationDays = in.readInt(); mPasswordHistory = in.readInt(); mPasswordComplexChars = in.readInt(); + mRequireEncryption = in.readInt() == 1; } @Override @@ -689,12 +765,11 @@ public class SecurityPolicy { flags |= mPasswordMode; flags |= (long)mMaxPasswordFails << PASSWORD_MAX_FAILS_SHIFT; flags |= (long)mMaxScreenLockTime << SCREEN_LOCK_TIME_SHIFT; - if (mRequireRemoteWipe) { - flags |= REQUIRE_REMOTE_WIPE; - } + if (mRequireRemoteWipe) flags |= REQUIRE_REMOTE_WIPE; flags |= (long)mPasswordHistory << PASSWORD_HISTORY_SHIFT; flags |= (long)mPasswordExpirationDays << PASSWORD_EXPIRATION_SHIFT; flags |= (long)mPasswordComplexChars << PASSWORD_COMPLEX_CHARS_SHIFT; + if (mRequireEncryption) flags |= REQUIRE_ENCRYPTION; return flags; } @@ -705,7 +780,8 @@ public class SecurityPolicy { + mMaxScreenLockTime + " remote-wipe-req=" + mRequireRemoteWipe + " pw-expiration=" + mPasswordExpirationDays + " pw-history=" + mPasswordHistory - + " pw-complex-chars=" + mPasswordComplexChars + "}"; + + " pw-complex-chars=" + mPasswordComplexChars + + " require-encryption=" + mRequireEncryption + "}"; } } diff --git a/src/com/android/email/activity/setup/AccountSecurity.java b/src/com/android/email/activity/setup/AccountSecurity.java index 51dce447c..a5c4fe3f5 100644 --- a/src/com/android/email/activity/setup/AccountSecurity.java +++ b/src/com/android/email/activity/setup/AccountSecurity.java @@ -37,12 +37,15 @@ import android.os.Bundle; * 3. When we are actively administrating, check current policies and see if they're sufficient * 4. If not, set policies * 5. If necessary, request for user to update device password + * 6. If necessary, request for user to activate device encryption */ public class AccountSecurity extends Activity { private static final String EXTRA_ACCOUNT_ID = "com.android.email.activity.setup.ACCOUNT_ID"; private static final int REQUEST_ENABLE = 1; + private static final int REQUEST_PASSWORD = 2; + private static final int REQUEST_ENCRYPTION = 3; /** * Used for generating intent for this activity (which is intended to be launched @@ -91,7 +94,11 @@ public class AccountSecurity extends Activity { } } else { // already active - try to set actual policies, finish, and return - setActivePolicies(); + boolean startedActivity = setActivePolicies(); + if (startedActivity) { + // keep this activity on stack to process result + return; + } } } } @@ -104,11 +111,17 @@ public class AccountSecurity extends Activity { */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + boolean startedActivity = false; switch (requestCode) { + case REQUEST_PASSWORD: + case REQUEST_ENCRYPTION: + // Force the result code and just check the DPM to check for actual success + resultCode = Activity.RESULT_OK; + //$FALL-THROUGH$ case REQUEST_ENABLE: if (resultCode == Activity.RESULT_OK) { // now active - try to set actual policies - setActivePolicies(); + startedActivity = setActivePolicies(); } else { // failed - repost notification, and exit final long accountId = getIntent().getLongExtra(EXTRA_ACCOUNT_ID, -1); @@ -123,30 +136,45 @@ public class AccountSecurity extends Activity { } } } - finish(); + if (!startedActivity) { + finish(); + } super.onActivityResult(requestCode, resultCode, data); } /** * Now that we are connected as an active device admin, try to set the device to the * correct security level, and ask for a password if necessary. + * @return true if we started another activity (and should not finish(), as we're waiting for + * their result.) */ - private void setActivePolicies() { + private boolean setActivePolicies() { SecurityPolicy sp = SecurityPolicy.getInstance(this); // check current security level - if sufficient, we're done! if (sp.isActive(null)) { Account.clearSecurityHoldOnAllAccounts(this); - return; + return false; } // set current security level sp.setActivePolicies(); // check current security level - if sufficient, we're done! - if (sp.isActive(null)) { + int inactiveReasons = sp.getInactiveReasons(null); + if (inactiveReasons == 0) { Account.clearSecurityHoldOnAllAccounts(this); - return; + return false; } - // if not sufficient, launch the activity to have the user set a new password. - Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); - startActivity(intent); + // If password or encryption required, launch relevant intent + if ((inactiveReasons & SecurityPolicy.INACTIVE_NEED_PASSWORD) != 0) { + // launch the activity to have the user set a new password. + Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); + startActivityForResult(intent, REQUEST_PASSWORD); + return true; + } else if ((inactiveReasons & SecurityPolicy.INACTIVE_NEED_ENCRYPTION) != 0) { + // launch the activity to start up encryption. + Intent intent = new Intent(DevicePolicyManager.ACTION_START_ENCRYPTION); + startActivityForResult(intent, REQUEST_ENCRYPTION); + return true; + } + return false; } } diff --git a/src/com/android/exchange/adapter/ProvisionParser.java b/src/com/android/exchange/adapter/ProvisionParser.java index f9429eb31..076a4caef 100644 --- a/src/com/android/exchange/adapter/ProvisionParser.java +++ b/src/com/android/exchange/adapter/ProvisionParser.java @@ -196,7 +196,7 @@ public class ProvisionParser extends Parser { mPolicySet = new SecurityPolicy.PolicySet(minPasswordLength, passwordMode, maxPasswordFails, maxScreenLockTime, true, passwordExpirationDays, passwordHistory, - passwordComplexChars); + passwordComplexChars, false); } /** @@ -253,7 +253,7 @@ public class ProvisionParser extends Parser { mPolicySet = new PolicySet(sps.mMinPasswordLength, sps.mPasswordMode, sps.mMaxPasswordFails, sps.mMaxScreenLockTime, true, sps.mPasswordExpiration, sps.mPasswordHistory, - sps.mPasswordComplexChars); + sps.mPasswordComplexChars, false); } /** diff --git a/tests/src/com/android/email/SecurityPolicyTests.java b/tests/src/com/android/email/SecurityPolicyTests.java index 862b8fa64..b97f04861 100644 --- a/tests/src/com/android/email/SecurityPolicyTests.java +++ b/tests/src/com/android/email/SecurityPolicyTests.java @@ -48,7 +48,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { private Context mMockContext; private static final PolicySet EMPTY_POLICY_SET = - new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false, 0, 0, 0); + new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false, 0, 0, 0, false); public SecurityPolicyTests() { super(EmailProvider.class, EmailProvider.EMAIL_AUTHORITY); @@ -108,28 +108,28 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // We know that EMPTY_POLICY_SET doesn't generate an Exception or we wouldn't be here // Try some illegal parameters try { - new PolicySet(100, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0); + new PolicySet(100, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0, false); fail("Too-long password allowed"); } catch (IllegalArgumentException e) { } try { - new PolicySet(0, PolicySet.PASSWORD_MODE_STRONG + 1, 0, 0, false, 0, 0, 0); + new PolicySet(0, PolicySet.PASSWORD_MODE_STRONG + 1, 0, 0, false, 0, 0, 0, false); fail("Illegal password mode allowed"); } catch (IllegalArgumentException e) { } PolicySet ps = new PolicySet(0, PolicySet.PASSWORD_MODE_SIMPLE, 0, - PolicySet.SCREEN_LOCK_TIME_MAX + 1, false, 0, 0, 0); + PolicySet.SCREEN_LOCK_TIME_MAX + 1, false, 0, 0, 0, false); assertEquals(PolicySet.SCREEN_LOCK_TIME_MAX, ps.getMaxScreenLockTimeForTest()); ps = new PolicySet(0, PolicySet.PASSWORD_MODE_SIMPLE, - PolicySet.PASSWORD_MAX_FAILS_MAX + 1, 0, false, 0, 0, 0); + PolicySet.PASSWORD_MAX_FAILS_MAX + 1, 0, false, 0, 0, 0, false); assertEquals(PolicySet.PASSWORD_MAX_FAILS_MAX, ps.getMaxPasswordFailsForTest()); // All password related fields should be zero when password mode is NONE // Illegal values for these fields should be ignored ps = new PolicySet(999/*length*/, PolicySet.PASSWORD_MODE_NONE, 999/*fails*/, 9999/*screenlock*/, false, 999/*expir*/, 999/*history*/, - 999/*complex*/); + 999/*complex*/, false); assertEquals(0, ps.mMinPasswordLength); assertEquals(0, ps.mMaxScreenLockTime); assertEquals(0, ps.mMaxPasswordFails); @@ -139,7 +139,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // With a simple password, we should set complex chars to zero ps = new PolicySet(4/*length*/, PolicySet.PASSWORD_MODE_SIMPLE, - 0, 0, false, 0, 0, 3/*complex*/); + 0, 0, false, 0, 0, 3/*complex*/, false); assertEquals(4, ps.mMinPasswordLength); assertEquals(0, ps.mPasswordComplexChars); } @@ -165,14 +165,16 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // with a single account in security mode, should return same security as in account // first test with partially-populated policies Account a3 = ProviderTestUtils.setupAccount("sec-3", false, mMockContext); - PolicySet p3ain = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0); + PolicySet p3ain = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0, + false); p3ain.writeAccount(a3, null, true, mMockContext); PolicySet p3aout = sp.computeAggregatePolicy(); assertNotNull(p3aout); assertEquals(p3ain, p3aout); // Repeat that test with fully-populated policies - PolicySet p3bin = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 15, 16, false, 6, 2, 3); + PolicySet p3bin = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 15, 16, false, 6, 2, 3, + false); p3bin.writeAccount(a3, null, true, mMockContext); PolicySet p3bout = sp.computeAggregatePolicy(); assertNotNull(p3bout); @@ -184,7 +186,9 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // wipe required - OR logic - will *not* change here because false // expiration - will not change because 0 (unspecified) // max complex chars - max logic - will change - PolicySet p4in = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 0, 5, 7); + // encryption required - OR logic - will *not* change here because false + PolicySet p4in = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 0, 5, 7, + false); Account a4 = ProviderTestUtils.setupAccount("sec-4", false, mMockContext); p4in.writeAccount(a4, null, true, mMockContext); PolicySet p4out = sp.computeAggregatePolicy(); @@ -197,6 +201,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(5, p4out.mPasswordHistory); assertEquals(7, p4out.mPasswordComplexChars); assertFalse(p4out.mRequireRemoteWipe); + assertFalse(p4out.mRequireEncryption); // 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 @@ -204,7 +209,9 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // wipe required - OR logic - will change here because true // expiration time - min logic - will change because lower here // history & complex chars - will not change because 0 (unspecified) - PolicySet p5in = new PolicySet(4, PolicySet.PASSWORD_MODE_SIMPLE, 5, 6, true, 1, 0, 0); + // encryption required - OR logic - will change here because true + PolicySet p5in = new PolicySet(4, PolicySet.PASSWORD_MODE_SIMPLE, 5, 6, true, 1, 0, 0, + true); Account a5 = ProviderTestUtils.setupAccount("sec-5", false, mMockContext); p5in.writeAccount(a5, null, true, mMockContext); PolicySet p5out = sp.computeAggregatePolicy(); @@ -214,9 +221,10 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(5, p5out.mMaxPasswordFails); assertEquals(6, p5out.mMaxScreenLockTime); assertEquals(1, p5out.mPasswordExpirationDays); - assertEquals(5, p4out.mPasswordHistory); - assertEquals(7, p4out.mPasswordComplexChars); + assertEquals(5, p5out.mPasswordHistory); + assertEquals(7, p5out.mPasswordComplexChars); assertTrue(p5out.mRequireRemoteWipe); + assertTrue(p5out.mRequireEncryption); } /** @@ -244,8 +252,9 @@ public class SecurityPolicyTests extends ProviderTestCase2 { */ @SmallTest public void testFieldIsolation() { + // Check PASSWORD_LENGTH PolicySet p = new PolicySet(PolicySet.PASSWORD_LENGTH_MAX, PolicySet.PASSWORD_MODE_SIMPLE, - 0, 0, false, 0, 0 ,0); + 0, 0, false, 0, 0 ,0, false); assertEquals(PolicySet.PASSWORD_MODE_SIMPLE, p.mPasswordMode); assertEquals(PolicySet.PASSWORD_LENGTH_MAX, p.mMinPasswordLength); assertEquals(0, p.mMaxPasswordFails); @@ -254,8 +263,10 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(0, p.mPasswordHistory); assertEquals(0, p.mPasswordComplexChars); assertFalse(p.mRequireRemoteWipe); + assertFalse(p.mRequireEncryption); - p = new PolicySet(0, PolicySet.PASSWORD_MODE_STRONG, 0, 0, false, 0, 0, 0); + // Check PASSWORD_MODE + p = new PolicySet(0, PolicySet.PASSWORD_MODE_STRONG, 0, 0, false, 0, 0, 0, false); assertEquals(PolicySet.PASSWORD_MODE_STRONG, p.mPasswordMode); assertEquals(0, p.mMinPasswordLength); assertEquals(0, p.mMaxPasswordFails); @@ -264,9 +275,11 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(0, p.mPasswordHistory); assertEquals(0, p.mPasswordComplexChars); assertFalse(p.mRequireRemoteWipe); + assertFalse(p.mRequireEncryption); + // Check PASSWORD_FAILS (note, mode must be set for this to be non-zero) p = new PolicySet(0, PolicySet.PASSWORD_MODE_SIMPLE, PolicySet.PASSWORD_MAX_FAILS_MAX, 0, - false, 0, 0, 0); + false, 0, 0, 0, false); assertEquals(PolicySet.PASSWORD_MODE_SIMPLE, p.mPasswordMode); assertEquals(0, p.mMinPasswordLength); assertEquals(PolicySet.PASSWORD_MAX_FAILS_MAX, p.mMaxPasswordFails); @@ -275,9 +288,11 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(0, p.mPasswordHistory); assertEquals(0, p.mPasswordComplexChars); assertFalse(p.mRequireRemoteWipe); + assertFalse(p.mRequireEncryption); + // Check SCREEN_LOCK_TIME (note, mode must be set for this to be non-zero) p = new PolicySet(0, PolicySet.PASSWORD_MODE_SIMPLE, 0, PolicySet.SCREEN_LOCK_TIME_MAX, - false, 0, 0, 0); + false, 0, 0, 0, false); assertEquals(PolicySet.PASSWORD_MODE_SIMPLE, p.mPasswordMode); assertEquals(0, p.mMinPasswordLength); assertEquals(0, p.mMaxPasswordFails); @@ -286,8 +301,10 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(0, p.mPasswordHistory); assertEquals(0, p.mPasswordComplexChars); assertFalse(p.mRequireRemoteWipe); + assertFalse(p.mRequireEncryption); - p = new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, true, 0, 0, 0); + // Check REQUIRE_REMOTE_WIPE + p = new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, true, 0, 0, 0, false); assertEquals(PolicySet.PASSWORD_MODE_NONE, p.mPasswordMode); assertEquals(0, p.mMinPasswordLength); assertEquals(0, p.mMaxPasswordFails); @@ -296,9 +313,11 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(0, p.mPasswordHistory); assertEquals(0, p.mPasswordComplexChars); assertTrue(p.mRequireRemoteWipe); + assertFalse(p.mRequireEncryption); + // Check PASSWORD_EXPIRATION (note, mode must be set for this to be non-zero) p = new PolicySet(0, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false, - PolicySet.PASSWORD_EXPIRATION_MAX, 0, 0); + PolicySet.PASSWORD_EXPIRATION_MAX, 0, 0, false); assertEquals(PolicySet.PASSWORD_MODE_SIMPLE, p.mPasswordMode); assertEquals(0, p.mMinPasswordLength); assertEquals(0, p.mMaxPasswordFails); @@ -307,9 +326,11 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(0, p.mPasswordHistory); assertEquals(0, p.mPasswordComplexChars); assertFalse(p.mRequireRemoteWipe); + assertFalse(p.mRequireEncryption); + // Check PASSWORD_HISTORY (note, mode must be set for this to be non-zero) p = new PolicySet(0, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, - PolicySet.PASSWORD_HISTORY_MAX, 0); + PolicySet.PASSWORD_HISTORY_MAX, 0, false); assertEquals(PolicySet.PASSWORD_MODE_SIMPLE, p.mPasswordMode); assertEquals(0, p.mMinPasswordLength); assertEquals(0, p.mMaxPasswordFails); @@ -318,9 +339,11 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(PolicySet.PASSWORD_HISTORY_MAX, p.mPasswordHistory); assertEquals(0, p.mPasswordComplexChars); assertFalse(p.mRequireRemoteWipe); + assertFalse(p.mRequireEncryption); + // Check PASSWORD_COMPLEX_CHARS (note, mode must be set for this to be non-zero) p = new PolicySet(0, PolicySet.PASSWORD_MODE_STRONG, 0, 0, false, 0, 0, - PolicySet.PASSWORD_COMPLEX_CHARS_MAX); + PolicySet.PASSWORD_COMPLEX_CHARS_MAX, false); assertEquals(PolicySet.PASSWORD_MODE_STRONG, p.mPasswordMode); assertEquals(0, p.mMinPasswordLength); assertEquals(0, p.mMaxPasswordFails); @@ -329,6 +352,19 @@ public class SecurityPolicyTests extends ProviderTestCase2 { assertEquals(0, p.mPasswordHistory); assertEquals(PolicySet.PASSWORD_COMPLEX_CHARS_MAX, p.mPasswordComplexChars); assertFalse(p.mRequireRemoteWipe); + assertFalse(p.mRequireEncryption); + + // Check REQUIRE_ENCRYPTION + p = new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false, 0, 0, 0, true); + assertEquals(PolicySet.PASSWORD_MODE_NONE, p.mPasswordMode); + assertEquals(0, p.mMinPasswordLength); + assertEquals(0, p.mMaxPasswordFails); + assertEquals(0, p.mMaxScreenLockTime); + assertEquals(0, p.mPasswordExpirationDays); + assertEquals(0, p.mPasswordHistory); + assertEquals(0, p.mPasswordComplexChars); + assertFalse(p.mRequireRemoteWipe); + assertTrue(p.mRequireEncryption); } /** @@ -336,7 +372,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { */ @SmallTest public void testAccountEncoding() { - PolicySet p1 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9); + PolicySet p1 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false); Account a = new Account(); final String SYNC_KEY = "test_sync_key"; p1.writeAccount(a, SYNC_KEY, false, null); @@ -350,9 +386,9 @@ public class SecurityPolicyTests extends ProviderTestCase2 { */ @SmallTest public void testEquals() { - PolicySet p1 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9); - PolicySet p2 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9); - PolicySet p3 = new PolicySet(2, PolicySet.PASSWORD_MODE_SIMPLE, 5, 6, true, 7, 8, 9); + PolicySet p1 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false); + PolicySet p2 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false); + PolicySet p3 = new PolicySet(2, PolicySet.PASSWORD_MODE_SIMPLE, 5, 6, true, 7, 8, 9, false); assertTrue(p1.equals(p2)); assertFalse(p2.equals(p3)); } @@ -401,11 +437,13 @@ public class SecurityPolicyTests extends ProviderTestCase2 { */ public void testDisableAdmin() { Account a1 = ProviderTestUtils.setupAccount("disable-1", false, mMockContext); - PolicySet p1 = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0); + PolicySet p1 = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0, + false); p1.writeAccount(a1, "sync-key-1", true, mMockContext); Account a2 = ProviderTestUtils.setupAccount("disable-2", false, mMockContext); - PolicySet p2 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 0, 0, 0); + PolicySet p2 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 0, 0, 0, + false); p2.writeAccount(a2, "sync-key-2", true, mMockContext); Account a3 = ProviderTestUtils.setupAccount("disable-3", false, mMockContext); @@ -460,7 +498,8 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // Add a single expiring account Account a2 = ProviderTestUtils.setupAccount("expiring-2", false, mMockContext); - PolicySet p2 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 30, 0, 0); + PolicySet p2 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 30, 0, 0, + false); p2.writeAccount(a2, "sync-key-2", true, mMockContext); // The expiring account should be returned @@ -469,7 +508,8 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // Add an account with a longer expiration Account a3 = ProviderTestUtils.setupAccount("expiring-3", false, mMockContext); - PolicySet p3 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 60, 0, 0); + PolicySet p3 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 60, 0, 0, + false); p3.writeAccount(a3, "sync-key-3", true, mMockContext); // The original expiring account (a2) should be returned @@ -478,7 +518,8 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // Add an account with a shorter expiration Account a4 = ProviderTestUtils.setupAccount("expiring-4", false, mMockContext); - PolicySet p4 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 15, 0, 0); + PolicySet p4 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 15, 0, 0, + false); p4.writeAccount(a4, "sync-key-4", true, mMockContext); // The new expiring account (a4) should be returned @@ -507,7 +548,8 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // Two accounts - a1 is normal, a2 has security (but no expiration) Account a1 = ProviderTestUtils.setupAccount("expired-1", true, mMockContext); Account a2 = ProviderTestUtils.setupAccount("expired-2", false, mMockContext); - PolicySet p2 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 0, 0, 0); + PolicySet p2 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 0, 0, 0, + false); p2.writeAccount(a2, "sync-key-2", true, mMockContext); // Add a mailbox & messages to each account @@ -532,7 +574,8 @@ public class SecurityPolicyTests extends ProviderTestCase2 { // Add 3rd account that really expires Account a3 = ProviderTestUtils.setupAccount("expired-3", false, mMockContext); - PolicySet p3 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 30, 0, 0); + PolicySet p3 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 30, 0, 0, + false); p3.writeAccount(a3, "sync-key-3", true, mMockContext); // Add mailbox & messages to 3rd account