summaryrefslogtreecommitdiff
path: root/tests/src/com/android/email/SecurityPolicyTests.java
blob: 83087b670dd59852607a5f6f779891d74a434d8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.email;

import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.test.ProviderTestCase2;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.Suppress;

import com.android.email.provider.ContentCache;
import com.android.email.provider.EmailProvider;
import com.android.email.provider.ProviderTestUtils;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.provider.Policy;
import com.android.emailcommon.service.LegacyPolicySet;

/**
 * This is a series of unit tests for backup/restore of the SecurityPolicy class.
 *
 * You can run this entire test case with:
 *   runtest -c com.android.email.SecurityPolicyTests email
 */

// TODO: after b/12085240 gets fixed, we need to see if this test can be enabled
@Suppress
@MediumTest
public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
    private Context mMockContext;
    private SecurityPolicy mSecurityPolicy;

    public SecurityPolicyTests() {
        super(EmailProvider.class, EmailContent.AUTHORITY);
    }

    private static final Policy EMPTY_POLICY = new Policy();

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mMockContext = new MockContext2(getMockContext(), mContext);
        // Invalidate all caches, since we reset the database for each test
        ContentCache.invalidateAllCaches();
    }

    /**
     * Delete any dummy accounts we set up for this test
     */
    @Override
    protected void tearDown() throws Exception {
        super.tearDown();
    }

    /**
     * Private context wrapper used to add back getPackageName() for these tests.
     *
     * This class also implements {@link Context} method(s) that are called during tests.
     */
    private static class MockContext2 extends ContextWrapper {

        private final Context mRealContext;

        public MockContext2(Context mockContext, Context realContext) {
            super(mockContext);
            mRealContext = realContext;
        }

        @Override
        public Context getApplicationContext() {
            return this;
        }

        @Override
        public String getPackageName() {
            return mRealContext.getPackageName();
        }

        @Override
        public Object getSystemService(String name) {
            return mRealContext.getSystemService(name);
        }
    }

    /**
     * Create a Policy using the arguments formerly used to create a PolicySet; this minimizes the
     * changes needed for re-using the PolicySet unit test logic
     */
    private Policy setupPolicy(int minPasswordLength, int passwordMode, int maxPasswordFails,
            int maxScreenLockTime, boolean requireRemoteWipe, int passwordExpirationDays,
            int passwordHistory, int passwordComplexChars, boolean requireEncryption,
            boolean dontAllowCamera)
            throws IllegalArgumentException {
        Policy policy = new Policy();
        policy.mPasswordMinLength = minPasswordLength;
        policy.mPasswordMode = passwordMode;
        policy.mPasswordMaxFails = maxPasswordFails;
        policy.mMaxScreenLockTime = maxScreenLockTime;
        policy.mRequireRemoteWipe = requireRemoteWipe;
        policy.mPasswordExpirationDays = passwordExpirationDays;
        policy.mPasswordHistory = passwordHistory;
        policy.mPasswordComplexChars = passwordComplexChars;
        policy.mRequireEncryption = requireEncryption;
        policy.mDontAllowCamera = dontAllowCamera;
        return policy;
    }

    /**
     * Test business logic of aggregating accounts with policies
     */
    public void testAggregator() {
        mSecurityPolicy = SecurityPolicy.getInstance(mMockContext);

        // with no accounts, should return empty set
        assertEquals(EMPTY_POLICY, mSecurityPolicy.computeAggregatePolicy());

        // with accounts having no security, empty set
        ProviderTestUtils.setupAccount("no-sec-1", true, mMockContext);
        ProviderTestUtils.setupAccount("no-sec-2", true, mMockContext);
        assertEquals(EMPTY_POLICY, mSecurityPolicy.computeAggregatePolicy());

        // 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", true, mMockContext);
        Policy p3ain = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0,
                false, false);
        SecurityPolicy.setAccountPolicy(mMockContext, a3, p3ain, null);
        Policy p3aout = mSecurityPolicy.computeAggregatePolicy();
        assertNotNull(p3aout);
        assertEquals(p3ain, p3aout);

        // Repeat that test with fully-populated policies
        Policy p3bin = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 15, 16, false, 6, 2, 3,
                false, false);
        SecurityPolicy.setAccountPolicy(mMockContext, a3, p3bin, null);
        Policy p3bout = mSecurityPolicy.computeAggregatePolicy();
        assertNotNull(p3bout);
        assertEquals(p3bin, p3bout);

        // add another account which mixes it up (some fields will change, others will not)
        // pw length and pw mode - max logic - will change because larger #s here
        // fail count and lock timer - min logic - will *not* change because larger #s here
        // wipe required - OR logic - will *not* change here because false
        // expiration - will not change because 0 (unspecified)
        // max complex chars - max logic - will change
        // encryption required - 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, true);
        Account a4 = ProviderTestUtils.setupAccount("sec-4", true, mMockContext);
        SecurityPolicy.setAccountPolicy(mMockContext, a4, p4in, null);
        Policy p4out = mSecurityPolicy.computeAggregatePolicy();
        assertNotNull(p4out);
        assertEquals(20, p4out.mPasswordMinLength);
        assertEquals(Policy.PASSWORD_MODE_STRONG, p4out.mPasswordMode);
        assertEquals(15, p4out.mPasswordMaxFails);
        assertEquals(16, p4out.mMaxScreenLockTime);
        assertEquals(6, p4out.mPasswordExpirationDays);
        assertEquals(5, p4out.mPasswordHistory);
        assertEquals(7, p4out.mPasswordComplexChars);
        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
        // fail count and lock timer - min logic - will change because smaller #s here
        // 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)
        // encryption required - OR logic - will change here because true
        // 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);
        Account a5 = ProviderTestUtils.setupAccount("sec-5", true, mMockContext);
        SecurityPolicy.setAccountPolicy(mMockContext, a5, p5in, null);
        Policy p5out = mSecurityPolicy.computeAggregatePolicy();
        assertNotNull(p5out);
        assertEquals(20, p5out.mPasswordMinLength);
        assertEquals(Policy.PASSWORD_MODE_STRONG, p5out.mPasswordMode);
        assertEquals(5, p5out.mPasswordMaxFails);
        assertEquals(6, p5out.mMaxScreenLockTime);
        assertEquals(1, p5out.mPasswordExpirationDays);
        assertEquals(5, p5out.mPasswordHistory);
        assertEquals(7, p5out.mPasswordComplexChars);
        assertTrue(p5out.mRequireRemoteWipe);
        assertFalse(p5out.mRequireEncryptionExternal);
        assertTrue(p5out.mDontAllowCamera);
    }

    private long assertAccountPolicyConsistent(long accountId, long oldKey) {
        Account account = Account.restoreAccountWithId(mMockContext, accountId);
        long policyKey = account.mPolicyKey;

        assertTrue(policyKey > 0);

        // Found a policy. Ensure it matches.
        Policy policy = Policy.restorePolicyWithId(mMockContext, policyKey);
        assertNotNull(policy);
        assertEquals(account.mPolicyKey, policy.mId);
        assertEquals(
                accountId,
                Policy.getAccountIdWithPolicyKey(mMockContext, policy.mId));

        // Assert the old one isn't there.
        if (oldKey > 0) {
            assertNull("old policy not cleaned up",
                    Policy.restorePolicyWithId(mMockContext, oldKey));
        }

        return policyKey;
    }

    @SmallTest
    public void testSettingAccountPolicy() {
        Account account = ProviderTestUtils.setupAccount("testaccount", true, mMockContext);
        long accountId = account.mId;
        Policy initial = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0,
                false, false);
        SecurityPolicy.setAccountPolicy(mMockContext, account, initial, null);

        long oldKey = assertAccountPolicyConsistent(account.mId, 0);

        Policy updated = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0,
                false, false);
        SecurityPolicy.setAccountPolicy(mMockContext, account, updated, null);
        oldKey = assertAccountPolicyConsistent(account.mId, oldKey);

        // Remove the policy
        SecurityPolicy.clearAccountPolicy(
                mMockContext, Account.restoreAccountWithId(mMockContext, accountId));
        assertNull("old policy not cleaned up",
                Policy.restorePolicyWithId(mMockContext, oldKey));
    }

    /**
     * Test equality.  Note, the tests for inequality are poor, as each field should
     * be tested individually.
     */
    @SmallTest
    public void testEquals() {
        Policy p1 =
            setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, false);
        Policy p2 =
            setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, false);
        Policy p3 =
            setupPolicy(2, Policy.PASSWORD_MODE_SIMPLE, 5, 6, true, 7, 8, 9, false, false);
        Policy p4 =
            setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, true);
        assertTrue(p1.equals(p2));
        assertFalse(p2.equals(p3));
        assertFalse(p1.equals(p4));
    }

    /**
     * Test the API to set/clear policy hold flags in an account
     */
    public void testSetClearHoldFlag() {
        Account a2 = ProviderTestUtils.setupAccount("holdflag-2", false, mMockContext);
        a2.mFlags = Account.FLAGS_SYNC_DISABLED | Account.FLAGS_SECURITY_HOLD;
        a2.save(mMockContext);

        // confirm set until cleared
        Account a2a = Account.restoreAccountWithId(mMockContext, a2.mId);
        assertEquals(Account.FLAGS_SYNC_DISABLED | Account.FLAGS_SECURITY_HOLD, a2a.mFlags);

        // set account hold flag off
        SecurityPolicy.setAccountHoldFlag(mMockContext, a2, false);
        assertEquals(Account.FLAGS_SYNC_DISABLED, a2.mFlags);

        // confirm account hold flag set
        Account a2b = Account.restoreAccountWithId(mMockContext, a2.mId);
        assertEquals(Account.FLAGS_SYNC_DISABLED, a2b.mFlags);
    }

    /**
     * Test the response to disabling DeviceAdmin status
     */
    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);
        SecurityPolicy.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);
        SecurityPolicy.setAccountPolicy(mMockContext, a2, p2, "security-sync-key-2");

        Account a3 = ProviderTestUtils.setupAccount("disable-3", true, mMockContext);
        SecurityPolicy.clearAccountPolicy(mMockContext, a3);

        mSecurityPolicy = SecurityPolicy.getInstance(mMockContext);

        // Confirm that "enabling" device admin does not change security status (policy & sync key)
        Policy before = mSecurityPolicy.getAggregatePolicy();
        mSecurityPolicy.onAdminEnabled(true);        // "enabled" should not change anything
        Policy after1 = mSecurityPolicy.getAggregatePolicy();
        assertEquals(before, after1);
        Account a1a = Account.restoreAccountWithId(mMockContext, a1.mId);
        assertNotNull(a1a.mSecuritySyncKey);
        assertTrue(a1a.mPolicyKey > 0);
        Account a2a = Account.restoreAccountWithId(mMockContext, a2.mId);
        assertNotNull(a2a.mSecuritySyncKey);
        assertTrue(a2a.mPolicyKey > 0);
        Account a3a = Account.restoreAccountWithId(mMockContext, a3.mId);
        assertNull(a3a.mSecuritySyncKey);
        assertTrue(a3a.mPolicyKey == 0);

        mSecurityPolicy.deleteSecuredAccounts(mMockContext);
        Policy after2 = mSecurityPolicy.getAggregatePolicy();
        assertEquals(EMPTY_POLICY, after2);
        Account a1b = Account.restoreAccountWithId(mMockContext, a1.mId);
        assertNull(a1b);
        Account a2b = Account.restoreAccountWithId(mMockContext, a2.mId);
        assertNull(a2b);
        Account a3b = Account.restoreAccountWithId(mMockContext, a3.mId);
        assertNull(a3b.mSecuritySyncKey);
    }

    /**
     * Test the scanner that finds expiring accounts
     */
    public void testFindExpiringAccount() {
        ProviderTestUtils.setupAccount("expiring-1", true, mMockContext);

        // With no expiring accounts, this should return null.
        long nextExpiringAccountId = SecurityPolicy.findShortestExpiration(mMockContext);
        assertEquals(-1, nextExpiringAccountId);

        // Add a single expiring account
        Account a2 =
            ProviderTestUtils.setupAccount("expiring-2", true, mMockContext);
        Policy p2 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 30, 0, 0,
                false, true);
        SecurityPolicy.setAccountPolicy(mMockContext, a2, p2, null);

        // The expiring account should be returned
        nextExpiringAccountId = SecurityPolicy.findShortestExpiration(mMockContext);
        assertEquals(a2.mId, nextExpiringAccountId);

        // 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, true);
        SecurityPolicy.setAccountPolicy(mMockContext, a3, p3, null);

        // The original expiring account (a2) should be returned
        nextExpiringAccountId = SecurityPolicy.findShortestExpiration(mMockContext);
        assertEquals(a2.mId, nextExpiringAccountId);

        // 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, true);
        SecurityPolicy.setAccountPolicy(mMockContext, a4, p4, null);

        // The new expiring account (a4) should be returned
        nextExpiringAccountId = SecurityPolicy.findShortestExpiration(mMockContext);
        assertEquals(a4.mId, nextExpiringAccountId);
    }

    /**
     * Test the scanner that wipes expiring accounts
     */
    public void testWipeExpiringAccounts() {
        mSecurityPolicy = SecurityPolicy.getInstance(mMockContext);

        // 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", true, mMockContext);
        Policy p2 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 0, 0, 0,
                false, true);
        SecurityPolicy.setAccountPolicy(mMockContext, a2, p2, null);

        // Add a mailbox & messages to each account
        long account1Id = a1.mId;
        long account2Id = a2.mId;
        Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
        long box1Id = box1.mId;
        ProviderTestUtils.setupMessage("message1", account1Id, box1Id, false, true, mMockContext);
        ProviderTestUtils.setupMessage("message2", account1Id, box1Id, false, true, mMockContext);
        Mailbox box2 = ProviderTestUtils.setupMailbox("box2", account2Id, true, mMockContext);
        long box2Id = box2.mId;
        ProviderTestUtils.setupMessage("message3", account2Id, box2Id, false, true, mMockContext);
        ProviderTestUtils.setupMessage("message4", account2Id, box2Id, false, true, mMockContext);

        // Run the expiration code - should do nothing
        boolean wiped = SecurityPolicy.wipeExpiredAccounts(mMockContext);
        assertFalse(wiped);
        // check mailboxes & messages not wiped
        assertEquals(2, EmailContent.count(mMockContext, Account.CONTENT_URI));
        assertEquals(2, EmailContent.count(mMockContext, Mailbox.CONTENT_URI));
        assertEquals(4, EmailContent.count(mMockContext, Message.CONTENT_URI));

        // 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, true);
        SecurityPolicy.setAccountPolicy(mMockContext, a3, p3, null);

        // Add mailbox & messages to 3rd account
        long account3Id = a3.mId;
        Mailbox box3 = ProviderTestUtils.setupMailbox("box3", account3Id, true, mMockContext);
        long box3Id = box3.mId;
        ProviderTestUtils.setupMessage("message5", account3Id, box3Id, false, true, mMockContext);
        ProviderTestUtils.setupMessage("message6", account3Id, box3Id, false, true, mMockContext);

        // check new counts
        assertEquals(3, EmailContent.count(mMockContext, Account.CONTENT_URI));
        assertEquals(3, EmailContent.count(mMockContext, Mailbox.CONTENT_URI));
        assertEquals(6, EmailContent.count(mMockContext, Message.CONTENT_URI));

        // Run the expiration code - wipe acct #3
        wiped = SecurityPolicy.wipeExpiredAccounts(mMockContext);
        assertTrue(wiped);
        // check new counts - account survives but data is wiped
        assertEquals(3, EmailContent.count(mMockContext, Account.CONTENT_URI));
        assertEquals(2, EmailContent.count(mMockContext, Mailbox.CONTENT_URI));
        assertEquals(4, EmailContent.count(mMockContext, Message.CONTENT_URI));

        // Check security hold states - only #3 should be in hold
        Account account = Account.restoreAccountWithId(mMockContext, account1Id);
        assertEquals(0, account.mFlags & Account.FLAGS_SECURITY_HOLD);
        account = Account.restoreAccountWithId(mMockContext, account2Id);
        assertEquals(0, account.mFlags & Account.FLAGS_SECURITY_HOLD);
        account = Account.restoreAccountWithId(mMockContext, account3Id);
        assertEquals(Account.FLAGS_SECURITY_HOLD, account.mFlags & Account.FLAGS_SECURITY_HOLD);
    }

    /**
     * Test the code that converts from exchange-style quality to DPM/Lockscreen style quality.
     */
    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);
        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);
        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);
        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);
        assertEquals(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX,
                p4.getDPManagerPasswordQuality());
    }

    private boolean policySetEqualsPolicy(PolicySet ps, Policy policy) {
        if ((ps.mPasswordMode >> LegacyPolicySet.PASSWORD_MODE_SHIFT) != policy.mPasswordMode) {
            return false;
        }
        if (ps.mMinPasswordLength != policy.mPasswordMinLength) return false;
        if (ps.mPasswordComplexChars != policy.mPasswordComplexChars) return false;
        if (ps.mPasswordHistory != policy.mPasswordHistory) return false;
        if (ps.mPasswordExpirationDays != policy.mPasswordExpirationDays) return false;
        if (ps.mMaxPasswordFails != policy.mPasswordMaxFails) return false;
        if (ps.mMaxScreenLockTime != policy.mMaxScreenLockTime) return false;
        if (ps.mRequireRemoteWipe != policy.mRequireRemoteWipe) return false;
        if (ps.mRequireEncryption != policy.mRequireEncryption) return false;
        if (ps.mRequireEncryptionExternal != policy.mRequireEncryptionExternal) return false;
        return true;
    }

    public void testPolicyFlagsToPolicy() {
        // Policy flags; the three sets included here correspond to policies for three test
        // accounts that, between them, use all of the possible policies
        long flags = 67096612L;
        PolicySet ps = new PolicySet(flags);
        Policy policy = LegacyPolicySet.flagsToPolicy(flags);
        assertTrue(policySetEqualsPolicy(ps, policy));
        flags = 52776591691846L;
        ps = new PolicySet(flags);
        policy = LegacyPolicySet.flagsToPolicy(flags);
        assertTrue(policySetEqualsPolicy(ps, policy));
        flags = 1689605957029924L;
        ps = new PolicySet(flags);
        policy = LegacyPolicySet.flagsToPolicy(flags);
        assertTrue(policySetEqualsPolicy(ps, policy));
    }

    /**
     * The old PolicySet class fields and constructor; we use this to test conversion to the
     * new Policy table scheme
     */
    private static class PolicySet {
        private final int mMinPasswordLength;
        private final int mPasswordMode;
        private final int mMaxPasswordFails;
        private final int mMaxScreenLockTime;
        private final boolean mRequireRemoteWipe;
        private final int mPasswordExpirationDays;
        private final int mPasswordHistory;
        private final int mPasswordComplexChars;
        private final boolean mRequireEncryption;
        private final boolean mRequireEncryptionExternal;

        /**
         * Create from values encoded in an account flags int
         */
        private PolicySet(long flags) {
            mMinPasswordLength = (int) ((flags & LegacyPolicySet.PASSWORD_LENGTH_MASK)
                    >> LegacyPolicySet.PASSWORD_LENGTH_SHIFT);
            mPasswordMode =
                (int) (flags & LegacyPolicySet.PASSWORD_MODE_MASK);
            mMaxPasswordFails = (int) ((flags & LegacyPolicySet.PASSWORD_MAX_FAILS_MASK)
                    >> LegacyPolicySet.PASSWORD_MAX_FAILS_SHIFT);
            mMaxScreenLockTime = (int) ((flags & LegacyPolicySet.SCREEN_LOCK_TIME_MASK)
                    >> LegacyPolicySet.SCREEN_LOCK_TIME_SHIFT);
            mRequireRemoteWipe = 0 != (flags & LegacyPolicySet.REQUIRE_REMOTE_WIPE);
            mPasswordExpirationDays = (int) ((flags & LegacyPolicySet.PASSWORD_EXPIRATION_MASK)
                    >> LegacyPolicySet.PASSWORD_EXPIRATION_SHIFT);
            mPasswordHistory = (int) ((flags & LegacyPolicySet.PASSWORD_HISTORY_MASK)
                    >> LegacyPolicySet.PASSWORD_HISTORY_SHIFT);
            mPasswordComplexChars = (int) ((flags & LegacyPolicySet.PASSWORD_COMPLEX_CHARS_MASK)
                    >> LegacyPolicySet.PASSWORD_COMPLEX_CHARS_SHIFT);
            mRequireEncryption = 0 != (flags & LegacyPolicySet.REQUIRE_ENCRYPTION);
            mRequireEncryptionExternal = 0 != (flags & LegacyPolicySet.REQUIRE_ENCRYPTION_EXTERNAL);
        }
    }
}