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