SecurityPolicy.java revision 9e2ddca59d048fc9ac55278b193ee36b330a7981
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 = 31; 455 // bits 5..8: password mode 456 private static final int PASSWORD_MODE_SHIFT = 5; 457 private static final int PASSWORD_MODE_MASK = 15 << PASSWORD_MODE_SHIFT; 458 public static final int PASSWORD_MODE_NONE = 0 << PASSWORD_MODE_SHIFT; 459 public static final int PASSWORD_MODE_SIMPLE = 1 << PASSWORD_MODE_SHIFT; 460 public static final int PASSWORD_MODE_STRONG = 2 << PASSWORD_MODE_SHIFT; 461 // bits 9..13: password failures -> wipe device (0=disabled) 462 private static final int PASSWORD_MAX_FAILS_SHIFT = 9; 463 private static final int PASSWORD_MAX_FAILS_MASK = 31 << PASSWORD_MAX_FAILS_SHIFT; 464 public static final int PASSWORD_MAX_FAILS_MAX = 31; 465 // bits 14..24: seconds to screen lock (0=not required) 466 private static final int SCREEN_LOCK_TIME_SHIFT = 14; 467 private static final int SCREEN_LOCK_TIME_MASK = 2047 << SCREEN_LOCK_TIME_SHIFT; 468 public static final int SCREEN_LOCK_TIME_MAX = 2047; 469 // bit 25: remote wipe capability required 470 private static final int REQUIRE_REMOTE_WIPE = 1 << 25; 471 472 public final int mMinPasswordLength; 473 public final int mPasswordMode; 474 public final int mMaxPasswordFails; 475 public final int mMaxScreenLockTime; 476 public final boolean mRequireRemoteWipe; 477 478 /** 479 * Create from raw values. 480 * @param minPasswordLength (0=not enforced) 481 * @param passwordMode 482 * @param maxPasswordFails (0=not enforced) 483 * @param maxScreenLockTime in seconds (0=not enforced) 484 * @param requireRemoteWipe 485 * @throws IllegalArgumentException when any arguments are outside of legal ranges. 486 */ 487 public PolicySet(int minPasswordLength, int passwordMode, int maxPasswordFails, 488 int maxScreenLockTime, boolean requireRemoteWipe) throws IllegalArgumentException { 489 if (minPasswordLength > PASSWORD_LENGTH_MAX) { 490 throw new IllegalArgumentException("password length"); 491 } 492 if (passwordMode < PASSWORD_MODE_NONE 493 || passwordMode > PASSWORD_MODE_STRONG) { 494 throw new IllegalArgumentException("password mode"); 495 } 496 if (maxPasswordFails > PASSWORD_MAX_FAILS_MAX) { 497 throw new IllegalArgumentException("password max fails"); 498 } 499 if (maxScreenLockTime > SCREEN_LOCK_TIME_MAX) { 500 throw new IllegalArgumentException("max screen lock time"); 501 } 502 503 mMinPasswordLength = minPasswordLength; 504 mPasswordMode = passwordMode; 505 mMaxPasswordFails = maxPasswordFails; 506 mMaxScreenLockTime = maxScreenLockTime; 507 mRequireRemoteWipe = requireRemoteWipe; 508 } 509 510 /** 511 * Create from values encoded in an account 512 * @param account 513 */ 514 public PolicySet(Account account) { 515 this(account.mSecurityFlags); 516 } 517 518 /** 519 * Create from values encoded in an account flags int 520 */ 521 public PolicySet(int flags) { 522 mMinPasswordLength = 523 (flags & PASSWORD_LENGTH_MASK) >> PASSWORD_LENGTH_SHIFT; 524 mPasswordMode = 525 (flags & PASSWORD_MODE_MASK); 526 mMaxPasswordFails = 527 (flags & PASSWORD_MAX_FAILS_MASK) >> PASSWORD_MAX_FAILS_SHIFT; 528 mMaxScreenLockTime = 529 (flags & SCREEN_LOCK_TIME_MASK) >> SCREEN_LOCK_TIME_SHIFT; 530 mRequireRemoteWipe = 0 != (flags & REQUIRE_REMOTE_WIPE); 531 } 532 533 /** 534 * Helper to map our internal encoding to DevicePolicyManager password modes. 535 */ 536 public int getDPManagerPasswordQuality() { 537 switch (mPasswordMode) { 538 case PASSWORD_MODE_SIMPLE: 539 return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 540 case PASSWORD_MODE_STRONG: 541 return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; 542 default: 543 return DevicePolicyManager .PASSWORD_QUALITY_UNSPECIFIED; 544 } 545 } 546 547 /** 548 * Record flags (and a sync key for the flags) into an Account 549 * Note: the hash code is defined as the encoding used in Account 550 * 551 * @param account to write the values mSecurityFlags and mSecuritySyncKey 552 * @param syncKey the value to write into the account's mSecuritySyncKey 553 * @param update if true, also writes the account back to the provider (updating only 554 * the fields changed by this API) 555 * @param context a context for writing to the provider 556 * @return true if the actual policies changed, false if no change (note, sync key 557 * does not affect this) 558 */ 559 public boolean writeAccount(Account account, String syncKey, boolean update, 560 Context context) { 561 int newFlags = hashCode(); 562 boolean dirty = (newFlags != account.mSecurityFlags); 563 account.mSecurityFlags = newFlags; 564 account.mSecuritySyncKey = syncKey; 565 if (update) { 566 if (account.isSaved()) { 567 ContentValues cv = new ContentValues(); 568 cv.put(AccountColumns.SECURITY_FLAGS, account.mSecurityFlags); 569 cv.put(AccountColumns.SECURITY_SYNC_KEY, account.mSecuritySyncKey); 570 account.update(context, cv); 571 } else { 572 account.save(context); 573 } 574 } 575 return dirty; 576 } 577 578 @Override 579 public boolean equals(Object o) { 580 if (o instanceof PolicySet) { 581 PolicySet other = (PolicySet)o; 582 return (this.mMinPasswordLength == other.mMinPasswordLength) 583 && (this.mPasswordMode == other.mPasswordMode) 584 && (this.mMaxPasswordFails == other.mMaxPasswordFails) 585 && (this.mMaxScreenLockTime == other.mMaxScreenLockTime) 586 && (this.mRequireRemoteWipe == other.mRequireRemoteWipe); 587 } 588 return false; 589 } 590 591 /** 592 * Note: the hash code is defined as the encoding used in Account 593 */ 594 @Override 595 public int hashCode() { 596 int flags = 0; 597 flags = mMinPasswordLength << PASSWORD_LENGTH_SHIFT; 598 flags |= mPasswordMode; 599 flags |= mMaxPasswordFails << PASSWORD_MAX_FAILS_SHIFT; 600 flags |= mMaxScreenLockTime << SCREEN_LOCK_TIME_SHIFT; 601 if (mRequireRemoteWipe) { 602 flags |= REQUIRE_REMOTE_WIPE; 603 } 604 return flags; 605 } 606 607 @Override 608 public String toString() { 609 return "{ " + "pw-len-min=" + mMinPasswordLength + " pw-mode=" + mPasswordMode 610 + " pw-fails-max=" + mMaxPasswordFails + " screenlock-max=" 611 + mMaxScreenLockTime + " remote-wipe-req=" + mRequireRemoteWipe + "}"; 612 } 613 } 614 615 /** 616 * If we are not the active device admin, try to become so. 617 * 618 * @return true if we are already active, false if we are not 619 */ 620 public boolean isActiveAdmin() { 621 DevicePolicyManager dpm = getDPM(); 622 return dpm.isAdminActive(mAdminName); 623 } 624 625 /** 626 * Report admin component name - for making calls into device policy manager 627 */ 628 public ComponentName getAdminComponent() { 629 return mAdminName; 630 } 631 632 /** 633 * Internal handler for enabled/disabled transitions. Handles DeviceAdmin.onEnabled and 634 * and DeviceAdmin.onDisabled. 635 */ 636 /* package */ void onAdminEnabled(boolean isEnabled) { 637 if (isEnabled && !mAdminEnabled) { 638 // TODO: transition to enabled state 639 } else if (!isEnabled && mAdminEnabled) { 640 // transition to disabled state 641 // Response: clear *all* security state information from the accounts, forcing 642 // them back to the initial configurations requiring policy administration 643 ContentValues cv = new ContentValues(); 644 cv.put(AccountColumns.SECURITY_FLAGS, 0); 645 cv.putNull(AccountColumns.SECURITY_SYNC_KEY); 646 mContext.getContentResolver().update(Account.CONTENT_URI, cv, null, null); 647 updatePolicies(-1); 648 } 649 mAdminEnabled = isEnabled; 650 } 651 652 /** 653 * Device Policy administrator. This is primarily a listener for device state changes. 654 * Note: This is instantiated by incoming messages. 655 * Note: We do not implement onPasswordFailed() because the default behavior of the 656 * DevicePolicyManager - complete local wipe after 'n' failures - is sufficient. 657 */ 658 public static class PolicyAdmin extends DeviceAdminReceiver { 659 660 /** 661 * Called after the administrator is first enabled. 662 */ 663 @Override 664 public void onEnabled(Context context, Intent intent) { 665 SecurityPolicy.getInstance(context).onAdminEnabled(true); 666 } 667 668 /** 669 * Called prior to the administrator being disabled. 670 */ 671 @Override 672 public void onDisabled(Context context, Intent intent) { 673 SecurityPolicy.getInstance(context).onAdminEnabled(false); 674 } 675 676 /** 677 * Called after the user has changed their password. 678 */ 679 @Override 680 public void onPasswordChanged(Context context, Intent intent) { 681 SecurityPolicy.getInstance(context).clearAccountHoldFlags(); 682 } 683 } 684} 685