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