SecurityPolicy.java revision a2269e84c6134bfd3506e5489c7ccfd60c32d41f
1345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler/* 2345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Copyright (C) 2010 The Android Open Source Project 3345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * 4345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Licensed under the Apache License, Version 2.0 (the "License"); 5345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * you may not use this file except in compliance with the License. 6345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * You may obtain a copy of the License at 7345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * 8345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * http://www.apache.org/licenses/LICENSE-2.0 9345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * 10345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Unless required by applicable law or agreed to in writing, software 11345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * distributed under the License is distributed on an "AS IS" BASIS, 12345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * See the License for the specific language governing permissions and 14345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * limitations under the License. 15345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 16345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 17345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadlerpackage com.android.email; 18345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 193d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadlerimport com.android.email.activity.setup.AccountSecurity; 203d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadlerimport com.android.email.provider.EmailContent; 21345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadlerimport com.android.email.provider.EmailContent.Account; 223d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadlerimport com.android.email.provider.EmailContent.AccountColumns; 23a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadlerimport com.android.email.service.EmailBroadcastProcessorService; 24345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 25e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadlerimport android.app.admin.DeviceAdminInfo; 266d0016229adc13fefe68820fe4d6e46f530952baDianne Hackbornimport android.app.admin.DeviceAdminReceiver; 276d0016229adc13fefe68820fe4d6e46f530952baDianne Hackbornimport android.app.admin.DevicePolicyManager; 28d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadlerimport android.content.ComponentName; 2902d59d21949a77c60859b615312f02e6d8003490Marc Blankimport android.content.ContentResolver; 303d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadlerimport android.content.ContentValues; 31345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadlerimport android.content.Context; 32345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadlerimport android.content.Intent; 33345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadlerimport android.database.Cursor; 34d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blankimport android.os.Parcel; 35d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blankimport android.os.Parcelable; 3650d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadlerimport android.util.Log; 37345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 38345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler/** 39d71d0b223a5cd02e2a8f1ec5c3f8cebab170d65fAndrew Stadler * Utility functions to support reading and writing security policies, and handshaking the device 40d71d0b223a5cd02e2a8f1ec5c3f8cebab170d65fAndrew Stadler * into and out of various security states. 41345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 42345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadlerpublic class SecurityPolicy { 4302d59d21949a77c60859b615312f02e6d8003490Marc Blank private static final String TAG = "SecurityPolicy"; 44345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static SecurityPolicy sInstance = null; 45345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private Context mContext; 46d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler private DevicePolicyManager mDPM; 47d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler private ComponentName mAdminName; 48d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler private PolicySet mAggregatePolicy; 49d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler 50af55e3e436991fde91cdc80efe2786eb8f509d15Andrew Stadler /* package */ static final PolicySet NO_POLICY_SET = 519b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false, 0, 0, 0); 52345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 53345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 54968be441b4c253668c4ee1c7a3f8e4b0eb12cf24Makoto Onuki * This projection on Account is for scanning/reading 55345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 56345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final String[] ACCOUNT_SECURITY_PROJECTION = new String[] { 572a5eeea9213005060256054ec773e72406415ce4Andrew Stadler AccountColumns.ID, AccountColumns.SECURITY_FLAGS 58345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler }; 591ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler private static final int ACCOUNT_SECURITY_COLUMN_ID = 0; 60345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final int ACCOUNT_SECURITY_COLUMN_FLAGS = 1; 612a5eeea9213005060256054ec773e72406415ce4Andrew Stadler 62a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler // Messages used for DevicePolicyManager callbacks 63a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler private static final int DEVICE_ADMIN_MESSAGE_ENABLED = 1; 64a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler private static final int DEVICE_ADMIN_MESSAGE_DISABLED = 2; 65a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler private static final int DEVICE_ADMIN_MESSAGE_PASSWORD_CHANGED = 3; 66a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler private static final int DEVICE_ADMIN_MESSAGE_PASSWORD_EXPIRING = 4; 67a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler 68345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 69345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Get the security policy instance 70345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 71345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public synchronized static SecurityPolicy getInstance(Context context) { 72345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler if (sInstance == null) { 73a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler sInstance = new SecurityPolicy(context.getApplicationContext()); 74345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 75345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler return sInstance; 76345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 77345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 78345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 79345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Private constructor (one time only) 80345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 81345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private SecurityPolicy(Context context) { 82968be441b4c253668c4ee1c7a3f8e4b0eb12cf24Makoto Onuki mContext = context.getApplicationContext(); 83d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler mDPM = null; 84d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler mAdminName = new ComponentName(context, PolicyAdmin.class); 85d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler mAggregatePolicy = null; 86345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 87345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 88345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 89345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * For testing only: Inject context into already-created instance 90345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 91345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /* package */ void setContext(Context context) { 92345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mContext = context; 93345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 94345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 95345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 96345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Compute the aggregate policy for all accounts that require it, and record it. 97345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * 98345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * The business logic is as follows: 99345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * min password length take the max 100345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * password mode take the max (strongest mode) 101345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * max password fails take the min 102345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * max screen lock time take the min 103345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * require remote wipe take the max (logical or) 1049b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank * password history take the max (strongest mode) 1051ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * password expiration take the min (strongest mode) 1069b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank * password complex chars take the max (strongest mode) 107968be441b4c253668c4ee1c7a3f8e4b0eb12cf24Makoto Onuki * 108d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * @return a policy representing the strongest aggregate. If no policy sets are defined, 109d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * a lightweight "nothing required" policy will be returned. Never null. 110345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 1119b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank /*package*/ PolicySet computeAggregatePolicy() { 112345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler boolean policiesFound = false; 113345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 114345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler int minPasswordLength = Integer.MIN_VALUE; 115345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler int passwordMode = Integer.MIN_VALUE; 116345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler int maxPasswordFails = Integer.MAX_VALUE; 117345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler int maxScreenLockTime = Integer.MAX_VALUE; 118345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler boolean requireRemoteWipe = false; 1199b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank int passwordHistory = Integer.MIN_VALUE; 1201ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler int passwordExpirationDays = Integer.MAX_VALUE; 1219b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank int passwordComplexChars = Integer.MIN_VALUE; 122345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 123345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler Cursor c = mContext.getContentResolver().query(Account.CONTENT_URI, 124bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki ACCOUNT_SECURITY_PROJECTION, Account.SECURITY_NONZERO_SELECTION, null, null); 125345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler try { 126345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler while (c.moveToNext()) { 1279b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank long flags = c.getLong(ACCOUNT_SECURITY_COLUMN_FLAGS); 128345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler if (flags != 0) { 129345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler PolicySet p = new PolicySet(flags); 1303d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler minPasswordLength = Math.max(p.mMinPasswordLength, minPasswordLength); 1313d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler passwordMode = Math.max(p.mPasswordMode, passwordMode); 132345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler if (p.mMaxPasswordFails > 0) { 133345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler maxPasswordFails = Math.min(p.mMaxPasswordFails, maxPasswordFails); 134345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 135345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler if (p.mMaxScreenLockTime > 0) { 136345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler maxScreenLockTime = Math.min(p.mMaxScreenLockTime, maxScreenLockTime); 137345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 1389b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank if (p.mPasswordHistory > 0) { 1399b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank passwordHistory = Math.max(p.mPasswordHistory, passwordHistory); 1409b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 1411ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (p.mPasswordExpirationDays > 0) { 1421ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler passwordExpirationDays = 1431ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Math.min(p.mPasswordExpirationDays, passwordExpirationDays); 1449b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 1459b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank if (p.mPasswordComplexChars > 0) { 1469b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank passwordComplexChars = Math.max(p.mPasswordComplexChars, 1479b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank passwordComplexChars); 1489b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 149345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler requireRemoteWipe |= p.mRequireRemoteWipe; 150345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler policiesFound = true; 151345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 152345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 153345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } finally { 154345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler c.close(); 155345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 156345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler if (policiesFound) { 1573d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler // final cleanup pass converts any untouched min/max values to zero (not specified) 1583d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (minPasswordLength == Integer.MIN_VALUE) minPasswordLength = 0; 1593d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (passwordMode == Integer.MIN_VALUE) passwordMode = 0; 1603d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (maxPasswordFails == Integer.MAX_VALUE) maxPasswordFails = 0; 1613d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (maxScreenLockTime == Integer.MAX_VALUE) maxScreenLockTime = 0; 1629b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank if (passwordHistory == Integer.MIN_VALUE) passwordHistory = 0; 1631ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (passwordExpirationDays == Integer.MAX_VALUE) passwordExpirationDays = 0; 1649b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank if (passwordComplexChars == Integer.MIN_VALUE) passwordComplexChars = 0; 1653d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 166345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler return new PolicySet(minPasswordLength, passwordMode, maxPasswordFails, 1671ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler maxScreenLockTime, requireRemoteWipe, passwordExpirationDays, passwordHistory, 1689b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank passwordComplexChars); 169345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } else { 170d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler return NO_POLICY_SET; 171345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 172345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 173345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 174345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 17550d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * Return updated aggregate policy, from cached value if possible 17650d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler */ 17750d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler public synchronized PolicySet getAggregatePolicy() { 17850d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler if (mAggregatePolicy == null) { 17950d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler mAggregatePolicy = computeAggregatePolicy(); 18050d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } 18150d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler return mAggregatePolicy; 18250d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } 18350d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler 18450d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler /** 185d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * Get the dpm. This mainly allows us to make some utility calls without it, for testing. 186d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler */ 187d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler private synchronized DevicePolicyManager getDPM() { 188d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler if (mDPM == null) { 189d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler mDPM = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 190d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 191d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler return mDPM; 192d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 193d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler 194d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler /** 195d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * API: Report that policies may have been updated due to rewriting values in an Account. 19650d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * @param accountId the account that has been updated, -1 if unknown/deleted 197d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler */ 1983d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler public synchronized void updatePolicies(long accountId) { 199d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler mAggregatePolicy = null; 200d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 201d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler 202d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler /** 20350d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * API: Report that policies may have been updated *and* the caller vouches that the 20450d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * change is a reduction in policies. This forces an immediate change to device state. 20550d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * Typically used when deleting accounts, although we may use it for server-side policy 20650d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * rollbacks. 20750d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler */ 20850d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler public void reducePolicies() { 20950d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler updatePolicies(-1); 21050d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler setActivePolicies(); 21150d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } 21250d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler 21350d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler /** 214d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * API: Query used to determine if a given policy is "active" (the device is operating at 2153d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * the required security level). 216345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * 2173d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * This can be used when syncing a specific account, by passing a specific set of policies 2183d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * for that account. Or, it can be used at any time to compare the device 2193d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * state against the aggregate set of device policies stored in all accounts. 2203d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * 2213d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * This method is for queries only, and does not trigger any change in device state. 2223d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * 2231ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * NOTE: If there are multiple accounts with password expiration policies, the device 2241ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * password will be set to expire in the shortest required interval (most secure). This method 2251ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * will return 'false' as soon as the password expires - irrespective of which account caused 2261ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * the expiration. In other words, all accounts (that require expiration) will run/stop 2271ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * based on the requirements of the account with the shortest interval. 2281ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * 2293d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * @param policies the policies requested, or null to check aggregate stored policies 230345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * @return true if the policies are active, false if not active 231345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 232345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public boolean isActive(PolicySet policies) { 23350d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler // select aggregate set if needed 23450d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler if (policies == null) { 23550d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler policies = getAggregatePolicy(); 23650d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } 23750d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler // quick check for the "empty set" of no policies 23850d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler if (policies == NO_POLICY_SET) { 23950d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler return true; 24050d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } 241d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler DevicePolicyManager dpm = getDPM(); 242e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler if (isActiveAdmin()) { 243d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler // check each policy explicitly 2443d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (policies.mMinPasswordLength > 0) { 2453d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (dpm.getPasswordMinimumLength(mAdminName) < policies.mMinPasswordLength) { 246d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler return false; 247d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 248d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 2493d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (policies.mPasswordMode > 0) { 2503d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (dpm.getPasswordQuality(mAdminName) < policies.getDPManagerPasswordQuality()) { 251d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler return false; 252d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 253d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler if (!dpm.isActivePasswordSufficient()) { 254d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler return false; 255d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 256d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 2573d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (policies.mMaxScreenLockTime > 0) { 258d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler // Note, we use seconds, dpm uses milliseconds 2593d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (dpm.getMaximumTimeToLock(mAdminName) > policies.mMaxScreenLockTime * 1000) { 260d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler return false; 261d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 262d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 2631ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (policies.mPasswordExpirationDays > 0) { 2641ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // confirm that expirations are currently set 2651ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long currentTimeout = dpm.getPasswordExpirationTimeout(mAdminName); 2661ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (currentTimeout == 0 2671ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler || currentTimeout > policies.getDPManagerPasswordExpirationTimeout()) { 2681ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler return false; 2691ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 2701ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // confirm that the current password hasn't expired 2711ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long expirationDate = dpm.getPasswordExpiration(mAdminName); 2721ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long timeUntilExpiration = expirationDate - System.currentTimeMillis(); 2731ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler boolean expired = timeUntilExpiration < 0; 2741ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (expired) { 2751ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler return false; 2761ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 2779b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 2789b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank if (policies.mPasswordHistory > 0) { 2799b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank if (dpm.getPasswordHistoryLength(mAdminName) < policies.mPasswordHistory) { 2809b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank return false; 2819b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 2829b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 2839b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank if (policies.mPasswordComplexChars > 0) { 2849b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank if (dpm.getPasswordMinimumNonLetter(mAdminName) < policies.mPasswordComplexChars) { 2859b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank return false; 2869b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 2879b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 288d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler // password failures are counted locally - no test required here 289d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler // no check required for remote wipe (it's supported, if we're the admin) 2902a5eeea9213005060256054ec773e72406415ce4Andrew Stadler 2912a5eeea9213005060256054ec773e72406415ce4Andrew Stadler // making it this far means we passed! 2922a5eeea9213005060256054ec773e72406415ce4Andrew Stadler return true; 293d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 294d71d0b223a5cd02e2a8f1ec5c3f8cebab170d65fAndrew Stadler // return false, not active 295d71d0b223a5cd02e2a8f1ec5c3f8cebab170d65fAndrew Stadler return false; 296345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 297345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 298345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 29950d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * Set the requested security level based on the aggregate set of requests. 30050d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * If the set is empty, we release our device administration. If the set is non-empty, 30150d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * we only proceed if we are already active as an admin. 3023d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 3033d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler public void setActivePolicies() { 3043d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler DevicePolicyManager dpm = getDPM(); 30550d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler // compute aggregate set of policies 30650d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler PolicySet policies = getAggregatePolicy(); 30750d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler // if empty set, detach from policy manager 30850d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler if (policies == NO_POLICY_SET) { 30950d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler dpm.removeActiveAdmin(mAdminName); 310e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler } else if (isActiveAdmin()) { 3113d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler // set each policy in the policy manager 3123d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler // password mode & length 3133d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler dpm.setPasswordQuality(mAdminName, policies.getDPManagerPasswordQuality()); 3143d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler dpm.setPasswordMinimumLength(mAdminName, policies.mMinPasswordLength); 3153d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler // screen lock time 3163d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler dpm.setMaximumTimeToLock(mAdminName, policies.mMaxScreenLockTime * 1000); 3173d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler // local wipe (failed passwords limit) 3183d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler dpm.setMaximumFailedPasswordsForWipe(mAdminName, policies.mMaxPasswordFails); 3191ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // password expiration (days until a password expires). API takes mSec. 3201ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler dpm.setPasswordExpirationTimeout(mAdminName, 3211ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler policies.getDPManagerPasswordExpirationTimeout()); 3229b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank // password history length (number of previous passwords that may not be reused) 3239b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank dpm.setPasswordHistoryLength(mAdminName, policies.mPasswordHistory); 3249b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank // password minimum complex characters 3259b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank dpm.setPasswordMinimumNonLetter(mAdminName, policies.mPasswordComplexChars); 3263d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 3273d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 3283d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 3293d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 3302a5eeea9213005060256054ec773e72406415ce4Andrew Stadler * API: Set/Clear the "hold" flag in any account. This flag serves a dual purpose: 3312a5eeea9213005060256054ec773e72406415ce4Andrew Stadler * Setting it gives us an indication that it was blocked, and clearing it gives EAS a 3322a5eeea9213005060256054ec773e72406415ce4Andrew Stadler * signal to try syncing again. 3331ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param context 3341ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param account The account to update 3351ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param newState true = security hold, false = free to sync 3362a5eeea9213005060256054ec773e72406415ce4Andrew Stadler */ 3371ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler public static void setAccountHoldFlag(Context context, Account account, boolean newState) { 3382a5eeea9213005060256054ec773e72406415ce4Andrew Stadler if (newState) { 3392a5eeea9213005060256054ec773e72406415ce4Andrew Stadler account.mFlags |= Account.FLAGS_SECURITY_HOLD; 3402a5eeea9213005060256054ec773e72406415ce4Andrew Stadler } else { 3412a5eeea9213005060256054ec773e72406415ce4Andrew Stadler account.mFlags &= ~Account.FLAGS_SECURITY_HOLD; 3422a5eeea9213005060256054ec773e72406415ce4Andrew Stadler } 3432a5eeea9213005060256054ec773e72406415ce4Andrew Stadler ContentValues cv = new ContentValues(); 3442a5eeea9213005060256054ec773e72406415ce4Andrew Stadler cv.put(AccountColumns.FLAGS, account.mFlags); 3451ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler account.update(context, cv); 3462a5eeea9213005060256054ec773e72406415ce4Andrew Stadler } 3472a5eeea9213005060256054ec773e72406415ce4Andrew Stadler 3482a5eeea9213005060256054ec773e72406415ce4Andrew Stadler /** 3493d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * API: Sync service should call this any time a sync fails due to isActive() returning false. 350d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * This will kick off the notify-acquire-admin-state process and/or increase the security level. 351345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * The caller needs to write the required policies into this account before making this call. 3523d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Should not be called from UI thread - uses DB lookups to prepare new notifications 353345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * 354345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * @param accountId the account for which sync cannot proceed 355345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 356345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public void policiesRequired(long accountId) { 3572a5eeea9213005060256054ec773e72406415ce4Andrew Stadler Account account = EmailContent.Account.restoreAccountWithId(mContext, accountId); 3581ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 3592a5eeea9213005060256054ec773e72406415ce4Andrew Stadler // Mark the account as "on hold". 3601ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler setAccountHoldFlag(mContext, account, true); 3611ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 3621ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // Put up a notification 3633d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler String tickerText = mContext.getString(R.string.security_notification_ticker_fmt, 3643d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler account.getDisplayName()); 3653d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler String contentTitle = mContext.getString(R.string.security_notification_content_title); 3663d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler String contentText = account.getDisplayName(); 3673d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler Intent intent = AccountSecurity.actionUpdateSecurityIntent(mContext, accountId); 3681ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler NotificationController.getInstance(mContext).postAccountNotification( 3691ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler account, tickerText, contentTitle, contentText, intent, 3701ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler NotificationController.NOTIFICATION_ID_SECURITY_NEEDED); 3713d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 3723d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 3733d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 3743d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Called from the notification's intent receiver to register that the notification can be 3753d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * cleared now. 3763d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 377a87f8d8bbc0b704974c4230f518cf23b7215fc55Andrew Stadler public void clearNotification(long accountId) { 3781ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler NotificationController.getInstance(mContext).cancelNotification( 3791ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler NotificationController.NOTIFICATION_ID_SECURITY_NEEDED); 3803d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 3813d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 3823d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 38350d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * API: Remote wipe (from server). This is final, there is no confirmation. It will only 38450d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * return to the caller if there is an unexpected failure. 3853d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 38650d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler public void remoteWipe() { 3873d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler DevicePolicyManager dpm = getDPM(); 3883d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (dpm.isAdminActive(mAdminName)) { 3893d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler dpm.wipeData(0); 39050d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } else { 39150d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler Log.d(Email.LOG_TAG, "Could not remote wipe because not device admin."); 3923d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 393345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 394345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 395345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 396345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Class for tracking policies and reading/writing into accounts 397345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 398d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank public static class PolicySet implements Parcelable { 399345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 400345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler // Security (provisioning) flags 401345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler // bits 0..4: password length (0=no password required) 402345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final int PASSWORD_LENGTH_MASK = 31; 403345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final int PASSWORD_LENGTH_SHIFT = 0; 404a843d40ba1d3eb77e76b4a28aa911588f0fd81a1Andrew Stadler public static final int PASSWORD_LENGTH_MAX = 30; 405345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler // bits 5..8: password mode 406345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final int PASSWORD_MODE_SHIFT = 5; 407345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final int PASSWORD_MODE_MASK = 15 << PASSWORD_MODE_SHIFT; 408345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public static final int PASSWORD_MODE_NONE = 0 << PASSWORD_MODE_SHIFT; 409345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public static final int PASSWORD_MODE_SIMPLE = 1 << PASSWORD_MODE_SHIFT; 410345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public static final int PASSWORD_MODE_STRONG = 2 << PASSWORD_MODE_SHIFT; 411345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler // bits 9..13: password failures -> wipe device (0=disabled) 412345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final int PASSWORD_MAX_FAILS_SHIFT = 9; 413345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final int PASSWORD_MAX_FAILS_MASK = 31 << PASSWORD_MAX_FAILS_SHIFT; 414345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public static final int PASSWORD_MAX_FAILS_MAX = 31; 415345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler // bits 14..24: seconds to screen lock (0=not required) 416345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final int SCREEN_LOCK_TIME_SHIFT = 14; 417345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final int SCREEN_LOCK_TIME_MASK = 2047 << SCREEN_LOCK_TIME_SHIFT; 418345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public static final int SCREEN_LOCK_TIME_MAX = 2047; 419345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler // bit 25: remote wipe capability required 420345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static final int REQUIRE_REMOTE_WIPE = 1 << 25; 4219b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank // bit 26..35: password expiration (days; 0=not required) 4229b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank private static final int PASSWORD_EXPIRATION_SHIFT = 26; 4239b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank private static final long PASSWORD_EXPIRATION_MASK = 1023L << PASSWORD_EXPIRATION_SHIFT; 4249b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank public static final int PASSWORD_EXPIRATION_MAX = 1023; 4259b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank // bit 35..42: password history (length; 0=not required) 4269b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank private static final int PASSWORD_HISTORY_SHIFT = 36; 4279b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank private static final long PASSWORD_HISTORY_MASK = 255L << PASSWORD_HISTORY_SHIFT; 4289b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank public static final int PASSWORD_HISTORY_MAX = 255; 4299b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank // bit 42..46: min complex characters (0=not required) 4309b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank private static final int PASSWORD_COMPLEX_CHARS_SHIFT = 44; 4319b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank private static final long PASSWORD_COMPLEX_CHARS_MASK = 31L << PASSWORD_COMPLEX_CHARS_SHIFT; 4329b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank public static final int PASSWORD_COMPLEX_CHARS_MAX = 31; 433345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 4341ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /* Convert days to mSec (used for password expiration) */ 4351ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler private static final long DAYS_TO_MSEC = 24 * 60 * 60 * 1000; 4361ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /* Small offset (2 minutes) added to policy expiration to make user testing easier. */ 4371ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler private static final long EXPIRATION_OFFSET_MSEC = 2 * 60 * 1000; 4381ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 4391d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank /*package*/ final int mMinPasswordLength; 4401d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank /*package*/ final int mPasswordMode; 4411d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank /*package*/ final int mMaxPasswordFails; 4421d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank /*package*/ final int mMaxScreenLockTime; 4431d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank /*package*/ final boolean mRequireRemoteWipe; 4441ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /*package*/ final int mPasswordExpirationDays; 4459b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank /*package*/ final int mPasswordHistory; 4469b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank /*package*/ final int mPasswordComplexChars; 4471d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank 4486278dcdeafadc55fe1a57eec42a0807874377f62Andy Stadler public int getMinPasswordLengthForTest() { 4491d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank return mMinPasswordLength; 4501d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank } 4511d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank 4526278dcdeafadc55fe1a57eec42a0807874377f62Andy Stadler public int getPasswordModeForTest() { 4531d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank return mPasswordMode; 4541d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank } 4551d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank 4566278dcdeafadc55fe1a57eec42a0807874377f62Andy Stadler public int getMaxPasswordFailsForTest() { 4571d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank return mMaxPasswordFails; 4581d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank } 4591d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank 4606278dcdeafadc55fe1a57eec42a0807874377f62Andy Stadler public int getMaxScreenLockTimeForTest() { 4611d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank return mMaxScreenLockTime; 4621d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank } 4631d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank 4646278dcdeafadc55fe1a57eec42a0807874377f62Andy Stadler public boolean isRequireRemoteWipeForTest() { 4651d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank return mRequireRemoteWipe; 4661d6dab29562eca7978f179be5f5c75f22f44d734Marc Blank } 467345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 468345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 469345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Create from raw values. 470345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * @param minPasswordLength (0=not enforced) 471345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * @param passwordMode 472345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * @param maxPasswordFails (0=not enforced) 473d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * @param maxScreenLockTime in seconds (0=not enforced) 474345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * @param requireRemoteWipe 4751ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param passwordExpirationDays in days (0=not enforced) 4761ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param passwordHistory (0=not enforced) 4771ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param passwordComplexChars (0=not enforced) 478a843d40ba1d3eb77e76b4a28aa911588f0fd81a1Andrew Stadler * @throws IllegalArgumentException for illegal arguments. 479345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 480345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public PolicySet(int minPasswordLength, int passwordMode, int maxPasswordFails, 4811ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler int maxScreenLockTime, boolean requireRemoteWipe, int passwordExpirationDays, 4829b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank int passwordHistory, int passwordComplexChars) throws IllegalArgumentException { 483c263810b08943541135a24e2b7520692152455ccMarc Blank // If we're not enforcing passwords, make sure we clean up related values, since EAS 484c263810b08943541135a24e2b7520692152455ccMarc Blank // can send non-zero values for any or all of these 485c263810b08943541135a24e2b7520692152455ccMarc Blank if (passwordMode == PASSWORD_MODE_NONE) { 486c263810b08943541135a24e2b7520692152455ccMarc Blank maxPasswordFails = 0; 487c263810b08943541135a24e2b7520692152455ccMarc Blank maxScreenLockTime = 0; 488c263810b08943541135a24e2b7520692152455ccMarc Blank minPasswordLength = 0; 489c263810b08943541135a24e2b7520692152455ccMarc Blank passwordComplexChars = 0; 490c263810b08943541135a24e2b7520692152455ccMarc Blank passwordHistory = 0; 4911ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler passwordExpirationDays = 0; 492c263810b08943541135a24e2b7520692152455ccMarc Blank } else { 493c263810b08943541135a24e2b7520692152455ccMarc Blank if ((passwordMode != PASSWORD_MODE_SIMPLE) && 494c263810b08943541135a24e2b7520692152455ccMarc Blank (passwordMode != PASSWORD_MODE_STRONG)) { 495c263810b08943541135a24e2b7520692152455ccMarc Blank throw new IllegalArgumentException("password mode"); 496c263810b08943541135a24e2b7520692152455ccMarc Blank } 49761911d4ff70132fa21c5ee7a987303479e8ef6aeMarc Blank // If we're only requiring a simple password, set complex chars to zero; note 49861911d4ff70132fa21c5ee7a987303479e8ef6aeMarc Blank // that EAS can erroneously send non-zero values in this case 49961911d4ff70132fa21c5ee7a987303479e8ef6aeMarc Blank if (passwordMode == PASSWORD_MODE_SIMPLE) { 50061911d4ff70132fa21c5ee7a987303479e8ef6aeMarc Blank passwordComplexChars = 0; 50161911d4ff70132fa21c5ee7a987303479e8ef6aeMarc Blank } 502c263810b08943541135a24e2b7520692152455ccMarc Blank // The next four values have hard limits which cannot be supported if exceeded. 503c263810b08943541135a24e2b7520692152455ccMarc Blank if (minPasswordLength > PASSWORD_LENGTH_MAX) { 504c263810b08943541135a24e2b7520692152455ccMarc Blank throw new IllegalArgumentException("password length"); 505c263810b08943541135a24e2b7520692152455ccMarc Blank } 5061ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (passwordExpirationDays > PASSWORD_EXPIRATION_MAX) { 507c263810b08943541135a24e2b7520692152455ccMarc Blank throw new IllegalArgumentException("password expiration"); 508c263810b08943541135a24e2b7520692152455ccMarc Blank } 509c263810b08943541135a24e2b7520692152455ccMarc Blank if (passwordHistory > PASSWORD_HISTORY_MAX) { 510c263810b08943541135a24e2b7520692152455ccMarc Blank throw new IllegalArgumentException("password history"); 511c263810b08943541135a24e2b7520692152455ccMarc Blank } 512c263810b08943541135a24e2b7520692152455ccMarc Blank if (passwordComplexChars > PASSWORD_COMPLEX_CHARS_MAX) { 513c263810b08943541135a24e2b7520692152455ccMarc Blank throw new IllegalArgumentException("complex chars"); 514c263810b08943541135a24e2b7520692152455ccMarc Blank } 515c263810b08943541135a24e2b7520692152455ccMarc Blank // This value can be reduced (which actually increases security) if necessary 516c263810b08943541135a24e2b7520692152455ccMarc Blank if (maxPasswordFails > PASSWORD_MAX_FAILS_MAX) { 517c263810b08943541135a24e2b7520692152455ccMarc Blank maxPasswordFails = PASSWORD_MAX_FAILS_MAX; 518c263810b08943541135a24e2b7520692152455ccMarc Blank } 519c263810b08943541135a24e2b7520692152455ccMarc Blank // This value can be reduced (which actually increases security) if necessary 520c263810b08943541135a24e2b7520692152455ccMarc Blank if (maxScreenLockTime > SCREEN_LOCK_TIME_MAX) { 521c263810b08943541135a24e2b7520692152455ccMarc Blank maxScreenLockTime = SCREEN_LOCK_TIME_MAX; 522c263810b08943541135a24e2b7520692152455ccMarc Blank } 523345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 524345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mMinPasswordLength = minPasswordLength; 525345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mPasswordMode = passwordMode; 526345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mMaxPasswordFails = maxPasswordFails; 527345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mMaxScreenLockTime = maxScreenLockTime; 528345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mRequireRemoteWipe = requireRemoteWipe; 5291ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler mPasswordExpirationDays = passwordExpirationDays; 5309b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank mPasswordHistory = passwordHistory; 5319b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank mPasswordComplexChars = passwordComplexChars; 532345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 533345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 534345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 535345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Create from values encoded in an account 536345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * @param account 537345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 538345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public PolicySet(Account account) { 539345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler this(account.mSecurityFlags); 540345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 541345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 542345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 543345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Create from values encoded in an account flags int 544345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 5459b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank public PolicySet(long flags) { 546345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mMinPasswordLength = 5479b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank (int) ((flags & PASSWORD_LENGTH_MASK) >> PASSWORD_LENGTH_SHIFT); 548345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mPasswordMode = 5499b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank (int) (flags & PASSWORD_MODE_MASK); 550345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mMaxPasswordFails = 5519b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank (int) ((flags & PASSWORD_MAX_FAILS_MASK) >> PASSWORD_MAX_FAILS_SHIFT); 552345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mMaxScreenLockTime = 5539b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank (int) ((flags & SCREEN_LOCK_TIME_MASK) >> SCREEN_LOCK_TIME_SHIFT); 554345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mRequireRemoteWipe = 0 != (flags & REQUIRE_REMOTE_WIPE); 5551ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler mPasswordExpirationDays = 5569b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank (int) ((flags & PASSWORD_EXPIRATION_MASK) >> PASSWORD_EXPIRATION_SHIFT); 5579b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank mPasswordHistory = 5589b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank (int) ((flags & PASSWORD_HISTORY_MASK) >> PASSWORD_HISTORY_SHIFT); 5599b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank mPasswordComplexChars = 5609b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank (int) ((flags & PASSWORD_COMPLEX_CHARS_MASK) >> PASSWORD_COMPLEX_CHARS_SHIFT); 561345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 562345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 563345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 5643d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Helper to map our internal encoding to DevicePolicyManager password modes. 565d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler */ 5663d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler public int getDPManagerPasswordQuality() { 567d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler switch (mPasswordMode) { 568d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler case PASSWORD_MODE_SIMPLE: 569d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 570d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler case PASSWORD_MODE_STRONG: 571d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; 572d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler default: 573d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler return DevicePolicyManager .PASSWORD_QUALITY_UNSPECIFIED; 574d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 575d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 576d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler 577d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler /** 5781ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * Helper to map expiration times to the millisecond values used by DevicePolicyManager. 5791ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 5801ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler public long getDPManagerPasswordExpirationTimeout() { 5811ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long result = mPasswordExpirationDays * DAYS_TO_MSEC; 5821ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // Add a small offset to the password expiration. This makes it easier to test 5831ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // by changing (for example) 1 day to 1 day + 5 minutes. If you set an expiration 5841ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // that is within the warning period, you should get a warning fairly quickly. 5851ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (result > 0) { 5861ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler result += EXPIRATION_OFFSET_MSEC; 5871ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 5881ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler return result; 5891ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 5901ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 5911ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 592345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Record flags (and a sync key for the flags) into an Account 593345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Note: the hash code is defined as the encoding used in Account 5943d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * 595345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * @param account to write the values mSecurityFlags and mSecuritySyncKey 596345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * @param syncKey the value to write into the account's mSecuritySyncKey 5973d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * @param update if true, also writes the account back to the provider (updating only 5983d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * the fields changed by this API) 5993d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * @param context a context for writing to the provider 6003d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * @return true if the actual policies changed, false if no change (note, sync key 6013d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * does not affect this) 602345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 6033d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler public boolean writeAccount(Account account, String syncKey, boolean update, 6043d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler Context context) { 6059b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank long newFlags = getSecurityCode(); 6063d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler boolean dirty = (newFlags != account.mSecurityFlags); 6073d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler account.mSecurityFlags = newFlags; 6083d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler account.mSecuritySyncKey = syncKey; 6093d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (update) { 6103d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (account.isSaved()) { 6113d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler ContentValues cv = new ContentValues(); 6123d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler cv.put(AccountColumns.SECURITY_FLAGS, account.mSecurityFlags); 6133d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler cv.put(AccountColumns.SECURITY_SYNC_KEY, account.mSecuritySyncKey); 6143d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler account.update(context, cv); 6153d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } else { 6163d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler account.save(context); 6173d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 6183d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 6193d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler return dirty; 620345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 621345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 622345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler @Override 623345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public boolean equals(Object o) { 624345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler if (o instanceof PolicySet) { 625345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler PolicySet other = (PolicySet)o; 6269b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank return (this.getSecurityCode() == other.getSecurityCode()); 627345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 628345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler return false; 629345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 630345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 631d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank /** 632d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank * Supports Parcelable 633d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank */ 634d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank public int describeContents() { 635d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank return 0; 636d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank } 637d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank 638d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank /** 639d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank * Supports Parcelable 640d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank */ 641d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank public static final Parcelable.Creator<PolicySet> CREATOR 642d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank = new Parcelable.Creator<PolicySet>() { 643d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank public PolicySet createFromParcel(Parcel in) { 644d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank return new PolicySet(in); 645d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank } 646d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank 647d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank public PolicySet[] newArray(int size) { 648d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank return new PolicySet[size]; 649d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank } 650d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank }; 651d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank 652d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank /** 653d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank * Supports Parcelable 654d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank */ 655d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank public void writeToParcel(Parcel dest, int flags) { 656d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank dest.writeInt(mMinPasswordLength); 657d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank dest.writeInt(mPasswordMode); 658d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank dest.writeInt(mMaxPasswordFails); 659d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank dest.writeInt(mMaxScreenLockTime); 660d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank dest.writeInt(mRequireRemoteWipe ? 1 : 0); 6611ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler dest.writeInt(mPasswordExpirationDays); 662d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank dest.writeInt(mPasswordHistory); 663d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank dest.writeInt(mPasswordComplexChars); 664d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank } 665d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank 666d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank /** 667d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank * Supports Parcelable 668d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank */ 669d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank public PolicySet(Parcel in) { 670d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank mMinPasswordLength = in.readInt(); 671d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank mPasswordMode = in.readInt(); 672d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank mMaxPasswordFails = in.readInt(); 673d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank mMaxScreenLockTime = in.readInt(); 674d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank mRequireRemoteWipe = in.readInt() == 1; 6751ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler mPasswordExpirationDays = in.readInt(); 676d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank mPasswordHistory = in.readInt(); 677d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank mPasswordComplexChars = in.readInt(); 678d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank } 679d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank 680345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler @Override 681345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public int hashCode() { 6829b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank long code = getSecurityCode(); 6839b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank return (int) code; 6849b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 6859b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank 6869b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank public long getSecurityCode() { 6879b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank long flags = 0; 6889b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank flags = (long)mMinPasswordLength << PASSWORD_LENGTH_SHIFT; 689345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler flags |= mPasswordMode; 6909b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank flags |= (long)mMaxPasswordFails << PASSWORD_MAX_FAILS_SHIFT; 6919b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank flags |= (long)mMaxScreenLockTime << SCREEN_LOCK_TIME_SHIFT; 692345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler if (mRequireRemoteWipe) { 693345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler flags |= REQUIRE_REMOTE_WIPE; 694345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 6959b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank flags |= (long)mPasswordHistory << PASSWORD_HISTORY_SHIFT; 6961ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler flags |= (long)mPasswordExpirationDays << PASSWORD_EXPIRATION_SHIFT; 6979b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank flags |= (long)mPasswordComplexChars << PASSWORD_COMPLEX_CHARS_SHIFT; 698345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler return flags; 699345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 700345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 701345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler @Override 702345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public String toString() { 703345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler return "{ " + "pw-len-min=" + mMinPasswordLength + " pw-mode=" + mPasswordMode 704345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler + " pw-fails-max=" + mMaxPasswordFails + " screenlock-max=" 7059b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank + mMaxScreenLockTime + " remote-wipe-req=" + mRequireRemoteWipe 7061ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler + " pw-expiration=" + mPasswordExpirationDays 7079b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank + " pw-history=" + mPasswordHistory 7089b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank + " pw-complex-chars=" + mPasswordComplexChars + "}"; 709345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 710345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 711345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 712345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 7133d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * If we are not the active device admin, try to become so. 7143d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * 715e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler * Also checks for any policies that we have added during the lifetime of this app. 716e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler * This catches the case where the user granted an earlier (smaller) set of policies 717e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler * but an app upgrade requires that new policies be granted. 718e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler * 7193d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * @return true if we are already active, false if we are not 720345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 7213d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler public boolean isActiveAdmin() { 7223d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler DevicePolicyManager dpm = getDPM(); 723e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler return dpm.isAdminActive(mAdminName) && 724e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler dpm.hasGrantedPolicy(mAdminName, DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD); 7253d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 726345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 7273d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 7283d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Report admin component name - for making calls into device policy manager 7293d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 7303d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler public ComponentName getAdminComponent() { 7313d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler return mAdminName; 7323d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 7333d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 7343d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 73502d59d21949a77c60859b615312f02e6d8003490Marc Blank * Delete all accounts whose security flags aren't zero (i.e. they have security enabled). 73602d59d21949a77c60859b615312f02e6d8003490Marc Blank * This method is synchronous, so it should normally be called within a worker thread (the 73702d59d21949a77c60859b615312f02e6d8003490Marc Blank * exception being for unit tests) 73802d59d21949a77c60859b615312f02e6d8003490Marc Blank * 73902d59d21949a77c60859b615312f02e6d8003490Marc Blank * @param context the caller's context 74002d59d21949a77c60859b615312f02e6d8003490Marc Blank */ 74102d59d21949a77c60859b615312f02e6d8003490Marc Blank /*package*/ void deleteSecuredAccounts(Context context) { 74202d59d21949a77c60859b615312f02e6d8003490Marc Blank ContentResolver cr = context.getContentResolver(); 74302d59d21949a77c60859b615312f02e6d8003490Marc Blank // Find all accounts with security and delete them 74402d59d21949a77c60859b615312f02e6d8003490Marc Blank Cursor c = cr.query(Account.CONTENT_URI, EmailContent.ID_PROJECTION, 74502d59d21949a77c60859b615312f02e6d8003490Marc Blank AccountColumns.SECURITY_FLAGS + "!=0", null, null); 74602d59d21949a77c60859b615312f02e6d8003490Marc Blank try { 74702d59d21949a77c60859b615312f02e6d8003490Marc Blank Log.w(TAG, "Email administration disabled; deleting " + c.getCount() + 74802d59d21949a77c60859b615312f02e6d8003490Marc Blank " secured account(s)"); 74902d59d21949a77c60859b615312f02e6d8003490Marc Blank while (c.moveToNext()) { 75002d59d21949a77c60859b615312f02e6d8003490Marc Blank Controller.getInstance(context).deleteAccountSync( 75102d59d21949a77c60859b615312f02e6d8003490Marc Blank c.getLong(EmailContent.ID_PROJECTION_COLUMN), context); 75202d59d21949a77c60859b615312f02e6d8003490Marc Blank } 75302d59d21949a77c60859b615312f02e6d8003490Marc Blank } finally { 75402d59d21949a77c60859b615312f02e6d8003490Marc Blank c.close(); 75502d59d21949a77c60859b615312f02e6d8003490Marc Blank } 75602d59d21949a77c60859b615312f02e6d8003490Marc Blank updatePolicies(-1); 75702d59d21949a77c60859b615312f02e6d8003490Marc Blank } 75802d59d21949a77c60859b615312f02e6d8003490Marc Blank 75902d59d21949a77c60859b615312f02e6d8003490Marc Blank /** 76002d59d21949a77c60859b615312f02e6d8003490Marc Blank * Internal handler for enabled->disabled transitions. Deletes all secured accounts. 761a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * Must call from worker thread, not on UI thread. 7623d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 76302d59d21949a77c60859b615312f02e6d8003490Marc Blank /*package*/ void onAdminEnabled(boolean isEnabled) { 764856e09d76ab62272e660fd4a08e25637f17319a0Andrew Stadler if (!isEnabled) { 765a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler deleteSecuredAccounts(mContext); 7663d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 7673d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 7683d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 7693d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 7701ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * Handle password expiration - if any accounts appear to have triggered this, put up 7711ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * warnings, or even shut them down. 7721ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * 7731ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * NOTE: If there are multiple accounts with password expiration policies, the device 7741ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * password will be set to expire in the shortest required interval (most secure). The logic 7751ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * in this method operates based on the aggregate setting - irrespective of which account caused 7761ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * the expiration. In other words, all accounts (that require expiration) will run/stop 7771ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * based on the requirements of the account with the shortest interval. 7781ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 779a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler private void onPasswordExpiring(Context context) { 7801ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // 1. Do we have any accounts that matter here? 7811ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long nextExpiringAccountId = findShortestExpiration(context); 7821ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 7831ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // 2. If not, exit immediately 7841ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (nextExpiringAccountId == -1) { 7851ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler return; 7861ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 7871ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 7881ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // 3. If yes, are we warning or expired? 7891ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long expirationDate = getDPM().getPasswordExpiration(mAdminName); 7901ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long timeUntilExpiration = expirationDate - System.currentTimeMillis(); 7911ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler boolean expired = timeUntilExpiration < 0; 7921ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (!expired) { 7931ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // 4. If warning, simply put up a generic notification and report that it came from 7941ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // the shortest-expiring account. 7951ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Account account = Account.restoreAccountWithId(context, nextExpiringAccountId); 7961ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (account == null) return; 7971ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); 7981ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler String ticker = context.getString( 7991ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler R.string.password_expire_warning_ticker_fmt, account.getDisplayName()); 8001ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler String contentTitle = context.getString( 8011ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler R.string.password_expire_warning_content_title); 8021ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler String contentText = context.getString( 8031ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler R.string.password_expire_warning_content_text_fmt, account.getDisplayName()); 8041ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler NotificationController nc = NotificationController.getInstance(mContext); 8051ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler nc.postAccountNotification(account, ticker, contentTitle, contentText, intent, 8061ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler NotificationController.NOTIFICATION_ID_PASSWORD_EXPIRING); 8071ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } else { 8081ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // 5. Actually expired - find all accounts that expire passwords, and wipe them 8091ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler boolean wiped = wipeExpiredAccounts(context, Controller.getInstance(context)); 8101ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (wiped) { 8111ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // Post notification 8121ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Account account = Account.restoreAccountWithId(context, nextExpiringAccountId); 8131ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (account == null) return; 8141ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Intent intent = 8151ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); 8161ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler String ticker = context.getString(R.string.password_expired_ticker); 8171ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler String contentTitle = context.getString(R.string.password_expired_content_title); 8181ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler String contentText = context.getString(R.string.password_expired_content_text); 8191ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler NotificationController nc = NotificationController.getInstance(mContext); 8201ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler nc.postAccountNotification(account, ticker, contentTitle, 8211ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler contentText, intent, 8221ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler NotificationController.NOTIFICATION_ID_PASSWORD_EXPIRED); 8231ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8241ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8251ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8261ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 8271ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 8281ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * Find the account with the shortest expiration time. This is always assumed to be 8291ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * the account that forces the password to be refreshed. 8301ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @return -1 if no expirations, or accountId if one is found 8311ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 8321ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /* package */ static long findShortestExpiration(Context context) { 8331ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long nextExpiringAccountId = -1; 8341ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long shortestExpiration = Long.MAX_VALUE; 8351ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Cursor c = context.getContentResolver().query(Account.CONTENT_URI, 8361ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler ACCOUNT_SECURITY_PROJECTION, Account.SECURITY_NONZERO_SELECTION, null, null); 8371ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler try { 8381ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler while (c.moveToNext()) { 8391ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long flags = c.getLong(ACCOUNT_SECURITY_COLUMN_FLAGS); 8401ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (flags != 0) { 8411ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler PolicySet p = new PolicySet(flags); 8421ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (p.mPasswordExpirationDays > 0 && 8431ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler p.mPasswordExpirationDays < shortestExpiration) { 8441ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler nextExpiringAccountId = c.getLong(ACCOUNT_SECURITY_COLUMN_ID); 8451ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler shortestExpiration = p.mPasswordExpirationDays; 8461ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8471ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8481ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8491ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } finally { 8501ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler c.close(); 8511ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8521ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler return nextExpiringAccountId; 8531ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8541ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 8551ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 8561ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * For all accounts that require password expiration, put them in security hold and wipe 8571ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * their data. 8581ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param context 8591ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param controller 8601ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @return true if one or more accounts were wiped 8611ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 8621ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /* package */ static boolean wipeExpiredAccounts(Context context, Controller controller) { 8631ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler boolean result = false; 8641ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Cursor c = context.getContentResolver().query(Account.CONTENT_URI, 8651ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler ACCOUNT_SECURITY_PROJECTION, Account.SECURITY_NONZERO_SELECTION, null, null); 8661ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler try { 8671ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler while (c.moveToNext()) { 8681ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long flags = c.getLong(ACCOUNT_SECURITY_COLUMN_FLAGS); 8691ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (flags != 0) { 8701ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler PolicySet p = new PolicySet(flags); 8711ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (p.mPasswordExpirationDays > 0) { 8721ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long accountId = c.getLong(ACCOUNT_SECURITY_COLUMN_ID); 8731ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Account account = Account.restoreAccountWithId(context, accountId); 8741ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (account != null) { 8751ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // Mark the account as "on hold". 8761ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler setAccountHoldFlag(context, account, true); 8771ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // Erase data 8781ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler controller.deleteSyncedDataSync(accountId); 8791ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // Report one or more were found 8801ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler result = true; 8811ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8821ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8831ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8841ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8851ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } finally { 8861ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler c.close(); 8871ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8881ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler return result; 8891ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8901ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 8911ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 892a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * Callback from EmailBroadcastProcessorService. This provides the workers for the 893a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * DeviceAdminReceiver calls. These should perform the work directly and not use async 894a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * threads for completion. 895a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler */ 896a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler public static void onDeviceAdminReceiverMessage(Context context, int message) { 897a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler SecurityPolicy instance = SecurityPolicy.getInstance(context); 898a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler switch (message) { 899a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler case DEVICE_ADMIN_MESSAGE_ENABLED: 900a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler instance.onAdminEnabled(true); 901a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler break; 902a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler case DEVICE_ADMIN_MESSAGE_DISABLED: 903a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler instance.onAdminEnabled(false); 904a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler break; 905a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler case DEVICE_ADMIN_MESSAGE_PASSWORD_CHANGED: 906a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler // TODO make a small helper for this 907a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler // Clear security holds (if any) 908a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler Account.clearSecurityHoldOnAllAccounts(context); 909a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler // Cancel any active notifications (if any are posted) 910a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler NotificationController nc = NotificationController.getInstance(context); 911a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler nc.cancelNotification(NotificationController.NOTIFICATION_ID_PASSWORD_EXPIRING); 912a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler nc.cancelNotification(NotificationController.NOTIFICATION_ID_PASSWORD_EXPIRED); 913a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler break; 914a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler case DEVICE_ADMIN_MESSAGE_PASSWORD_EXPIRING: 915a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler instance.onPasswordExpiring(instance.mContext); 916a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler break; 917a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler } 918a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler } 919a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler 920a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler /** 9213d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Device Policy administrator. This is primarily a listener for device state changes. 9223d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Note: This is instantiated by incoming messages. 923a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * Note: This is actually a BroadcastReceiver and must remain within the guidelines required 924a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * for proper behavior, including avoidance of ANRs. 9255893e9e008d46cf4be0c7f709a6e77e2652c3dddAndrew Stadler * Note: We do not implement onPasswordFailed() because the default behavior of the 9265893e9e008d46cf4be0c7f709a6e77e2652c3dddAndrew Stadler * DevicePolicyManager - complete local wipe after 'n' failures - is sufficient. 9273d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 9284ae83c58b3e136b4b1e859ee304ad1b332e9597fDianne Hackborn public static class PolicyAdmin extends DeviceAdminReceiver { 929345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 930345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 931345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Called after the administrator is first enabled. 932345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 933345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler @Override 934345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public void onEnabled(Context context, Intent intent) { 935a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler EmailBroadcastProcessorService.processDevicePolicyMessage(context, 936a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler DEVICE_ADMIN_MESSAGE_ENABLED); 937345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 938856e09d76ab62272e660fd4a08e25637f17319a0Andrew Stadler 939345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 940345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Called prior to the administrator being disabled. 941345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 942345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler @Override 943345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public void onDisabled(Context context, Intent intent) { 944a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler EmailBroadcastProcessorService.processDevicePolicyMessage(context, 945a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler DEVICE_ADMIN_MESSAGE_DISABLED); 946345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 947856e09d76ab62272e660fd4a08e25637f17319a0Andrew Stadler 948345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 94902d59d21949a77c60859b615312f02e6d8003490Marc Blank * Called when the user asks to disable administration; we return a warning string that 95002d59d21949a77c60859b615312f02e6d8003490Marc Blank * will be presented to the user 95102d59d21949a77c60859b615312f02e6d8003490Marc Blank */ 95202d59d21949a77c60859b615312f02e6d8003490Marc Blank @Override 95302d59d21949a77c60859b615312f02e6d8003490Marc Blank public CharSequence onDisableRequested(Context context, Intent intent) { 95402d59d21949a77c60859b615312f02e6d8003490Marc Blank return context.getString(R.string.disable_admin_warning); 95502d59d21949a77c60859b615312f02e6d8003490Marc Blank } 95602d59d21949a77c60859b615312f02e6d8003490Marc Blank 95702d59d21949a77c60859b615312f02e6d8003490Marc Blank /** 958345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Called after the user has changed their password. 959345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 960345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler @Override 961345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public void onPasswordChanged(Context context, Intent intent) { 962a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler EmailBroadcastProcessorService.processDevicePolicyMessage(context, 963a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler DEVICE_ADMIN_MESSAGE_PASSWORD_CHANGED); 9641ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 9651ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 9661ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 9671ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * Called when device password is expiring 9681ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 9691ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler @Override 9701ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler public void onPasswordExpiring(Context context, Intent intent) { 971a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler EmailBroadcastProcessorService.processDevicePolicyMessage(context, 972a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler DEVICE_ADMIN_MESSAGE_PASSWORD_EXPIRING); 973345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 974345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 975345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler} 976