SecurityPolicy.java revision 968be441b4c253668c4ee1c7a3f8e4b0eb12cf24
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email; 18 19import com.android.email.activity.setup.AccountSecurity; 20import com.android.email.provider.EmailContent; 21import com.android.email.provider.EmailContent.Account; 22import com.android.email.provider.EmailContent.AccountColumns; 23import com.android.email.service.MailService; 24 25import android.app.Notification; 26import android.app.NotificationManager; 27import android.app.PendingIntent; 28import android.app.admin.DeviceAdminReceiver; 29import android.app.admin.DevicePolicyManager; 30import android.content.ComponentName; 31import android.content.ContentResolver; 32import android.content.ContentUris; 33import android.content.ContentValues; 34import android.content.Context; 35import android.content.Intent; 36import android.database.Cursor; 37import android.media.AudioManager; 38import android.net.Uri; 39import android.util.Log; 40 41/** 42 * Utility functions to support reading and writing security policies, and handshaking the device 43 * into and out of various security states. 44 */ 45public class SecurityPolicy { 46 47 private static SecurityPolicy sInstance = null; 48 private Context mContext; 49 private DevicePolicyManager mDPM; 50 private ComponentName mAdminName; 51 private PolicySet mAggregatePolicy; 52 53 /* package */ static final PolicySet NO_POLICY_SET = 54 new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false); 55 56 /** 57 * This projection on Account is for scanning/reading 58 */ 59 private static final String[] ACCOUNT_SECURITY_PROJECTION = new String[] { 60 AccountColumns.ID, AccountColumns.SECURITY_FLAGS 61 }; 62 private static final int ACCOUNT_SECURITY_COLUMN_FLAGS = 1; 63 // Note, this handles the NULL case to deal with older accounts where the column was added 64 private static final String WHERE_ACCOUNT_SECURITY_NONZERO = 65 Account.SECURITY_FLAGS + " IS NOT NULL AND " + Account.SECURITY_FLAGS + "!=0"; 66 67 /** 68 * This projection on Account is for clearing the "security hold" column. Also includes 69 * the security flags column, so we can use it for selecting. 70 */ 71 private static final String[] ACCOUNT_FLAGS_PROJECTION = new String[] { 72 AccountColumns.ID, AccountColumns.FLAGS, AccountColumns.SECURITY_FLAGS 73 }; 74 private static final int ACCOUNT_FLAGS_COLUMN_ID = 0; 75 private static final int ACCOUNT_FLAGS_COLUMN_FLAGS = 1; 76 77 /** 78 * These are hardcoded limits based on knowledge of the current DevicePolicyManager 79 * and screen lock mechanisms. Wherever possible, these should be replaced with queries of 80 * dynamic capabilities of the device (e.g. what password modes are supported?) 81 */ 82 private static final int LIMIT_MIN_PASSWORD_LENGTH = 16; 83 private static final int LIMIT_PASSWORD_MODE = PolicySet.PASSWORD_MODE_STRONG; 84 private static final int LIMIT_SCREENLOCK_TIME = PolicySet.SCREEN_LOCK_TIME_MAX; 85 86 /** 87 * Get the security policy instance 88 */ 89 public synchronized static SecurityPolicy getInstance(Context context) { 90 if (sInstance == null) { 91 sInstance = new SecurityPolicy(context); 92 } 93 return sInstance; 94 } 95 96 /** 97 * Private constructor (one time only) 98 */ 99 private SecurityPolicy(Context context) { 100 mContext = context.getApplicationContext(); 101 mDPM = null; 102 mAdminName = new ComponentName(context, PolicyAdmin.class); 103 mAggregatePolicy = null; 104 } 105 106 /** 107 * For testing only: Inject context into already-created instance 108 */ 109 /* package */ void setContext(Context context) { 110 mContext = context; 111 } 112 113 /** 114 * Compute the aggregate policy for all accounts that require it, and record it. 115 * 116 * The business logic is as follows: 117 * min password length take the max 118 * password mode take the max (strongest mode) 119 * max password fails take the min 120 * max screen lock time take the min 121 * require remote wipe take the max (logical or) 122 * 123 * @return a policy representing the strongest aggregate. If no policy sets are defined, 124 * a lightweight "nothing required" policy will be returned. Never null. 125 */ 126 /* package */ PolicySet computeAggregatePolicy() { 127 boolean policiesFound = false; 128 129 int minPasswordLength = Integer.MIN_VALUE; 130 int passwordMode = Integer.MIN_VALUE; 131 int maxPasswordFails = Integer.MAX_VALUE; 132 int maxScreenLockTime = Integer.MAX_VALUE; 133 boolean requireRemoteWipe = false; 134 135 Cursor c = mContext.getContentResolver().query(Account.CONTENT_URI, 136 ACCOUNT_SECURITY_PROJECTION, WHERE_ACCOUNT_SECURITY_NONZERO, null, null); 137 try { 138 while (c.moveToNext()) { 139 int flags = c.getInt(ACCOUNT_SECURITY_COLUMN_FLAGS); 140 if (flags != 0) { 141 PolicySet p = new PolicySet(flags); 142 minPasswordLength = Math.max(p.mMinPasswordLength, minPasswordLength); 143 passwordMode = Math.max(p.mPasswordMode, passwordMode); 144 if (p.mMaxPasswordFails > 0) { 145 maxPasswordFails = Math.min(p.mMaxPasswordFails, maxPasswordFails); 146 } 147 if (p.mMaxScreenLockTime > 0) { 148 maxScreenLockTime = Math.min(p.mMaxScreenLockTime, maxScreenLockTime); 149 } 150 requireRemoteWipe |= p.mRequireRemoteWipe; 151 policiesFound = true; 152 } 153 } 154 } finally { 155 c.close(); 156 } 157 if (policiesFound) { 158 // final cleanup pass converts any untouched min/max values to zero (not specified) 159 if (minPasswordLength == Integer.MIN_VALUE) minPasswordLength = 0; 160 if (passwordMode == Integer.MIN_VALUE) passwordMode = 0; 161 if (maxPasswordFails == Integer.MAX_VALUE) maxPasswordFails = 0; 162 if (maxScreenLockTime == Integer.MAX_VALUE) maxScreenLockTime = 0; 163 164 return new PolicySet(minPasswordLength, passwordMode, maxPasswordFails, 165 maxScreenLockTime, requireRemoteWipe); 166 } else { 167 return NO_POLICY_SET; 168 } 169 } 170 171 /** 172 * Return updated aggregate policy, from cached value if possible 173 */ 174 public synchronized PolicySet getAggregatePolicy() { 175 if (mAggregatePolicy == null) { 176 mAggregatePolicy = computeAggregatePolicy(); 177 } 178 return mAggregatePolicy; 179 } 180 181 /** 182 * Get the dpm. This mainly allows us to make some utility calls without it, for testing. 183 */ 184 private synchronized DevicePolicyManager getDPM() { 185 if (mDPM == null) { 186 mDPM = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 187 } 188 return mDPM; 189 } 190 191 /** 192 * API: Query used to determine if a given policy is "possible" (irrespective of current 193 * device state. This is used when creating new accounts. 194 * 195 * TODO: This is hardcoded based on knowledge of the current DevicePolicyManager 196 * and screen lock mechanisms. It would be nice to replace these tests with something 197 * more dynamic. 198 * 199 * @param policies the policies requested 200 * @return true if the policies are supported, false if not supported 201 */ 202 public boolean isSupported(PolicySet policies) { 203 if (policies.mMinPasswordLength > LIMIT_MIN_PASSWORD_LENGTH) { 204 return false; 205 } 206 if (policies.mPasswordMode > LIMIT_PASSWORD_MODE ) { 207 return false; 208 } 209 // No limit on password fail count 210 if (policies.mMaxScreenLockTime > LIMIT_SCREENLOCK_TIME ) { 211 return false; 212 } 213 // No limit on remote wipe capable 214 215 return true; 216 } 217 218 /** 219 * API: Report that policies may have been updated due to rewriting values in an Account. 220 * @param accountId the account that has been updated, -1 if unknown/deleted 221 */ 222 public synchronized void updatePolicies(long accountId) { 223 mAggregatePolicy = null; 224 } 225 226 /** 227 * API: Report that policies may have been updated *and* the caller vouches that the 228 * change is a reduction in policies. This forces an immediate change to device state. 229 * Typically used when deleting accounts, although we may use it for server-side policy 230 * rollbacks. 231 */ 232 public void reducePolicies() { 233 updatePolicies(-1); 234 setActivePolicies(); 235 } 236 237 /** 238 * API: Query used to determine if a given policy is "active" (the device is operating at 239 * the required security level). 240 * 241 * This can be used when syncing a specific account, by passing a specific set of policies 242 * for that account. Or, it can be used at any time to compare the device 243 * state against the aggregate set of device policies stored in all accounts. 244 * 245 * This method is for queries only, and does not trigger any change in device state. 246 * 247 * @param policies the policies requested, or null to check aggregate stored policies 248 * @return true if the policies are active, false if not active 249 */ 250 public boolean isActive(PolicySet policies) { 251 // select aggregate set if needed 252 if (policies == null) { 253 policies = getAggregatePolicy(); 254 } 255 // quick check for the "empty set" of no policies 256 if (policies == NO_POLICY_SET) { 257 return true; 258 } 259 DevicePolicyManager dpm = getDPM(); 260 if (dpm.isAdminActive(mAdminName)) { 261 // check each policy explicitly 262 if (policies.mMinPasswordLength > 0) { 263 if (dpm.getPasswordMinimumLength(mAdminName) < policies.mMinPasswordLength) { 264 return false; 265 } 266 } 267 if (policies.mPasswordMode > 0) { 268 if (dpm.getPasswordQuality(mAdminName) < policies.getDPManagerPasswordQuality()) { 269 return false; 270 } 271 if (!dpm.isActivePasswordSufficient()) { 272 return false; 273 } 274 } 275 if (policies.mMaxScreenLockTime > 0) { 276 // Note, we use seconds, dpm uses milliseconds 277 if (dpm.getMaximumTimeToLock(mAdminName) > policies.mMaxScreenLockTime * 1000) { 278 return false; 279 } 280 } 281 // password failures are counted locally - no test required here 282 // no check required for remote wipe (it's supported, if we're the admin) 283 284 // making it this far means we passed! 285 return true; 286 } 287 // return false, not active 288 return false; 289 } 290 291 /** 292 * Set the requested security level based on the aggregate set of requests. 293 * If the set is empty, we release our device administration. If the set is non-empty, 294 * we only proceed if we are already active as an admin. 295 */ 296 public void setActivePolicies() { 297 DevicePolicyManager dpm = getDPM(); 298 // compute aggregate set of policies 299 PolicySet policies = getAggregatePolicy(); 300 // if empty set, detach from policy manager 301 if (policies == NO_POLICY_SET) { 302 dpm.removeActiveAdmin(mAdminName); 303 } else if (dpm.isAdminActive(mAdminName)) { 304 // set each policy in the policy manager 305 // password mode & length 306 dpm.setPasswordQuality(mAdminName, policies.getDPManagerPasswordQuality()); 307 dpm.setPasswordMinimumLength(mAdminName, policies.mMinPasswordLength); 308 // screen lock time 309 dpm.setMaximumTimeToLock(mAdminName, policies.mMaxScreenLockTime * 1000); 310 // local wipe (failed passwords limit) 311 dpm.setMaximumFailedPasswordsForWipe(mAdminName, policies.mMaxPasswordFails); 312 } 313 } 314 315 /** 316 * API: Set/Clear the "hold" flag in any account. This flag serves a dual purpose: 317 * Setting it gives us an indication that it was blocked, and clearing it gives EAS a 318 * signal to try syncing again. 319 */ 320 public void setAccountHoldFlag(Account account, boolean newState) { 321 if (newState) { 322 account.mFlags |= Account.FLAGS_SECURITY_HOLD; 323 } else { 324 account.mFlags &= ~Account.FLAGS_SECURITY_HOLD; 325 } 326 ContentValues cv = new ContentValues(); 327 cv.put(AccountColumns.FLAGS, account.mFlags); 328 account.update(mContext, cv); 329 } 330 331 /** 332 * Clear all account hold flags that are set. This will trigger watchers, and in particular 333 * will cause EAS to try and resync the account(s). 334 */ 335 public void clearAccountHoldFlags() { 336 ContentResolver resolver = mContext.getContentResolver(); 337 Cursor c = resolver.query(Account.CONTENT_URI, ACCOUNT_FLAGS_PROJECTION, 338 WHERE_ACCOUNT_SECURITY_NONZERO, null, null); 339 try { 340 while (c.moveToNext()) { 341 int flags = c.getInt(ACCOUNT_FLAGS_COLUMN_FLAGS); 342 if (0 != (flags & Account.FLAGS_SECURITY_HOLD)) { 343 ContentValues cv = new ContentValues(); 344 cv.put(AccountColumns.FLAGS, flags & ~Account.FLAGS_SECURITY_HOLD); 345 long accountId = c.getLong(ACCOUNT_FLAGS_COLUMN_ID); 346 Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId); 347 resolver.update(uri, cv, null, null); 348 } 349 } 350 } finally { 351 c.close(); 352 } 353 } 354 355 /** 356 * API: Sync service should call this any time a sync fails due to isActive() returning false. 357 * This will kick off the notify-acquire-admin-state process and/or increase the security level. 358 * The caller needs to write the required policies into this account before making this call. 359 * Should not be called from UI thread - uses DB lookups to prepare new notifications 360 * 361 * @param accountId the account for which sync cannot proceed 362 */ 363 public void policiesRequired(long accountId) { 364 Account account = EmailContent.Account.restoreAccountWithId(mContext, accountId); 365 366 // Mark the account as "on hold". 367 setAccountHoldFlag(account, true); 368 369 // Put up a notification 370 String tickerText = mContext.getString(R.string.security_notification_ticker_fmt, 371 account.getDisplayName()); 372 String contentTitle = mContext.getString(R.string.security_notification_content_title); 373 String contentText = account.getDisplayName(); 374 String ringtoneString = account.getRingtone(); 375 Uri ringTone = (ringtoneString == null) ? null : Uri.parse(ringtoneString); 376 boolean vibrate = 0 != (account.mFlags & Account.FLAGS_VIBRATE_ALWAYS); 377 boolean vibrateWhenSilent = 0 != (account.mFlags & Account.FLAGS_VIBRATE_WHEN_SILENT); 378 379 Intent intent = AccountSecurity.actionUpdateSecurityIntent(mContext, accountId); 380 PendingIntent pending = 381 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 382 383 Notification notification = new Notification(R.drawable.stat_notify_email_generic, 384 tickerText, System.currentTimeMillis()); 385 notification.setLatestEventInfo(mContext, contentTitle, contentText, pending); 386 387 // Use the account's notification rules for sound & vibrate (but always notify) 388 AudioManager audioManager = 389 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 390 boolean nowSilent = 391 audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE; 392 notification.sound = ringTone; 393 394 if (vibrate || (vibrateWhenSilent && nowSilent)) { 395 notification.defaults |= Notification.DEFAULT_VIBRATE; 396 } 397 notification.flags |= Notification.FLAG_SHOW_LIGHTS; 398 notification.defaults |= Notification.DEFAULT_LIGHTS; 399 400 NotificationManager notificationManager = 401 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 402 notificationManager.notify(MailService.NOTIFICATION_ID_SECURITY_NEEDED, notification); 403 } 404 405 /** 406 * Called from the notification's intent receiver to register that the notification can be 407 * cleared now. 408 */ 409 public void clearNotification(long accountId) { 410 NotificationManager notificationManager = 411 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 412 notificationManager.cancel(MailService.NOTIFICATION_ID_SECURITY_NEEDED); 413 } 414 415 /** 416 * API: Remote wipe (from server). This is final, there is no confirmation. It will only 417 * return to the caller if there is an unexpected failure. 418 */ 419 public void remoteWipe() { 420 DevicePolicyManager dpm = getDPM(); 421 if (dpm.isAdminActive(mAdminName)) { 422 dpm.wipeData(0); 423 } else { 424 Log.d(Email.LOG_TAG, "Could not remote wipe because not device admin."); 425 } 426 } 427 428 /** 429 * Class for tracking policies and reading/writing into accounts 430 */ 431 public static class PolicySet { 432 433 // Security (provisioning) flags 434 // bits 0..4: password length (0=no password required) 435 private static final int PASSWORD_LENGTH_MASK = 31; 436 private static final int PASSWORD_LENGTH_SHIFT = 0; 437 public static final int PASSWORD_LENGTH_MAX = 30; 438 private static final int PASSWORD_LENGTH_EXCEEDED = 31; 439 // bits 5..8: password mode 440 private static final int PASSWORD_MODE_SHIFT = 5; 441 private static final int PASSWORD_MODE_MASK = 15 << PASSWORD_MODE_SHIFT; 442 public static final int PASSWORD_MODE_NONE = 0 << PASSWORD_MODE_SHIFT; 443 public static final int PASSWORD_MODE_SIMPLE = 1 << PASSWORD_MODE_SHIFT; 444 public static final int PASSWORD_MODE_STRONG = 2 << PASSWORD_MODE_SHIFT; 445 // bits 9..13: password failures -> wipe device (0=disabled) 446 private static final int PASSWORD_MAX_FAILS_SHIFT = 9; 447 private static final int PASSWORD_MAX_FAILS_MASK = 31 << PASSWORD_MAX_FAILS_SHIFT; 448 public static final int PASSWORD_MAX_FAILS_MAX = 31; 449 // bits 14..24: seconds to screen lock (0=not required) 450 private static final int SCREEN_LOCK_TIME_SHIFT = 14; 451 private static final int SCREEN_LOCK_TIME_MASK = 2047 << SCREEN_LOCK_TIME_SHIFT; 452 public static final int SCREEN_LOCK_TIME_MAX = 2047; 453 // bit 25: remote wipe capability required 454 private static final int REQUIRE_REMOTE_WIPE = 1 << 25; 455 456 public final int mMinPasswordLength; 457 public final int mPasswordMode; 458 public final int mMaxPasswordFails; 459 public final int mMaxScreenLockTime; 460 public final boolean mRequireRemoteWipe; 461 462 /** 463 * Create from raw values. 464 * @param minPasswordLength (0=not enforced) 465 * @param passwordMode 466 * @param maxPasswordFails (0=not enforced) 467 * @param maxScreenLockTime in seconds (0=not enforced) 468 * @param requireRemoteWipe 469 * @throws IllegalArgumentException for illegal arguments. 470 */ 471 public PolicySet(int minPasswordLength, int passwordMode, int maxPasswordFails, 472 int maxScreenLockTime, boolean requireRemoteWipe) throws IllegalArgumentException { 473 // This value has a hard limit which cannot be supported if exceeded. Setting the 474 // exceeded value will force isSupported() to return false. 475 if (minPasswordLength > PASSWORD_LENGTH_MAX) { 476 minPasswordLength = PASSWORD_LENGTH_EXCEEDED; 477 } 478 if (passwordMode < PASSWORD_MODE_NONE 479 || passwordMode > PASSWORD_MODE_STRONG) { 480 throw new IllegalArgumentException("password mode"); 481 } 482 // This value can be reduced (which actually increases security) if necessary 483 if (maxPasswordFails > PASSWORD_MAX_FAILS_MAX) { 484 maxPasswordFails = PASSWORD_MAX_FAILS_MAX; 485 } 486 // This value can be reduced (which actually increases security) if necessary 487 if (maxScreenLockTime > SCREEN_LOCK_TIME_MAX) { 488 maxScreenLockTime = SCREEN_LOCK_TIME_MAX; 489 } 490 491 mMinPasswordLength = minPasswordLength; 492 mPasswordMode = passwordMode; 493 mMaxPasswordFails = maxPasswordFails; 494 mMaxScreenLockTime = maxScreenLockTime; 495 mRequireRemoteWipe = requireRemoteWipe; 496 } 497 498 /** 499 * Create from values encoded in an account 500 * @param account 501 */ 502 public PolicySet(Account account) { 503 this(account.mSecurityFlags); 504 } 505 506 /** 507 * Create from values encoded in an account flags int 508 */ 509 public PolicySet(int flags) { 510 mMinPasswordLength = 511 (flags & PASSWORD_LENGTH_MASK) >> PASSWORD_LENGTH_SHIFT; 512 mPasswordMode = 513 (flags & PASSWORD_MODE_MASK); 514 mMaxPasswordFails = 515 (flags & PASSWORD_MAX_FAILS_MASK) >> PASSWORD_MAX_FAILS_SHIFT; 516 mMaxScreenLockTime = 517 (flags & SCREEN_LOCK_TIME_MASK) >> SCREEN_LOCK_TIME_SHIFT; 518 mRequireRemoteWipe = 0 != (flags & REQUIRE_REMOTE_WIPE); 519 } 520 521 /** 522 * Helper to map our internal encoding to DevicePolicyManager password modes. 523 */ 524 public int getDPManagerPasswordQuality() { 525 switch (mPasswordMode) { 526 case PASSWORD_MODE_SIMPLE: 527 return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 528 case PASSWORD_MODE_STRONG: 529 return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; 530 default: 531 return DevicePolicyManager .PASSWORD_QUALITY_UNSPECIFIED; 532 } 533 } 534 535 /** 536 * Record flags (and a sync key for the flags) into an Account 537 * Note: the hash code is defined as the encoding used in Account 538 * 539 * @param account to write the values mSecurityFlags and mSecuritySyncKey 540 * @param syncKey the value to write into the account's mSecuritySyncKey 541 * @param update if true, also writes the account back to the provider (updating only 542 * the fields changed by this API) 543 * @param context a context for writing to the provider 544 * @return true if the actual policies changed, false if no change (note, sync key 545 * does not affect this) 546 */ 547 public boolean writeAccount(Account account, String syncKey, boolean update, 548 Context context) { 549 int newFlags = hashCode(); 550 boolean dirty = (newFlags != account.mSecurityFlags); 551 account.mSecurityFlags = newFlags; 552 account.mSecuritySyncKey = syncKey; 553 if (update) { 554 if (account.isSaved()) { 555 ContentValues cv = new ContentValues(); 556 cv.put(AccountColumns.SECURITY_FLAGS, account.mSecurityFlags); 557 cv.put(AccountColumns.SECURITY_SYNC_KEY, account.mSecuritySyncKey); 558 account.update(context, cv); 559 } else { 560 account.save(context); 561 } 562 } 563 return dirty; 564 } 565 566 @Override 567 public boolean equals(Object o) { 568 if (o instanceof PolicySet) { 569 PolicySet other = (PolicySet)o; 570 return (this.mMinPasswordLength == other.mMinPasswordLength) 571 && (this.mPasswordMode == other.mPasswordMode) 572 && (this.mMaxPasswordFails == other.mMaxPasswordFails) 573 && (this.mMaxScreenLockTime == other.mMaxScreenLockTime) 574 && (this.mRequireRemoteWipe == other.mRequireRemoteWipe); 575 } 576 return false; 577 } 578 579 /** 580 * Note: the hash code is defined as the encoding used in Account 581 */ 582 @Override 583 public int hashCode() { 584 int flags = 0; 585 flags = mMinPasswordLength << PASSWORD_LENGTH_SHIFT; 586 flags |= mPasswordMode; 587 flags |= mMaxPasswordFails << PASSWORD_MAX_FAILS_SHIFT; 588 flags |= mMaxScreenLockTime << SCREEN_LOCK_TIME_SHIFT; 589 if (mRequireRemoteWipe) { 590 flags |= REQUIRE_REMOTE_WIPE; 591 } 592 return flags; 593 } 594 595 @Override 596 public String toString() { 597 return "{ " + "pw-len-min=" + mMinPasswordLength + " pw-mode=" + mPasswordMode 598 + " pw-fails-max=" + mMaxPasswordFails + " screenlock-max=" 599 + mMaxScreenLockTime + " remote-wipe-req=" + mRequireRemoteWipe + "}"; 600 } 601 } 602 603 /** 604 * If we are not the active device admin, try to become so. 605 * 606 * @return true if we are already active, false if we are not 607 */ 608 public boolean isActiveAdmin() { 609 DevicePolicyManager dpm = getDPM(); 610 return dpm.isAdminActive(mAdminName); 611 } 612 613 /** 614 * Report admin component name - for making calls into device policy manager 615 */ 616 public ComponentName getAdminComponent() { 617 return mAdminName; 618 } 619 620 /** 621 * Internal handler for enabled->disabled transitions. Resets all security keys 622 * forcing EAS to resync security state. 623 */ 624 /* package */ void onAdminEnabled(boolean isEnabled) { 625 if (!isEnabled) { 626 // transition to disabled state 627 // Response: clear *all* security state information from the accounts, forcing 628 // them back to the initial configurations requiring policy administration 629 ContentValues cv = new ContentValues(); 630 cv.put(AccountColumns.SECURITY_FLAGS, 0); 631 cv.putNull(AccountColumns.SECURITY_SYNC_KEY); 632 mContext.getContentResolver().update(Account.CONTENT_URI, cv, null, null); 633 updatePolicies(-1); 634 } 635 } 636 637 /** 638 * Device Policy administrator. This is primarily a listener for device state changes. 639 * Note: This is instantiated by incoming messages. 640 * Note: We do not implement onPasswordFailed() because the default behavior of the 641 * DevicePolicyManager - complete local wipe after 'n' failures - is sufficient. 642 */ 643 public static class PolicyAdmin extends DeviceAdminReceiver { 644 645 /** 646 * Called after the administrator is first enabled. 647 */ 648 @Override 649 public void onEnabled(Context context, Intent intent) { 650 SecurityPolicy.getInstance(context).onAdminEnabled(true); 651 } 652 653 /** 654 * Called prior to the administrator being disabled. 655 */ 656 @Override 657 public void onDisabled(Context context, Intent intent) { 658 SecurityPolicy.getInstance(context).onAdminEnabled(false); 659 } 660 661 /** 662 * Called after the user has changed their password. 663 */ 664 @Override 665 public void onPasswordChanged(Context context, Intent intent) { 666 SecurityPolicy.getInstance(context).clearAccountHoldFlags(); 667 } 668 } 669} 670