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