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