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 19e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadlerimport android.app.admin.DeviceAdminInfo; 206d0016229adc13fefe68820fe4d6e46f530952baDianne Hackbornimport android.app.admin.DeviceAdminReceiver; 216d0016229adc13fefe68820fe4d6e46f530952baDianne Hackbornimport android.app.admin.DevicePolicyManager; 22d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadlerimport android.content.ComponentName; 23f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.content.ContentProviderOperation; 2402d59d21949a77c60859b615312f02e6d8003490Marc Blankimport android.content.ContentResolver; 25f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.content.ContentUris; 263d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadlerimport android.content.ContentValues; 27345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadlerimport android.content.Context; 28345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadlerimport android.content.Intent; 29f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.content.OperationApplicationException; 30345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadlerimport android.database.Cursor; 31f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.net.Uri; 32bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Huimport android.os.Bundle; 33f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.os.RemoteException; 34345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 35bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrookimport com.android.email.NotificationController; 36bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrookimport com.android.email.NotificationControllerCreatorHolder; 37a60550e0eb08e0239d1fcea261b37ba592a35ba4Yu Ping Huimport com.android.email.provider.AccountReconciler; 38f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email.provider.EmailProvider; 39c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blankimport com.android.email.service.EmailBroadcastProcessorService; 40bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Huimport com.android.email.service.EmailServiceUtils; 41c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blankimport com.android.emailcommon.Logging; 42c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blankimport com.android.emailcommon.provider.Account; 43c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blankimport com.android.emailcommon.provider.EmailContent; 44c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blankimport com.android.emailcommon.provider.EmailContent.AccountColumns; 45c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blankimport com.android.emailcommon.provider.EmailContent.PolicyColumns; 46c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blankimport com.android.emailcommon.provider.Policy; 47f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.utility.TextUtilities; 48c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blankimport com.android.emailcommon.utility.Utility; 49560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils; 50c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blankimport com.google.common.annotations.VisibleForTesting; 51c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blank 52f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport java.util.ArrayList; 53f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 54345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler/** 55d71d0b223a5cd02e2a8f1ec5c3f8cebab170d65fAndrew Stadler * Utility functions to support reading and writing security policies, and handshaking the device 56d71d0b223a5cd02e2a8f1ec5c3f8cebab170d65fAndrew Stadler * into and out of various security states. 57345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 58345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadlerpublic class SecurityPolicy { 59ce6916b32a98a568ceafb734d050801f4459a532Martin Hibdon private static final String TAG = "Email"; 60345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private static SecurityPolicy sInstance = null; 61345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private Context mContext; 62d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler private DevicePolicyManager mDPM; 63d09cff08882e553afce919865a2cc60b657d4659Ben Komalo private final ComponentName mAdminName; 64aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank private Policy mAggregatePolicy; 652a5eeea9213005060256054ec773e72406415ce4Andrew Stadler 66a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler // Messages used for DevicePolicyManager callbacks 67a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler private static final int DEVICE_ADMIN_MESSAGE_ENABLED = 1; 68a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler private static final int DEVICE_ADMIN_MESSAGE_DISABLED = 2; 69a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler private static final int DEVICE_ADMIN_MESSAGE_PASSWORD_CHANGED = 3; 70a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler private static final int DEVICE_ADMIN_MESSAGE_PASSWORD_EXPIRING = 4; 71a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler 72aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank private static final String HAS_PASSWORD_EXPIRATION = 73aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank PolicyColumns.PASSWORD_EXPIRATION_DAYS + ">0"; 74aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank 75345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 76345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Get the security policy instance 77345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 78345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public synchronized static SecurityPolicy getInstance(Context context) { 79345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler if (sInstance == null) { 80a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler sInstance = new SecurityPolicy(context.getApplicationContext()); 81345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 82345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler return sInstance; 83345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 84345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 85345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 86345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Private constructor (one time only) 87345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 88345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler private SecurityPolicy(Context context) { 89968be441b4c253668c4ee1c7a3f8e4b0eb12cf24Makoto Onuki mContext = context.getApplicationContext(); 90d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler mDPM = null; 91d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler mAdminName = new ComponentName(context, PolicyAdmin.class); 92d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler mAggregatePolicy = null; 93345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 94345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 95345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 96345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * For testing only: Inject context into already-created instance 97345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 98345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /* package */ void setContext(Context context) { 99345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler mContext = context; 100345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 101345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 102345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 103345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Compute the aggregate policy for all accounts that require it, and record it. 104345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * 105345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * The business logic is as follows: 106345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * min password length take the max 107345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * password mode take the max (strongest mode) 108345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * max password fails take the min 109345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * max screen lock time take the min 110345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * require remote wipe take the max (logical or) 1119b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank * password history take the max (strongest mode) 1121ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * password expiration take the min (strongest mode) 1139b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank * password complex chars take the max (strongest mode) 114469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * encryption take the max (logical or) 115968be441b4c253668c4ee1c7a3f8e4b0eb12cf24Makoto Onuki * 116d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * @return a policy representing the strongest aggregate. If no policy sets are defined, 117d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * a lightweight "nothing required" policy will be returned. Never null. 118345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 119d09cff08882e553afce919865a2cc60b657d4659Ben Komalo @VisibleForTesting 120d09cff08882e553afce919865a2cc60b657d4659Ben Komalo Policy computeAggregatePolicy() { 121345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler boolean policiesFound = false; 122aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Policy aggregate = new Policy(); 123aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordMinLength = Integer.MIN_VALUE; 124aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordMode = Integer.MIN_VALUE; 125aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordMaxFails = Integer.MAX_VALUE; 126aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordHistory = Integer.MIN_VALUE; 127aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordExpirationDays = Integer.MAX_VALUE; 128aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordComplexChars = Integer.MIN_VALUE; 129aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mMaxScreenLockTime = Integer.MAX_VALUE; 130aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mRequireRemoteWipe = false; 131aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mRequireEncryption = false; 132e76962b1b9c66ecc3fd49cd4c956f234365bfe5cBen Komalo 133e76962b1b9c66ecc3fd49cd4c956f234365bfe5cBen Komalo // This can never be supported at this time. It exists only for historic reasons where 134e76962b1b9c66ecc3fd49cd4c956f234365bfe5cBen Komalo // this was able to be supported prior to the introduction of proper removable storage 135e76962b1b9c66ecc3fd49cd4c956f234365bfe5cBen Komalo // support for external storage. 136aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mRequireEncryptionExternal = false; 137aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank 138aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Cursor c = mContext.getContentResolver().query(Policy.CONTENT_URI, 139aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Policy.CONTENT_PROJECTION, null, null, null); 140aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Policy policy = new Policy(); 141345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler try { 142345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler while (c.moveToNext()) { 143aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank policy.restore(c); 14451c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 145560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(TAG, "Aggregate from: " + policy); 146aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 147aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordMinLength = 148aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Math.max(policy.mPasswordMinLength, aggregate.mPasswordMinLength); 149aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordMode = Math.max(policy.mPasswordMode, aggregate.mPasswordMode); 150aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mPasswordMaxFails > 0) { 151aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordMaxFails = 152aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Math.min(policy.mPasswordMaxFails, aggregate.mPasswordMaxFails); 153aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 154aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mMaxScreenLockTime > 0) { 155aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mMaxScreenLockTime = Math.min(policy.mMaxScreenLockTime, 156aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mMaxScreenLockTime); 157aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 158aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mPasswordHistory > 0) { 159aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordHistory = 160aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Math.max(policy.mPasswordHistory, aggregate.mPasswordHistory); 161345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 162aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mPasswordExpirationDays > 0) { 163aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordExpirationDays = 164aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Math.min(policy.mPasswordExpirationDays, aggregate.mPasswordExpirationDays); 165aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 166aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mPasswordComplexChars > 0) { 167aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordComplexChars = Math.max(policy.mPasswordComplexChars, 168aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordComplexChars); 169aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 170aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mRequireRemoteWipe |= policy.mRequireRemoteWipe; 171aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mRequireEncryption |= policy.mRequireEncryption; 172d09cff08882e553afce919865a2cc60b657d4659Ben Komalo aggregate.mDontAllowCamera |= policy.mDontAllowCamera; 173aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank policiesFound = true; 174345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 175345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } finally { 176345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler c.close(); 177345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 178345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler if (policiesFound) { 1793d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler // final cleanup pass converts any untouched min/max values to zero (not specified) 180aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (aggregate.mPasswordMinLength == Integer.MIN_VALUE) aggregate.mPasswordMinLength = 0; 181aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (aggregate.mPasswordMode == Integer.MIN_VALUE) aggregate.mPasswordMode = 0; 182aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (aggregate.mPasswordMaxFails == Integer.MAX_VALUE) aggregate.mPasswordMaxFails = 0; 183aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (aggregate.mMaxScreenLockTime == Integer.MAX_VALUE) aggregate.mMaxScreenLockTime = 0; 184aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (aggregate.mPasswordHistory == Integer.MIN_VALUE) aggregate.mPasswordHistory = 0; 185aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (aggregate.mPasswordExpirationDays == Integer.MAX_VALUE) 186aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordExpirationDays = 0; 187aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (aggregate.mPasswordComplexChars == Integer.MIN_VALUE) 188aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregate.mPasswordComplexChars = 0; 18951c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 190560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(TAG, "Calculated Aggregate: " + aggregate); 191aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 192aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank return aggregate; 193345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 19451c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 195560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(TAG, "Calculated Aggregate: no policy"); 196aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 197aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank return Policy.NO_POLICY; 198345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 199345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 200345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 20150d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * Return updated aggregate policy, from cached value if possible 20250d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler */ 203aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank public synchronized Policy getAggregatePolicy() { 20450d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler if (mAggregatePolicy == null) { 20550d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler mAggregatePolicy = computeAggregatePolicy(); 20650d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } 20750d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler return mAggregatePolicy; 20850d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } 20950d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler 21050d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler /** 211d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * Get the dpm. This mainly allows us to make some utility calls without it, for testing. 212d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler */ 213a0d080558ff06f88f000cf424803c8241dd8d2ebAndy Stadler /* package */ synchronized DevicePolicyManager getDPM() { 214d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler if (mDPM == null) { 215d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler mDPM = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 216d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 217d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler return mDPM; 218d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 219d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler 220d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler /** 221f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * API: Report that policies may have been updated due to rewriting values in an Account; we 222f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * clear the aggregate policy (so it can be recomputed) and set the policies in the DPM 223d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler */ 224f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank public synchronized void policiesUpdated() { 225d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler mAggregatePolicy = null; 226f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank setActivePolicies(); 227d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 228d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler 229d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler /** 23050d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * API: Report that policies may have been updated *and* the caller vouches that the 23150d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * change is a reduction in policies. This forces an immediate change to device state. 23250d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * Typically used when deleting accounts, although we may use it for server-side policy 23350d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * rollbacks. 23450d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler */ 23550d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler public void reducePolicies() { 23651c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 237560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(TAG, "reducePolicies"); 238aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 239f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank policiesUpdated(); 240a0d080558ff06f88f000cf424803c8241dd8d2ebAndy Stadler } 241a0d080558ff06f88f000cf424803c8241dd8d2ebAndy Stadler 242a0d080558ff06f88f000cf424803c8241dd8d2ebAndy Stadler /** 243469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * API: Query used to determine if a given policy is "active" (the device is operating at 244469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * the required security level). 245469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * 246d09cff08882e553afce919865a2cc60b657d4659Ben Komalo * @param policy the policies requested, or null to check aggregate stored policies 247469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * @return true if the requested policies are active, false if not. 248469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler */ 249aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank public boolean isActive(Policy policy) { 250aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank int reasons = getInactiveReasons(policy); 25151c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG && (reasons != 0)) { 252aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank StringBuilder sb = new StringBuilder("isActive for " + policy + ": "); 253076ab8307439f31e8197579f11b9bc3342fd5626Tony Mantler sb.append("FALSE -> "); 254aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if ((reasons & INACTIVE_NEED_ACTIVATION) != 0) { 255aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank sb.append("no_admin "); 256aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 257aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if ((reasons & INACTIVE_NEED_CONFIGURATION) != 0) { 258aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank sb.append("config "); 259aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 260aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if ((reasons & INACTIVE_NEED_PASSWORD) != 0) { 261aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank sb.append("password "); 262aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 263aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if ((reasons & INACTIVE_NEED_ENCRYPTION) != 0) { 264aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank sb.append("encryption "); 265aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 266f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if ((reasons & INACTIVE_PROTOCOL_POLICIES) != 0) { 267f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank sb.append("protocol "); 268f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 269560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(TAG, sb.toString()); 270aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 271469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler return reasons == 0; 272469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler } 273469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler 274469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler /** 275469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * Return bits from isActive: Device Policy Manager has not been activated 276469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler */ 277469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler public final static int INACTIVE_NEED_ACTIVATION = 1; 278469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler 279469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler /** 280469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * Return bits from isActive: Some required configuration is not correct (no user action). 281469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler */ 282469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler public final static int INACTIVE_NEED_CONFIGURATION = 2; 283469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler 284469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler /** 285469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * Return bits from isActive: Password needs to be set or updated 286469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler */ 287469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler public final static int INACTIVE_NEED_PASSWORD = 4; 288469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler 289469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler /** 290469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * Return bits from isActive: Encryption has not be enabled 291469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler */ 292469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler public final static int INACTIVE_NEED_ENCRYPTION = 8; 293469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler 294469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler /** 295f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * Return bits from isActive: Protocol-specific policies cannot be enforced 296f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank */ 297f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank public final static int INACTIVE_PROTOCOL_POLICIES = 16; 298f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 299f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank /** 300d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * API: Query used to determine if a given policy is "active" (the device is operating at 3013d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * the required security level). 302345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * 3033d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * This can be used when syncing a specific account, by passing a specific set of policies 3043d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * for that account. Or, it can be used at any time to compare the device 3053d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * state against the aggregate set of device policies stored in all accounts. 3063d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * 3073d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * This method is for queries only, and does not trigger any change in device state. 3083d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * 3091ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * NOTE: If there are multiple accounts with password expiration policies, the device 3101ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * password will be set to expire in the shortest required interval (most secure). This method 3111ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * will return 'false' as soon as the password expires - irrespective of which account caused 3121ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * the expiration. In other words, all accounts (that require expiration) will run/stop 3131ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * based on the requirements of the account with the shortest interval. 3141ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * 315d09cff08882e553afce919865a2cc60b657d4659Ben Komalo * @param policy the policies requested, or null to check aggregate stored policies 316469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * @return zero if the requested policies are active, non-zero bits indicates that more work 317469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler * is needed (typically, by the user) before the required security polices are fully active. 318345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 319aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank public int getInactiveReasons(Policy policy) { 32050d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler // select aggregate set if needed 321aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy == null) { 322aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank policy = getAggregatePolicy(); 32350d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } 32450d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler // quick check for the "empty set" of no policies 325aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy == Policy.NO_POLICY) { 326469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler return 0; 32750d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } 328469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler int reasons = 0; 329d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler DevicePolicyManager dpm = getDPM(); 330e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler if (isActiveAdmin()) { 331d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler // check each policy explicitly 332aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mPasswordMinLength > 0) { 333aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (dpm.getPasswordMinimumLength(mAdminName) < policy.mPasswordMinLength) { 334469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler reasons |= INACTIVE_NEED_PASSWORD; 335d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 336d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 337aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mPasswordMode > 0) { 338aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (dpm.getPasswordQuality(mAdminName) < policy.getDPManagerPasswordQuality()) { 339469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler reasons |= INACTIVE_NEED_PASSWORD; 340d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 341d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler if (!dpm.isActivePasswordSufficient()) { 342469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler reasons |= INACTIVE_NEED_PASSWORD; 343d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 344d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 345aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mMaxScreenLockTime > 0) { 346d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler // Note, we use seconds, dpm uses milliseconds 347aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (dpm.getMaximumTimeToLock(mAdminName) > policy.mMaxScreenLockTime * 1000) { 348469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler reasons |= INACTIVE_NEED_CONFIGURATION; 349d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 350d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 351aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mPasswordExpirationDays > 0) { 3521ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // confirm that expirations are currently set 3531ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long currentTimeout = dpm.getPasswordExpirationTimeout(mAdminName); 3541ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (currentTimeout == 0 355aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank || currentTimeout > policy.getDPManagerPasswordExpirationTimeout()) { 356469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler reasons |= INACTIVE_NEED_PASSWORD; 3571ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 3581ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // confirm that the current password hasn't expired 3591ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long expirationDate = dpm.getPasswordExpiration(mAdminName); 3601ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long timeUntilExpiration = expirationDate - System.currentTimeMillis(); 3611ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler boolean expired = timeUntilExpiration < 0; 3621ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (expired) { 363469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler reasons |= INACTIVE_NEED_PASSWORD; 3641ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 3659b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 366aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mPasswordHistory > 0) { 367aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (dpm.getPasswordHistoryLength(mAdminName) < policy.mPasswordHistory) { 368e86d8af163ada4d0b3f7c5cb0b32cfeb12da473cMarc Blank // There's no user action for changes here; this is just a configuration change 369e86d8af163ada4d0b3f7c5cb0b32cfeb12da473cMarc Blank reasons |= INACTIVE_NEED_CONFIGURATION; 3709b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 3719b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 372aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mPasswordComplexChars > 0) { 373aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (dpm.getPasswordMinimumNonLetter(mAdminName) < policy.mPasswordComplexChars) { 374469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler reasons |= INACTIVE_NEED_PASSWORD; 375469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler } 376469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler } 377aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policy.mRequireEncryption) { 378c2e638351c19ab22ad9ab4cce2853414c34724c3Andy Stadler int encryptionStatus = getDPM().getStorageEncryptionStatus(); 379469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler if (encryptionStatus != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) { 380469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler reasons |= INACTIVE_NEED_ENCRYPTION; 3819b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 3829b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank } 383ce582527bbdce6e7a5c255123e9f2b743467919eMarc Blank if (policy.mDontAllowCamera && !dpm.getCameraDisabled(mAdminName)) { 384ce582527bbdce6e7a5c255123e9f2b743467919eMarc Blank reasons |= INACTIVE_NEED_CONFIGURATION; 385ce582527bbdce6e7a5c255123e9f2b743467919eMarc Blank } 386d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler // password failures are counted locally - no test required here 387d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler // no check required for remote wipe (it's supported, if we're the admin) 3882a5eeea9213005060256054ec773e72406415ce4Andrew Stadler 389f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (policy.mProtocolPoliciesUnsupported != null) { 390f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank reasons |= INACTIVE_PROTOCOL_POLICIES; 391f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 392f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 393469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler // If we made it all the way, reasons == 0 here. Otherwise it's a list of grievances. 394469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler return reasons; 395d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler } 396d71d0b223a5cd02e2a8f1ec5c3f8cebab170d65fAndrew Stadler // return false, not active 397469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler return INACTIVE_NEED_ACTIVATION; 398345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 399345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 400345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 40150d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * Set the requested security level based on the aggregate set of requests. 40250d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * If the set is empty, we release our device administration. If the set is non-empty, 40350d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * we only proceed if we are already active as an admin. 4043d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 4053d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler public void setActivePolicies() { 4063d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler DevicePolicyManager dpm = getDPM(); 40750d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler // compute aggregate set of policies 408aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Policy aggregatePolicy = getAggregatePolicy(); 40950d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler // if empty set, detach from policy manager 410aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (aggregatePolicy == Policy.NO_POLICY) { 41151c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 412560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(TAG, "setActivePolicies: none, remove admin"); 413aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 41450d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler dpm.removeActiveAdmin(mAdminName); 415e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler } else if (isActiveAdmin()) { 41651c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 417560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(TAG, "setActivePolicies: " + aggregatePolicy); 418aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 4193d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler // set each policy in the policy manager 4203d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler // password mode & length 421aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank dpm.setPasswordQuality(mAdminName, aggregatePolicy.getDPManagerPasswordQuality()); 422aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank dpm.setPasswordMinimumLength(mAdminName, aggregatePolicy.mPasswordMinLength); 4233d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler // screen lock time 424aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank dpm.setMaximumTimeToLock(mAdminName, aggregatePolicy.mMaxScreenLockTime * 1000); 4253d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler // local wipe (failed passwords limit) 426aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank dpm.setMaximumFailedPasswordsForWipe(mAdminName, aggregatePolicy.mPasswordMaxFails); 4271ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // password expiration (days until a password expires). API takes mSec. 4281ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler dpm.setPasswordExpirationTimeout(mAdminName, 429aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank aggregatePolicy.getDPManagerPasswordExpirationTimeout()); 4309b4988de43dbee6c06066caab63806e8c8303d7dMarc Blank // password history length (number of previous passwords that may not be reused) 431aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank dpm.setPasswordHistoryLength(mAdminName, aggregatePolicy.mPasswordHistory); 43222759bacd95385d95d3d9321f490763df1aba89dAndy Stadler // password minimum complex characters. 43322759bacd95385d95d3d9321f490763df1aba89dAndy Stadler // Note, in Exchange, "complex chars" simply means "non alpha", but in the DPM, 43422759bacd95385d95d3d9321f490763df1aba89dAndy Stadler // setting the quality to complex also defaults min symbols=1 and min numeric=1. 43522759bacd95385d95d3d9321f490763df1aba89dAndy Stadler // We always / safely clear minSymbols & minNumeric to zero (there is no policy 43622759bacd95385d95d3d9321f490763df1aba89dAndy Stadler // configuration in which we explicitly require a minimum number of digits or symbols.) 43722759bacd95385d95d3d9321f490763df1aba89dAndy Stadler dpm.setPasswordMinimumSymbols(mAdminName, 0); 43822759bacd95385d95d3d9321f490763df1aba89dAndy Stadler dpm.setPasswordMinimumNumeric(mAdminName, 0); 439aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank dpm.setPasswordMinimumNonLetter(mAdminName, aggregatePolicy.mPasswordComplexChars); 440d09cff08882e553afce919865a2cc60b657d4659Ben Komalo // Device capabilities 441ce6916b32a98a568ceafb734d050801f4459a532Martin Hibdon try { 442ce6916b32a98a568ceafb734d050801f4459a532Martin Hibdon // If we are running in a managed policy, it is a securityException to even 443ce6916b32a98a568ceafb734d050801f4459a532Martin Hibdon // call setCameraDisabled(), if is disabled is false. We have to swallow 444ce6916b32a98a568ceafb734d050801f4459a532Martin Hibdon // the exception here. 445ce6916b32a98a568ceafb734d050801f4459a532Martin Hibdon dpm.setCameraDisabled(mAdminName, aggregatePolicy.mDontAllowCamera); 446ce6916b32a98a568ceafb734d050801f4459a532Martin Hibdon } catch (SecurityException e) { 447ce6916b32a98a568ceafb734d050801f4459a532Martin Hibdon LogUtils.d(TAG, "SecurityException in setCameraDisabled, nothing changed"); 448ce6916b32a98a568ceafb734d050801f4459a532Martin Hibdon } 449d09cff08882e553afce919865a2cc60b657d4659Ben Komalo 450469f2987dc11d153434e50eb04dd6b83b924d09dAndy Stadler // encryption required 451aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank dpm.setStorageEncryption(mAdminName, aggregatePolicy.mRequireEncryption); 4523d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 4533d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 4543d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 4553d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 4569ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank * Convenience method; see javadoc below 4579ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank */ 4589ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank public static void setAccountHoldFlag(Context context, long accountId, boolean newState) { 4599ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank Account account = Account.restoreAccountWithId(context, accountId); 4609ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank if (account != null) { 4619ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank setAccountHoldFlag(context, account, newState); 462bc53491b95e4de2e30b04e696f30d209539ec87aMarc Blank if (newState) { 463bc53491b95e4de2e30b04e696f30d209539ec87aMarc Blank // Make sure there's a notification up 464bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook final NotificationController nc = 465bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook NotificationControllerCreatorHolder.getInstance(context); 466bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook nc.showSecurityNeededNotification(account); 467bc53491b95e4de2e30b04e696f30d209539ec87aMarc Blank } 4689ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank } 4699ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank } 4709ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank 4719ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank /** 4722a5eeea9213005060256054ec773e72406415ce4Andrew Stadler * API: Set/Clear the "hold" flag in any account. This flag serves a dual purpose: 4732a5eeea9213005060256054ec773e72406415ce4Andrew Stadler * Setting it gives us an indication that it was blocked, and clearing it gives EAS a 4742a5eeea9213005060256054ec773e72406415ce4Andrew Stadler * signal to try syncing again. 475076ab8307439f31e8197579f11b9bc3342fd5626Tony Mantler * @param context context 4769ba506c4dd498150555f6c59aa758f7467bf9236Marc Blank * @param account the account whose hold flag is to be set/cleared 4771ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param newState true = security hold, false = free to sync 4782a5eeea9213005060256054ec773e72406415ce4Andrew Stadler */ 4791ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler public static void setAccountHoldFlag(Context context, Account account, boolean newState) { 4802a5eeea9213005060256054ec773e72406415ce4Andrew Stadler if (newState) { 4812a5eeea9213005060256054ec773e72406415ce4Andrew Stadler account.mFlags |= Account.FLAGS_SECURITY_HOLD; 4822a5eeea9213005060256054ec773e72406415ce4Andrew Stadler } else { 4832a5eeea9213005060256054ec773e72406415ce4Andrew Stadler account.mFlags &= ~Account.FLAGS_SECURITY_HOLD; 4842a5eeea9213005060256054ec773e72406415ce4Andrew Stadler } 4852a5eeea9213005060256054ec773e72406415ce4Andrew Stadler ContentValues cv = new ContentValues(); 4862a5eeea9213005060256054ec773e72406415ce4Andrew Stadler cv.put(AccountColumns.FLAGS, account.mFlags); 4871ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler account.update(context, cv); 4882a5eeea9213005060256054ec773e72406415ce4Andrew Stadler } 4892a5eeea9213005060256054ec773e72406415ce4Andrew Stadler 4902a5eeea9213005060256054ec773e72406415ce4Andrew Stadler /** 4913d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * API: Sync service should call this any time a sync fails due to isActive() returning false. 492d62860821c2dbc14ab493b888cb129bd5addd53dAndrew Stadler * This will kick off the notify-acquire-admin-state process and/or increase the security level. 493345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * The caller needs to write the required policies into this account before making this call. 4943d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Should not be called from UI thread - uses DB lookups to prepare new notifications 495345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * 496345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * @param accountId the account for which sync cannot proceed 497345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 498345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public void policiesRequired(long accountId) { 499f5418f1f93b02e7fab9f15eb201800b65510998eMarc Blank Account account = Account.restoreAccountWithId(mContext, accountId); 500844b14f851ce748b7b204125264bb3343c1a9039Marc Blank // In case the account has been deleted, just return 501844b14f851ce748b7b204125264bb3343c1a9039Marc Blank if (account == null) return; 502f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (account.mPolicyKey == 0) return; 503f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank Policy policy = Policy.restorePolicyWithId(mContext, account.mPolicyKey); 504f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (policy == null) return; 50551c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 506560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(TAG, "policiesRequired for " + account.mDisplayName + ": " + policy); 507aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank } 5081ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 5092a5eeea9213005060256054ec773e72406415ce4Andrew Stadler // Mark the account as "on hold". 5101ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler setAccountHoldFlag(mContext, account, true); 5111ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 512f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // Put up an appropriate notification 513bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook final NotificationController nc = 514bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook NotificationControllerCreatorHolder.getInstance(mContext); 515f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (policy.mProtocolPoliciesUnsupported == null) { 516bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook nc.showSecurityNeededNotification(account); 517f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } else { 518bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook nc.showSecurityUnsupportedNotification(account); 519f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 520f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 521f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 522f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank public static void clearAccountPolicy(Context context, Account account) { 523f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank setAccountPolicy(context, account, null, null); 524f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 525f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 526f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank /** 527f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * Set the policy for an account atomically; this also removes any other policy associated with 528f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * the account and sets the policy key for the account. If policy is null, the policyKey is 529f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * set to 0 and the securitySyncKey to null. Also, update the account object to reflect the 530f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * current policyKey and securitySyncKey 531f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * @param context the caller's context 532f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * @param account the account whose policy is to be set 533f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * @param policy the policy to set, or null if we're clearing the policy 534f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank * @param securitySyncKey the security sync key for this account (ignored if policy is null) 535f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank */ 536f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank public static void setAccountPolicy(Context context, Account account, Policy policy, 537f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank String securitySyncKey) { 538f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 539f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 540f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // Make sure this is a valid policy set 541f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (policy != null) { 542f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank policy.normalize(); 543f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // Add the new policy (no account will yet reference this) 544f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank ops.add(ContentProviderOperation.newInsert( 545f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank Policy.CONTENT_URI).withValues(policy.toContentValues()).build()); 546f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // Make the policyKey of the account our newly created policy, and set the sync key 547f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank ops.add(ContentProviderOperation.newUpdate( 548f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank ContentUris.withAppendedId(Account.CONTENT_URI, account.mId)) 549f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank .withValueBackReference(AccountColumns.POLICY_KEY, 0) 550f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank .withValue(AccountColumns.SECURITY_SYNC_KEY, securitySyncKey) 551f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank .build()); 552f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } else { 553f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank ops.add(ContentProviderOperation.newUpdate( 554f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank ContentUris.withAppendedId(Account.CONTENT_URI, account.mId)) 555f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank .withValue(AccountColumns.SECURITY_SYNC_KEY, null) 556f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank .withValue(AccountColumns.POLICY_KEY, 0) 557f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank .build()); 558f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 559f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 560f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // Delete the previous policy associated with this account, if any 561f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (account.mPolicyKey > 0) { 562f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank ops.add(ContentProviderOperation.newDelete( 563f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank ContentUris.withAppendedId( 564f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank Policy.CONTENT_URI, account.mPolicyKey)).build()); 565f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 566f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 567f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank try { 568f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops); 569f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank account.refresh(context); 570bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu syncAccount(context, account); 571f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } catch (RemoteException e) { 572f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // This is fatal to a remote process 573f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank throw new IllegalStateException("Exception setting account policy."); 574f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } catch (OperationApplicationException e) { 575f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // Can't happen; our provider doesn't throw this exception 576f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 577f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 578f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 579bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu private static void syncAccount(final Context context, final Account account) { 580bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu final EmailServiceUtils.EmailServiceInfo info = 581bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu EmailServiceUtils.getServiceInfo(context, account.getProtocol(context)); 582bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu final android.accounts.Account amAccount = 583bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu new android.accounts.Account(account.mEmailAddress, info.accountType); 584bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu final Bundle extras = new Bundle(3); 585bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); 586bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true); 587bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); 588bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu ContentResolver.requestSync(amAccount, EmailContent.AUTHORITY, extras); 589921c04d2ac5df091fb3c5cfa823e6dc2fc6cdf5aMartin Hibdon LogUtils.i(TAG, "requestSync SecurityPolicy syncAccount %s, %s", account.toString(), 590921c04d2ac5df091fb3c5cfa823e6dc2fc6cdf5aMartin Hibdon extras.toString()); 591bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu } 592bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu 593bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu public void syncAccount(final Account account) { 594bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu syncAccount(mContext, account); 595bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu } 596bd1e0b79e470afcfb9e6281711ad1038050c38fbYu Ping Hu 59734662f11b2ff38f05219415f3672a52a7af2338fTony Mantler public void setAccountPolicy(long accountId, Policy policy, String securityKey, 59834662f11b2ff38f05219415f3672a52a7af2338fTony Mantler boolean notify) { 599f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank Account account = Account.restoreAccountWithId(mContext, accountId); 600f3440d33a9da544b202c05d1d5e4849674bcf5bfJay Shrauner // In case the account has been deleted, just return 601f3440d33a9da544b202c05d1d5e4849674bcf5bfJay Shrauner if (account == null) { 602f3440d33a9da544b202c05d1d5e4849674bcf5bfJay Shrauner return; 603f3440d33a9da544b202c05d1d5e4849674bcf5bfJay Shrauner } 604f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank Policy oldPolicy = null; 605f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (account.mPolicyKey > 0) { 606f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank oldPolicy = Policy.restorePolicyWithId(mContext, account.mPolicyKey); 607f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 608940a335bab634a849b45f62d711601d57f59aeb1Yu Ping Hu 609940a335bab634a849b45f62d711601d57f59aeb1Yu Ping Hu // If attachment policies have changed, fix up any affected attachment records 610940a335bab634a849b45f62d711601d57f59aeb1Yu Ping Hu if (oldPolicy != null && securityKey != null) { 611940a335bab634a849b45f62d711601d57f59aeb1Yu Ping Hu if ((oldPolicy.mDontAllowAttachments != policy.mDontAllowAttachments) || 612940a335bab634a849b45f62d711601d57f59aeb1Yu Ping Hu (oldPolicy.mMaxAttachmentSize != policy.mMaxAttachmentSize)) { 613940a335bab634a849b45f62d711601d57f59aeb1Yu Ping Hu Policy.setAttachmentFlagsForNewPolicy(mContext, account, policy); 614940a335bab634a849b45f62d711601d57f59aeb1Yu Ping Hu } 615940a335bab634a849b45f62d711601d57f59aeb1Yu Ping Hu } 616940a335bab634a849b45f62d711601d57f59aeb1Yu Ping Hu 617f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank boolean policyChanged = (oldPolicy == null) || !oldPolicy.equals(policy); 618f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (!policyChanged && (TextUtilities.stringOrNullEquals(securityKey, 619f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank account.mSecuritySyncKey))) { 620560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "setAccountPolicy; policy unchanged"); 621f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } else { 622f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank setAccountPolicy(mContext, account, policy, securityKey); 623f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank policiesUpdated(); 624f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 625f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 626f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank boolean setHold = false; 627bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook final NotificationController nc = 628bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook NotificationControllerCreatorHolder.getInstance(mContext); 629f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (policy.mProtocolPoliciesUnsupported != null) { 630f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // We can't support this, reasons in unsupportedRemotePolicies 631560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, 632f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank "Notify policies for " + account.mDisplayName + " not supported."); 633f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank setHold = true; 63434662f11b2ff38f05219415f3672a52a7af2338fTony Mantler if (notify) { 635bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook nc.showSecurityUnsupportedNotification(account); 63634662f11b2ff38f05219415f3672a52a7af2338fTony Mantler } 637f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // Erase data 638f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank Uri uri = EmailProvider.uiUri("uiaccountdata", accountId); 639f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank mContext.getContentResolver().delete(uri, null, null); 640f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } else if (isActive(policy)) { 641f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (policyChanged) { 642560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "Notify policies for " + account.mDisplayName 643560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy + " changed."); 64434662f11b2ff38f05219415f3672a52a7af2338fTony Mantler if (notify) { 64534662f11b2ff38f05219415f3672a52a7af2338fTony Mantler // Notify that policies changed 646bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook nc.showSecurityChangedNotification(account); 64734662f11b2ff38f05219415f3672a52a7af2338fTony Mantler } 648bc53491b95e4de2e30b04e696f30d209539ec87aMarc Blank } else { 649560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "Policy is active and unchanged; do not notify."); 650f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 651f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } else { 652f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank setHold = true; 653560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "Notify policies for " + account.mDisplayName + 654f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank " are not being enforced."); 65534662f11b2ff38f05219415f3672a52a7af2338fTony Mantler if (notify) { 65634662f11b2ff38f05219415f3672a52a7af2338fTony Mantler // Put up a notification 657bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook nc.showSecurityNeededNotification(account); 65834662f11b2ff38f05219415f3672a52a7af2338fTony Mantler } 659f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank } 660f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // Set/clear the account hold. 661f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank setAccountHoldFlag(mContext, account, setHold); 6623d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 6633d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 6643d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 6653d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Called from the notification's intent receiver to register that the notification can be 6663d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * cleared now. 6673d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 668c6df1d605fb3e235df6d4a21ae00632c9d6e3cc2Marc Blank public void clearNotification() { 669bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook final NotificationController nc = 670bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook NotificationControllerCreatorHolder.getInstance(mContext); 671bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook 672bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook nc.cancelSecurityNeededNotification(); 6733d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 6743d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 6753d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 67650d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler * API: Remote wipe (from server). This is final, there is no confirmation. It will only 677c82c1caf0138dbcef044ee41d24791f2ebeeb88aMarc Blank * return to the caller if there is an unexpected failure. The wipe includes external storage. 6783d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 67950d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler public void remoteWipe() { 6803d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler DevicePolicyManager dpm = getDPM(); 6813d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler if (dpm.isAdminActive(mAdminName)) { 682c82c1caf0138dbcef044ee41d24791f2ebeeb88aMarc Blank dpm.wipeData(DevicePolicyManager.WIPE_EXTERNAL_STORAGE); 68350d1610c43c70039e9a02b862ec43cd6ee3d7906Andrew Stadler } else { 684560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "Could not remote wipe because not device admin."); 6853d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 686345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 687345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 6883d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * If we are not the active device admin, try to become so. 6893d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * 690e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler * Also checks for any policies that we have added during the lifetime of this app. 691e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler * This catches the case where the user granted an earlier (smaller) set of policies 692e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler * but an app upgrade requires that new policies be granted. 693e7f4d3ebfcf497c015ba65be7ecebea8926b995cAndy Stadler * 6943d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * @return true if we are already active, false if we are not 695345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 6963d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler public boolean isActiveAdmin() { 6973d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler DevicePolicyManager dpm = getDPM(); 698c2e638351c19ab22ad9ab4cce2853414c34724c3Andy Stadler return dpm.isAdminActive(mAdminName) 699c2e638351c19ab22ad9ab4cce2853414c34724c3Andy Stadler && dpm.hasGrantedPolicy(mAdminName, DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD) 700aa0a3553974972561e089f2780fb7f6743b3303eBen Komalo && dpm.hasGrantedPolicy(mAdminName, DeviceAdminInfo.USES_ENCRYPTED_STORAGE) 701aa0a3553974972561e089f2780fb7f6743b3303eBen Komalo && dpm.hasGrantedPolicy(mAdminName, DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA); 7023d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 703345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 7043d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 7053d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Report admin component name - for making calls into device policy manager 7063d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 7073d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler public ComponentName getAdminComponent() { 7083d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler return mAdminName; 7093d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 7103d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 7113d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 71202d59d21949a77c60859b615312f02e6d8003490Marc Blank * Delete all accounts whose security flags aren't zero (i.e. they have security enabled). 71302d59d21949a77c60859b615312f02e6d8003490Marc Blank * This method is synchronous, so it should normally be called within a worker thread (the 71402d59d21949a77c60859b615312f02e6d8003490Marc Blank * exception being for unit tests) 71502d59d21949a77c60859b615312f02e6d8003490Marc Blank * 71602d59d21949a77c60859b615312f02e6d8003490Marc Blank * @param context the caller's context 71702d59d21949a77c60859b615312f02e6d8003490Marc Blank */ 71802d59d21949a77c60859b615312f02e6d8003490Marc Blank /*package*/ void deleteSecuredAccounts(Context context) { 71902d59d21949a77c60859b615312f02e6d8003490Marc Blank ContentResolver cr = context.getContentResolver(); 72002d59d21949a77c60859b615312f02e6d8003490Marc Blank // Find all accounts with security and delete them 72102d59d21949a77c60859b615312f02e6d8003490Marc Blank Cursor c = cr.query(Account.CONTENT_URI, EmailContent.ID_PROJECTION, 722aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Account.SECURITY_NONZERO_SELECTION, null, null); 72302d59d21949a77c60859b615312f02e6d8003490Marc Blank try { 724560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.w(TAG, "Email administration disabled; deleting " + c.getCount() + 72502d59d21949a77c60859b615312f02e6d8003490Marc Blank " secured account(s)"); 72602d59d21949a77c60859b615312f02e6d8003490Marc Blank while (c.moveToNext()) { 727f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank long accountId = c.getLong(EmailContent.ID_PROJECTION_COLUMN); 728a60550e0eb08e0239d1fcea261b37ba592a35ba4Yu Ping Hu Uri uri = EmailProvider.uiUri("uiaccount", accountId); 729f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank cr.delete(uri, null, null); 73002d59d21949a77c60859b615312f02e6d8003490Marc Blank } 73102d59d21949a77c60859b615312f02e6d8003490Marc Blank } finally { 73202d59d21949a77c60859b615312f02e6d8003490Marc Blank c.close(); 73302d59d21949a77c60859b615312f02e6d8003490Marc Blank } 734f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank policiesUpdated(); 735a60550e0eb08e0239d1fcea261b37ba592a35ba4Yu Ping Hu AccountReconciler.reconcileAccounts(context); 73602d59d21949a77c60859b615312f02e6d8003490Marc Blank } 73702d59d21949a77c60859b615312f02e6d8003490Marc Blank 73802d59d21949a77c60859b615312f02e6d8003490Marc Blank /** 73902d59d21949a77c60859b615312f02e6d8003490Marc Blank * Internal handler for enabled->disabled transitions. Deletes all secured accounts. 740a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * Must call from worker thread, not on UI thread. 7413d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 74202d59d21949a77c60859b615312f02e6d8003490Marc Blank /*package*/ void onAdminEnabled(boolean isEnabled) { 743856e09d76ab62272e660fd4a08e25637f17319a0Andrew Stadler if (!isEnabled) { 744a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler deleteSecuredAccounts(mContext); 7453d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 7463d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler } 7473d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler 7483d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler /** 7491ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * Handle password expiration - if any accounts appear to have triggered this, put up 7501ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * warnings, or even shut them down. 7511ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * 7521ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * NOTE: If there are multiple accounts with password expiration policies, the device 7531ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * password will be set to expire in the shortest required interval (most secure). The logic 7541ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * in this method operates based on the aggregate setting - irrespective of which account caused 7551ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * the expiration. In other words, all accounts (that require expiration) will run/stop 7561ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * based on the requirements of the account with the shortest interval. 7571ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 758a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler private void onPasswordExpiring(Context context) { 7591ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // 1. Do we have any accounts that matter here? 7601ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long nextExpiringAccountId = findShortestExpiration(context); 7611ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 7621ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // 2. If not, exit immediately 7631ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (nextExpiringAccountId == -1) { 7641ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler return; 7651ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 7661ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 7671ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // 3. If yes, are we warning or expired? 7681ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long expirationDate = getDPM().getPasswordExpiration(mAdminName); 7691ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler long timeUntilExpiration = expirationDate - System.currentTimeMillis(); 7701ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler boolean expired = timeUntilExpiration < 0; 771bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook final NotificationController nc = 772bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook NotificationControllerCreatorHolder.getInstance(context); 7731ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (!expired) { 7741ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // 4. If warning, simply put up a generic notification and report that it came from 7751ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // the shortest-expiring account. 776bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook nc.showPasswordExpiringNotificationSynchronous(nextExpiringAccountId); 7771ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } else { 7781ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // 5. Actually expired - find all accounts that expire passwords, and wipe them 779f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank boolean wiped = wipeExpiredAccounts(context); 7801ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (wiped) { 781bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook nc.showPasswordExpiredNotificationSynchronous(nextExpiringAccountId); 7821ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 7831ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 7841ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 7851ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 7861ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 7871ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * Find the account with the shortest expiration time. This is always assumed to be 7881ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * the account that forces the password to be refreshed. 7891ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @return -1 if no expirations, or accountId if one is found 7901ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 791aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank @VisibleForTesting 792aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank /*package*/ static long findShortestExpiration(Context context) { 793aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank long policyId = Utility.getFirstRowLong(context, Policy.CONTENT_URI, Policy.ID_PROJECTION, 794aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank HAS_PASSWORD_EXPIRATION, null, PolicyColumns.PASSWORD_EXPIRATION_DAYS + " ASC", 795aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank EmailContent.ID_PROJECTION_COLUMN, -1L); 796aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (policyId < 0) return -1L; 797aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank return Policy.getAccountIdWithPolicyKey(context, policyId); 7981ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 7991ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 8001ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 8011ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * For all accounts that require password expiration, put them in security hold and wipe 8021ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * their data. 803076ab8307439f31e8197579f11b9bc3342fd5626Tony Mantler * @param context context 8041ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @return true if one or more accounts were wiped 8051ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 806aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank @VisibleForTesting 807f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank /*package*/ static boolean wipeExpiredAccounts(Context context) { 8081ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler boolean result = false; 809aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Cursor c = context.getContentResolver().query(Policy.CONTENT_URI, 810aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Policy.ID_PROJECTION, HAS_PASSWORD_EXPIRATION, null, null); 811f3440d33a9da544b202c05d1d5e4849674bcf5bfJay Shrauner if (c == null) { 812f3440d33a9da544b202c05d1d5e4849674bcf5bfJay Shrauner return false; 813f3440d33a9da544b202c05d1d5e4849674bcf5bfJay Shrauner } 8141ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler try { 8151ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler while (c.moveToNext()) { 816aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank long policyId = c.getLong(Policy.ID_PROJECTION_COLUMN); 817aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank long accountId = Policy.getAccountIdWithPolicyKey(context, policyId); 818aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (accountId < 0) continue; 819aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank Account account = Account.restoreAccountWithId(context, accountId); 820aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank if (account != null) { 821aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank // Mark the account as "on hold". 822aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank setAccountHoldFlag(context, account, true); 823aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank // Erase data 824f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank Uri uri = EmailProvider.uiUri("uiaccountdata", accountId); 825f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank context.getContentResolver().delete(uri, null, null); 826aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank // Report one or more were found 827aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank result = true; 8281ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8291ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8301ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } finally { 8311ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler c.close(); 8321ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8331ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler return result; 8341ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 8351ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 8361ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 837a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * Callback from EmailBroadcastProcessorService. This provides the workers for the 838a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * DeviceAdminReceiver calls. These should perform the work directly and not use async 839a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * threads for completion. 840a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler */ 841a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler public static void onDeviceAdminReceiverMessage(Context context, int message) { 842a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler SecurityPolicy instance = SecurityPolicy.getInstance(context); 843a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler switch (message) { 844a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler case DEVICE_ADMIN_MESSAGE_ENABLED: 845a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler instance.onAdminEnabled(true); 846a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler break; 847a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler case DEVICE_ADMIN_MESSAGE_DISABLED: 848a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler instance.onAdminEnabled(false); 849a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler break; 850a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler case DEVICE_ADMIN_MESSAGE_PASSWORD_CHANGED: 851a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler // TODO make a small helper for this 852a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler // Clear security holds (if any) 853a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler Account.clearSecurityHoldOnAllAccounts(context); 854a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler // Cancel any active notifications (if any are posted) 855bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook final NotificationController nc = 856bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook NotificationControllerCreatorHolder.getInstance(context); 857bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook 858bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook nc.cancelPasswordExpirationNotifications(); 859a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler break; 860a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler case DEVICE_ADMIN_MESSAGE_PASSWORD_EXPIRING: 861a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler instance.onPasswordExpiring(instance.mContext); 862a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler break; 863a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler } 864a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler } 865a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler 866a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler /** 8673d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Device Policy administrator. This is primarily a listener for device state changes. 8683d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler * Note: This is instantiated by incoming messages. 869a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * Note: This is actually a BroadcastReceiver and must remain within the guidelines required 870a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler * for proper behavior, including avoidance of ANRs. 8715893e9e008d46cf4be0c7f709a6e77e2652c3dddAndrew Stadler * Note: We do not implement onPasswordFailed() because the default behavior of the 8725893e9e008d46cf4be0c7f709a6e77e2652c3dddAndrew Stadler * DevicePolicyManager - complete local wipe after 'n' failures - is sufficient. 8733d2b3b3b3554be2ac23d9a49fee00faa9693e857Andrew Stadler */ 8744ae83c58b3e136b4b1e859ee304ad1b332e9597fDianne Hackborn public static class PolicyAdmin extends DeviceAdminReceiver { 875345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler 876345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 877345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Called after the administrator is first enabled. 878345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 879345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler @Override 880345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public void onEnabled(Context context, Intent intent) { 881a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler EmailBroadcastProcessorService.processDevicePolicyMessage(context, 882a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler DEVICE_ADMIN_MESSAGE_ENABLED); 883345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 884856e09d76ab62272e660fd4a08e25637f17319a0Andrew Stadler 885345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 886345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Called prior to the administrator being disabled. 887345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 888345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler @Override 889345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public void onDisabled(Context context, Intent intent) { 890a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler EmailBroadcastProcessorService.processDevicePolicyMessage(context, 891a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler DEVICE_ADMIN_MESSAGE_DISABLED); 892345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 893856e09d76ab62272e660fd4a08e25637f17319a0Andrew Stadler 894345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler /** 89502d59d21949a77c60859b615312f02e6d8003490Marc Blank * Called when the user asks to disable administration; we return a warning string that 89602d59d21949a77c60859b615312f02e6d8003490Marc Blank * will be presented to the user 89702d59d21949a77c60859b615312f02e6d8003490Marc Blank */ 89802d59d21949a77c60859b615312f02e6d8003490Marc Blank @Override 89902d59d21949a77c60859b615312f02e6d8003490Marc Blank public CharSequence onDisableRequested(Context context, Intent intent) { 90002d59d21949a77c60859b615312f02e6d8003490Marc Blank return context.getString(R.string.disable_admin_warning); 90102d59d21949a77c60859b615312f02e6d8003490Marc Blank } 90202d59d21949a77c60859b615312f02e6d8003490Marc Blank 90302d59d21949a77c60859b615312f02e6d8003490Marc Blank /** 904345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler * Called after the user has changed their password. 905345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler */ 906345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler @Override 907345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler public void onPasswordChanged(Context context, Intent intent) { 908a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler EmailBroadcastProcessorService.processDevicePolicyMessage(context, 909a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler DEVICE_ADMIN_MESSAGE_PASSWORD_CHANGED); 9101ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 9111ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 9121ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 9131ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * Called when device password is expiring 9141ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 9151ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler @Override 9161ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler public void onPasswordExpiring(Context context, Intent intent) { 917a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler EmailBroadcastProcessorService.processDevicePolicyMessage(context, 918a2269e84c6134bfd3506e5489c7ccfd60c32d41fAndy Stadler DEVICE_ADMIN_MESSAGE_PASSWORD_EXPIRING); 919345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 920345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler } 921345fb8b737c1632fb2a7e69ac44b8612be6237edAndrew Stadler} 922