1 /* 2 * Copyright (C) 2011 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 17 package com.android.emailcommon.provider; 18 import android.app.admin.DevicePolicyManager; 19 import android.content.ContentProviderOperation; 20 import android.content.ContentResolver; 21 import android.content.ContentUris; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.content.OperationApplicationException; 25 import android.database.Cursor; 26 import android.net.Uri; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 import com.android.emailcommon.utility.Utility; 33 import com.google.common.annotations.VisibleForTesting; 34 35 import java.util.ArrayList; 36 37 /** 38 * The Policy class represents a set of security requirements that are associated with an Account. 39 * The requirements may be either device-specific (e.g. password) or application-specific (e.g. 40 * a limit on the sync window for the Account) 41 */ 42 public final class Policy extends EmailContent implements EmailContent.PolicyColumns, Parcelable { 43 public static final boolean DEBUG_POLICY = false; // DO NOT SUBMIT WITH THIS SET TO TRUE 44 public static final String TAG = "Email/Policy"; 45 46 public static final String TABLE_NAME = "Policy"; 47 @SuppressWarnings("hiding") 48 public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/policy"); 49 50 /* Convert days to mSec (used for password expiration) */ 51 private static final long DAYS_TO_MSEC = 24 * 60 * 60 * 1000; 52 /* Small offset (2 minutes) added to policy expiration to make user testing easier. */ 53 private static final long EXPIRATION_OFFSET_MSEC = 2 * 60 * 1000; 54 55 public static final int PASSWORD_MODE_NONE = 0; 56 public static final int PASSWORD_MODE_SIMPLE = 1; 57 public static final int PASSWORD_MODE_STRONG = 2; 58 59 public int mPasswordMode; 60 public int mPasswordMinLength; 61 public int mPasswordMaxFails; 62 public int mPasswordExpirationDays; 63 public int mPasswordHistory; 64 public int mPasswordComplexChars; 65 public int mMaxScreenLockTime; 66 public boolean mRequireRemoteWipe; 67 public boolean mRequireEncryption; 68 public boolean mRequireEncryptionExternal; 69 public boolean mRequireManualSyncWhenRoaming; 70 public boolean mDontAllowCamera; 71 public boolean mDontAllowAttachments; 72 public boolean mDontAllowHtml; 73 public int mMaxAttachmentSize; 74 public int mMaxTextTruncationSize; 75 public int mMaxHtmlTruncationSize; 76 public int mMaxEmailLookback; 77 public int mMaxCalendarLookback; 78 public boolean mPasswordRecoveryEnabled; 79 80 public static final int CONTENT_ID_COLUMN = 0; 81 public static final int CONTENT_PASSWORD_MODE_COLUMN = 1; 82 public static final int CONTENT_PASSWORD_MIN_LENGTH_COLUMN = 2; 83 public static final int CONTENT_PASSWORD_EXPIRATION_DAYS_COLUMN = 3; 84 public static final int CONTENT_PASSWORD_HISTORY_COLUMN = 4; 85 public static final int CONTENT_PASSWORD_COMPLEX_CHARS_COLUMN = 5; 86 public static final int CONTENT_PASSWORD_MAX_FAILS_COLUMN = 6; 87 public static final int CONTENT_MAX_SCREEN_LOCK_TIME_COLUMN = 7; 88 public static final int CONTENT_REQUIRE_REMOTE_WIPE_COLUMN = 8; 89 public static final int CONTENT_REQUIRE_ENCRYPTION_COLUMN = 9; 90 public static final int CONTENT_REQUIRE_ENCRYPTION_EXTERNAL_COLUMN = 10; 91 public static final int CONTENT_REQUIRE_MANUAL_SYNC_WHEN_ROAMING = 11; 92 public static final int CONTENT_DONT_ALLOW_CAMERA_COLUMN = 12; 93 public static final int CONTENT_DONT_ALLOW_ATTACHMENTS_COLUMN = 13; 94 public static final int CONTENT_DONT_ALLOW_HTML_COLUMN = 14; 95 public static final int CONTENT_MAX_ATTACHMENT_SIZE_COLUMN = 15; 96 public static final int CONTENT_MAX_TEXT_TRUNCATION_SIZE_COLUMN = 16; 97 public static final int CONTENT_MAX_HTML_TRUNCATION_SIZE_COLUMN = 17; 98 public static final int CONTENT_MAX_EMAIL_LOOKBACK_COLUMN = 18; 99 public static final int CONTENT_MAX_CALENDAR_LOOKBACK_COLUMN = 19; 100 public static final int CONTENT_PASSWORD_RECOVERY_ENABLED_COLUMN = 20; 101 102 public static final String[] CONTENT_PROJECTION = new String[] {RECORD_ID, 103 PolicyColumns.PASSWORD_MODE, PolicyColumns.PASSWORD_MIN_LENGTH, 104 PolicyColumns.PASSWORD_EXPIRATION_DAYS, PolicyColumns.PASSWORD_HISTORY, 105 PolicyColumns.PASSWORD_COMPLEX_CHARS, PolicyColumns.PASSWORD_MAX_FAILS, 106 PolicyColumns.MAX_SCREEN_LOCK_TIME, PolicyColumns.REQUIRE_REMOTE_WIPE, 107 PolicyColumns.REQUIRE_ENCRYPTION, PolicyColumns.REQUIRE_ENCRYPTION_EXTERNAL, 108 PolicyColumns.REQUIRE_MANUAL_SYNC_WHEN_ROAMING, PolicyColumns.DONT_ALLOW_CAMERA, 109 PolicyColumns.DONT_ALLOW_ATTACHMENTS, PolicyColumns.DONT_ALLOW_HTML, 110 PolicyColumns.MAX_ATTACHMENT_SIZE, PolicyColumns.MAX_TEXT_TRUNCATION_SIZE, 111 PolicyColumns.MAX_HTML_TRUNCATION_SIZE, PolicyColumns.MAX_EMAIL_LOOKBACK, 112 PolicyColumns.MAX_CALENDAR_LOOKBACK, PolicyColumns.PASSWORD_RECOVERY_ENABLED 113 }; 114 115 public static final Policy NO_POLICY = new Policy(); 116 117 private static final String[] ATTACHMENT_RESET_PROJECTION = 118 new String[] {EmailContent.RECORD_ID, AttachmentColumns.SIZE, AttachmentColumns.FLAGS}; 119 private static final int ATTACHMENT_RESET_PROJECTION_ID = 0; 120 private static final int ATTACHMENT_RESET_PROJECTION_SIZE = 1; 121 private static final int ATTACHMENT_RESET_PROJECTION_FLAGS = 2; 122 123 public Policy() { 124 mBaseUri = CONTENT_URI; 125 // By default, the password mode is "none" 126 mPasswordMode = PASSWORD_MODE_NONE; 127 // All server policies require the ability to wipe the device 128 mRequireRemoteWipe = true; 129 } 130 131 public static Policy restorePolicyWithId(Context context, long id) { 132 return EmailContent.restoreContentWithId(context, Policy.class, Policy.CONTENT_URI, 133 Policy.CONTENT_PROJECTION, id); 134 } 135 136 public static long getAccountIdWithPolicyKey(Context context, long id) { 137 return Utility.getFirstRowLong(context, Account.CONTENT_URI, Account.ID_PROJECTION, 138 AccountColumns.POLICY_KEY + "=?", new String[] {Long.toString(id)}, null, 139 Account.ID_PROJECTION_COLUMN, Account.NO_ACCOUNT); 140 } 141 142 // We override this method to insure that we never write invalid policy data to the provider 143 @Override 144 public Uri save(Context context) { 145 normalize(); 146 return super.save(context); 147 } 148 149 public static void clearAccountPolicy(Context context, Account account) { 150 setAccountPolicy(context, account, null, null); 151 } 152 153 /** 154 * Convenience method for {@link #setAccountPolicy(Context, Account, Policy, String)}. 155 */ 156 @VisibleForTesting 157 public static void setAccountPolicy(Context context, long accountId, Policy policy, 158 String securitySyncKey) { 159 setAccountPolicy(context, Account.restoreAccountWithId(context, accountId), 160 policy, securitySyncKey); 161 } 162 163 /** 164 * Set the policy for an account atomically; this also removes any other policy associated with 165 * the account and sets the policy key for the account. If policy is null, the policyKey is 166 * set to 0 and the securitySyncKey to null. Also, update the account object to reflect the 167 * current policyKey and securitySyncKey 168 * @param context the caller's context 169 * @param account the account whose policy is to be set 170 * @param policy the policy to set, or null if we're clearing the policy 171 * @param securitySyncKey the security sync key for this account (ignored if policy is null) 172 */ 173 public static void setAccountPolicy(Context context, Account account, Policy policy, 174 String securitySyncKey) { 175 if (DEBUG_POLICY) { 176 Log.d(TAG, "Set policy for account " + account.mDisplayName + ": " + 177 ((policy == null) ? "none" : policy.toString())); 178 } 179 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 180 181 // Make sure this is a valid policy set 182 if (policy != null) { 183 policy.normalize(); 184 // Add the new policy (no account will yet reference this) 185 ops.add(ContentProviderOperation.newInsert( 186 Policy.CONTENT_URI).withValues(policy.toContentValues()).build()); 187 // Make the policyKey of the account our newly created policy, and set the sync key 188 ops.add(ContentProviderOperation.newUpdate( 189 ContentUris.withAppendedId(Account.CONTENT_URI, account.mId)) 190 .withValueBackReference(AccountColumns.POLICY_KEY, 0) 191 .withValue(AccountColumns.SECURITY_SYNC_KEY, securitySyncKey) 192 .build()); 193 } else { 194 ops.add(ContentProviderOperation.newUpdate( 195 ContentUris.withAppendedId(Account.CONTENT_URI, account.mId)) 196 .withValue(AccountColumns.SECURITY_SYNC_KEY, null) 197 .withValue(AccountColumns.POLICY_KEY, 0) 198 .build()); 199 } 200 201 // Delete the previous policy associated with this account, if any 202 if (account.mPolicyKey > 0) { 203 ops.add(ContentProviderOperation.newDelete( 204 ContentUris.withAppendedId( 205 Policy.CONTENT_URI, account.mPolicyKey)).build()); 206 } 207 208 try { 209 context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops); 210 account.refresh(context); 211 } catch (RemoteException e) { 212 // This is fatal to a remote process 213 throw new IllegalStateException("Exception setting account policy."); 214 } catch (OperationApplicationException e) { 215 // Can't happen; our provider doesn't throw this exception 216 } 217 } 218 219 /** 220 * Review all attachment records for this account, and reset the "don't allow download" flag 221 * as required by the account's new security policies 222 * @param context the caller's context 223 * @param account the account whose attachments need to be reviewed 224 * @param policy the new policy for this account 225 */ 226 public static void setAttachmentFlagsForNewPolicy(Context context, Account account, 227 Policy policy) { 228 // A nasty bit of work; start with all attachments for a given account 229 ContentResolver resolver = context.getContentResolver(); 230 Cursor c = resolver.query(Attachment.CONTENT_URI, ATTACHMENT_RESET_PROJECTION, 231 AttachmentColumns.ACCOUNT_KEY + "=?", new String[] {Long.toString(account.mId)}, 232 null); 233 ContentValues cv = new ContentValues(); 234 try { 235 // Get maximum allowed size (0 if we don't allow attachments at all) 236 int policyMax = policy.mDontAllowAttachments ? 0 : (policy.mMaxAttachmentSize > 0) ? 237 policy.mMaxAttachmentSize : Integer.MAX_VALUE; 238 while (c.moveToNext()) { 239 int flags = c.getInt(ATTACHMENT_RESET_PROJECTION_FLAGS); 240 int size = c.getInt(ATTACHMENT_RESET_PROJECTION_SIZE); 241 boolean wasRestricted = (flags & Attachment.FLAG_POLICY_DISALLOWS_DOWNLOAD) != 0; 242 boolean isRestricted = size > policyMax; 243 if (isRestricted != wasRestricted) { 244 if (isRestricted) { 245 flags |= Attachment.FLAG_POLICY_DISALLOWS_DOWNLOAD; 246 } else { 247 flags &= ~Attachment.FLAG_POLICY_DISALLOWS_DOWNLOAD; 248 } 249 long id = c.getLong(ATTACHMENT_RESET_PROJECTION_ID); 250 cv.put(AttachmentColumns.FLAGS, flags); 251 resolver.update(ContentUris.withAppendedId(Attachment.CONTENT_URI, id), 252 cv, null, null); 253 } 254 } 255 } finally { 256 c.close(); 257 } 258 } 259 260 /** 261 * Normalize the Policy. If the password mode is "none", zero out all password-related fields; 262 * zero out complex characters for simple passwords. 263 */ 264 public void normalize() { 265 if (mPasswordMode == PASSWORD_MODE_NONE) { 266 mPasswordMaxFails = 0; 267 mMaxScreenLockTime = 0; 268 mPasswordMinLength = 0; 269 mPasswordComplexChars = 0; 270 mPasswordHistory = 0; 271 mPasswordExpirationDays = 0; 272 } else { 273 if ((mPasswordMode != PASSWORD_MODE_SIMPLE) && 274 (mPasswordMode != PASSWORD_MODE_STRONG)) { 275 throw new IllegalArgumentException("password mode"); 276 } 277 // If we're only requiring a simple password, set complex chars to zero; note 278 // that EAS can erroneously send non-zero values in this case 279 if (mPasswordMode == PASSWORD_MODE_SIMPLE) { 280 mPasswordComplexChars = 0; 281 } 282 } 283 } 284 285 @Override 286 public boolean equals(Object other) { 287 if (!(other instanceof Policy)) return false; 288 Policy otherPolicy = (Policy)other; 289 if (mRequireEncryption != otherPolicy.mRequireEncryption) return false; 290 if (mRequireEncryptionExternal != otherPolicy.mRequireEncryptionExternal) return false; 291 if (mRequireRemoteWipe != otherPolicy.mRequireRemoteWipe) return false; 292 if (mMaxScreenLockTime != otherPolicy.mMaxScreenLockTime) return false; 293 if (mPasswordComplexChars != otherPolicy.mPasswordComplexChars) return false; 294 if (mPasswordExpirationDays != otherPolicy.mPasswordExpirationDays) return false; 295 if (mPasswordHistory != otherPolicy.mPasswordHistory) return false; 296 if (mPasswordMaxFails != otherPolicy.mPasswordMaxFails) return false; 297 if (mPasswordMinLength != otherPolicy.mPasswordMinLength) return false; 298 if (mPasswordMode != otherPolicy.mPasswordMode) return false; 299 if (mRequireManualSyncWhenRoaming != otherPolicy.mRequireManualSyncWhenRoaming) { 300 return false; 301 } 302 if (mDontAllowCamera != otherPolicy.mDontAllowCamera) return false; 303 if (mDontAllowAttachments != otherPolicy.mDontAllowAttachments) return false; 304 if (mDontAllowHtml != otherPolicy.mDontAllowHtml) return false; 305 if (mMaxAttachmentSize != otherPolicy.mMaxAttachmentSize) return false; 306 if (mMaxTextTruncationSize != otherPolicy.mMaxTextTruncationSize) return false; 307 if (mMaxHtmlTruncationSize != otherPolicy.mMaxHtmlTruncationSize) return false; 308 if (mMaxEmailLookback != otherPolicy.mMaxEmailLookback) return false; 309 if (mMaxCalendarLookback != otherPolicy.mMaxCalendarLookback) return false; 310 if (mPasswordRecoveryEnabled != otherPolicy.mPasswordRecoveryEnabled) return false; 311 return true; 312 } 313 314 @Override 315 public int hashCode() { 316 int code = mRequireEncryption ? 1 : 0; 317 code += (mRequireEncryptionExternal ? 1 : 0) << 1; 318 code += (mRequireRemoteWipe ? 1 : 0) << 2; 319 code += (mMaxScreenLockTime << 3); 320 code += (mPasswordComplexChars << 6); 321 code += (mPasswordExpirationDays << 12); 322 code += (mPasswordHistory << 15); 323 code += (mPasswordMaxFails << 18); 324 code += (mPasswordMinLength << 22); 325 code += (mPasswordMode << 26); 326 // Don't need to include the other fields 327 return code; 328 } 329 330 @Override 331 public void restore(Cursor cursor) { 332 mBaseUri = CONTENT_URI; 333 mId = cursor.getLong(CONTENT_ID_COLUMN); 334 mPasswordMode = cursor.getInt(CONTENT_PASSWORD_MODE_COLUMN); 335 mPasswordMinLength = cursor.getInt(CONTENT_PASSWORD_MIN_LENGTH_COLUMN); 336 mPasswordMaxFails = cursor.getInt(CONTENT_PASSWORD_MAX_FAILS_COLUMN); 337 mPasswordHistory = cursor.getInt(CONTENT_PASSWORD_HISTORY_COLUMN); 338 mPasswordExpirationDays = cursor.getInt(CONTENT_PASSWORD_EXPIRATION_DAYS_COLUMN); 339 mPasswordComplexChars = cursor.getInt(CONTENT_PASSWORD_COMPLEX_CHARS_COLUMN); 340 mMaxScreenLockTime = cursor.getInt(CONTENT_MAX_SCREEN_LOCK_TIME_COLUMN); 341 mRequireRemoteWipe = cursor.getInt(CONTENT_REQUIRE_REMOTE_WIPE_COLUMN) == 1; 342 mRequireEncryption = cursor.getInt(CONTENT_REQUIRE_ENCRYPTION_COLUMN) == 1; 343 mRequireEncryptionExternal = 344 cursor.getInt(CONTENT_REQUIRE_ENCRYPTION_EXTERNAL_COLUMN) == 1; 345 mRequireManualSyncWhenRoaming = 346 cursor.getInt(CONTENT_REQUIRE_MANUAL_SYNC_WHEN_ROAMING) == 1; 347 mDontAllowCamera = cursor.getInt(CONTENT_DONT_ALLOW_CAMERA_COLUMN) == 1; 348 mDontAllowAttachments = cursor.getInt(CONTENT_DONT_ALLOW_ATTACHMENTS_COLUMN) == 1; 349 mDontAllowHtml = cursor.getInt(CONTENT_DONT_ALLOW_HTML_COLUMN) == 1; 350 mMaxAttachmentSize = cursor.getInt(CONTENT_MAX_ATTACHMENT_SIZE_COLUMN); 351 mMaxTextTruncationSize = cursor.getInt(CONTENT_MAX_TEXT_TRUNCATION_SIZE_COLUMN); 352 mMaxHtmlTruncationSize = cursor.getInt(CONTENT_MAX_HTML_TRUNCATION_SIZE_COLUMN); 353 mMaxEmailLookback = cursor.getInt(CONTENT_MAX_EMAIL_LOOKBACK_COLUMN); 354 mMaxCalendarLookback = cursor.getInt(CONTENT_MAX_CALENDAR_LOOKBACK_COLUMN); 355 mPasswordRecoveryEnabled = cursor.getInt(CONTENT_PASSWORD_RECOVERY_ENABLED_COLUMN) == 1; 356 } 357 358 @Override 359 public ContentValues toContentValues() { 360 ContentValues values = new ContentValues(); 361 values.put(PolicyColumns.PASSWORD_MODE, mPasswordMode); 362 values.put(PolicyColumns.PASSWORD_MIN_LENGTH, mPasswordMinLength); 363 values.put(PolicyColumns.PASSWORD_MAX_FAILS, mPasswordMaxFails); 364 values.put(PolicyColumns.PASSWORD_HISTORY, mPasswordHistory); 365 values.put(PolicyColumns.PASSWORD_EXPIRATION_DAYS, mPasswordExpirationDays); 366 values.put(PolicyColumns.PASSWORD_COMPLEX_CHARS, mPasswordComplexChars); 367 values.put(PolicyColumns.MAX_SCREEN_LOCK_TIME, mMaxScreenLockTime); 368 values.put(PolicyColumns.REQUIRE_REMOTE_WIPE, mRequireRemoteWipe); 369 values.put(PolicyColumns.REQUIRE_ENCRYPTION, mRequireEncryption); 370 values.put(PolicyColumns.REQUIRE_ENCRYPTION_EXTERNAL, mRequireEncryptionExternal); 371 values.put(PolicyColumns.REQUIRE_MANUAL_SYNC_WHEN_ROAMING, mRequireManualSyncWhenRoaming); 372 values.put(PolicyColumns.DONT_ALLOW_CAMERA, mDontAllowCamera); 373 values.put(PolicyColumns.DONT_ALLOW_ATTACHMENTS, mDontAllowAttachments); 374 values.put(PolicyColumns.DONT_ALLOW_HTML, mDontAllowHtml); 375 values.put(PolicyColumns.MAX_ATTACHMENT_SIZE, mMaxAttachmentSize); 376 values.put(PolicyColumns.MAX_TEXT_TRUNCATION_SIZE, mMaxTextTruncationSize); 377 values.put(PolicyColumns.MAX_HTML_TRUNCATION_SIZE, mMaxHtmlTruncationSize); 378 values.put(PolicyColumns.MAX_EMAIL_LOOKBACK, mMaxEmailLookback); 379 values.put(PolicyColumns.MAX_CALENDAR_LOOKBACK, mMaxCalendarLookback); 380 values.put(PolicyColumns.PASSWORD_RECOVERY_ENABLED, mPasswordRecoveryEnabled); 381 return values; 382 } 383 384 /** 385 * Helper to map our internal encoding to DevicePolicyManager password modes. 386 */ 387 public int getDPManagerPasswordQuality() { 388 switch (mPasswordMode) { 389 case PASSWORD_MODE_SIMPLE: 390 return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 391 case PASSWORD_MODE_STRONG: 392 if (mPasswordComplexChars == 0) { 393 return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; 394 } else { 395 return DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; 396 } 397 default: 398 return DevicePolicyManager .PASSWORD_QUALITY_UNSPECIFIED; 399 } 400 } 401 402 /** 403 * Helper to map expiration times to the millisecond values used by DevicePolicyManager. 404 */ 405 public long getDPManagerPasswordExpirationTimeout() { 406 long result = mPasswordExpirationDays * DAYS_TO_MSEC; 407 // Add a small offset to the password expiration. This makes it easier to test 408 // by changing (for example) 1 day to 1 day + 5 minutes. If you set an expiration 409 // that is within the warning period, you should get a warning fairly quickly. 410 if (result > 0) { 411 result += EXPIRATION_OFFSET_MSEC; 412 } 413 return result; 414 } 415 416 private void appendPolicy(StringBuilder sb, String code, int value) { 417 sb.append(code); 418 sb.append(":"); 419 sb.append(value); 420 sb.append(" "); 421 } 422 423 @Override 424 public String toString() { 425 StringBuilder sb = new StringBuilder("["); 426 if (equals(NO_POLICY)) { 427 sb.append("No policies]"); 428 } else { 429 if (mPasswordMode == PASSWORD_MODE_NONE) { 430 sb.append("Pwd none "); 431 } else { 432 appendPolicy(sb, "Pwd strong", mPasswordMode == PASSWORD_MODE_STRONG ? 1 : 0); 433 appendPolicy(sb, "len", mPasswordMinLength); 434 appendPolicy(sb, "cmpx", mPasswordComplexChars); 435 appendPolicy(sb, "expy", mPasswordExpirationDays); 436 appendPolicy(sb, "hist", mPasswordHistory); 437 appendPolicy(sb, "fail", mPasswordMaxFails); 438 appendPolicy(sb, "idle", mMaxScreenLockTime); 439 } 440 if (mRequireEncryption) { 441 sb.append("encrypt "); 442 } 443 if (mRequireEncryptionExternal) { 444 sb.append("encryptsd "); 445 } 446 if (mDontAllowCamera) { 447 sb.append("nocamera "); 448 } 449 if (mDontAllowAttachments) { 450 sb.append("noatts "); 451 } 452 if (mRequireManualSyncWhenRoaming) { 453 sb.append("nopushroam "); 454 } 455 if (mMaxAttachmentSize > 0) { 456 appendPolicy(sb, "attmax", mMaxAttachmentSize); 457 } 458 sb.append("]"); 459 } 460 return sb.toString(); 461 } 462 463 /** 464 * Supports Parcelable 465 */ 466 @Override 467 public int describeContents() { 468 return 0; 469 } 470 471 /** 472 * Supports Parcelable 473 */ 474 public static final Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() { 475 public Policy createFromParcel(Parcel in) { 476 return new Policy(in); 477 } 478 479 public Policy[] newArray(int size) { 480 return new Policy[size]; 481 } 482 }; 483 484 /** 485 * Supports Parcelable 486 */ 487 @Override 488 public void writeToParcel(Parcel dest, int flags) { 489 // mBaseUri is not parceled 490 dest.writeLong(mId); 491 dest.writeInt(mPasswordMode); 492 dest.writeInt(mPasswordMinLength); 493 dest.writeInt(mPasswordMaxFails); 494 dest.writeInt(mPasswordHistory); 495 dest.writeInt(mPasswordExpirationDays); 496 dest.writeInt(mPasswordComplexChars); 497 dest.writeInt(mMaxScreenLockTime); 498 dest.writeInt(mRequireRemoteWipe ? 1 : 0); 499 dest.writeInt(mRequireEncryption ? 1 : 0); 500 dest.writeInt(mRequireEncryptionExternal ? 1 : 0); 501 dest.writeInt(mRequireManualSyncWhenRoaming ? 1 : 0); 502 dest.writeInt(mDontAllowCamera ? 1 : 0); 503 dest.writeInt(mDontAllowAttachments ? 1 : 0); 504 dest.writeInt(mDontAllowHtml ? 1 : 0); 505 dest.writeInt(mMaxAttachmentSize); 506 dest.writeInt(mMaxTextTruncationSize); 507 dest.writeInt(mMaxHtmlTruncationSize); 508 dest.writeInt(mMaxEmailLookback); 509 dest.writeInt(mMaxCalendarLookback); 510 dest.writeInt(mPasswordRecoveryEnabled ? 1 : 0); 511 } 512 513 /** 514 * Supports Parcelable 515 */ 516 public Policy(Parcel in) { 517 mBaseUri = CONTENT_URI; 518 mId = in.readLong(); 519 mPasswordMode = in.readInt(); 520 mPasswordMinLength = in.readInt(); 521 mPasswordMaxFails = in.readInt(); 522 mPasswordHistory = in.readInt(); 523 mPasswordExpirationDays = in.readInt(); 524 mPasswordComplexChars = in.readInt(); 525 mMaxScreenLockTime = in.readInt(); 526 mRequireRemoteWipe = in.readInt() == 1; 527 mRequireEncryption = in.readInt() == 1; 528 mRequireEncryptionExternal = in.readInt() == 1; 529 mRequireManualSyncWhenRoaming = in.readInt() == 1; 530 mDontAllowCamera = in.readInt() == 1; 531 mDontAllowAttachments = in.readInt() == 1; 532 mDontAllowHtml = in.readInt() == 1; 533 mMaxAttachmentSize = in.readInt(); 534 mMaxTextTruncationSize = in.readInt(); 535 mMaxHtmlTruncationSize = in.readInt(); 536 mMaxEmailLookback = in.readInt(); 537 mMaxCalendarLookback = in.readInt(); 538 mPasswordRecoveryEnabled = in.readInt() == 1; 539 } 540 }