• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     @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 
Policy()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 
restorePolicyWithId(Context context, long id)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 
getAccountIdWithPolicyKey(Context context, long id)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 
addPolicyStringToList(String policyString, ArrayList<String> policyList)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
save(Context context)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      */
setAttachmentFlagsForNewPolicy(Context context, Account account, Policy policy)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      */
normalize()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
equals(Object other)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
hashCode()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
restore(Cursor cursor)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
toContentValues()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      */
getDPManagerPasswordQuality()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      */
getDPManagerPasswordExpirationTimeout()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 
appendPolicy(StringBuilder sb, String code, int value)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
toString()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
describeContents()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
writeToParcel(Parcel dest, int flags)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      */
Policy(Parcel in)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 }