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