Policy.java revision e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98
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    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 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        public Policy createFromParcel(Parcel in) {
448            return new Policy(in);
449        }
450
451        public Policy[] newArray(int size) {
452            return new Policy[size];
453        }
454    };
455
456    /**
457     * Supports Parcelable
458     */
459    @Override
460    public void writeToParcel(Parcel dest, int flags) {
461        // mBaseUri is not parceled
462        dest.writeLong(mId);
463        dest.writeInt(mPasswordMode);
464        dest.writeInt(mPasswordMinLength);
465        dest.writeInt(mPasswordMaxFails);
466        dest.writeInt(mPasswordHistory);
467        dest.writeInt(mPasswordExpirationDays);
468        dest.writeInt(mPasswordComplexChars);
469        dest.writeInt(mMaxScreenLockTime);
470        dest.writeInt(mRequireRemoteWipe ? 1 : 0);
471        dest.writeInt(mRequireEncryption ? 1 : 0);
472        dest.writeInt(mRequireEncryptionExternal ? 1 : 0);
473        dest.writeInt(mRequireManualSyncWhenRoaming ? 1 : 0);
474        dest.writeInt(mDontAllowCamera ? 1 : 0);
475        dest.writeInt(mDontAllowAttachments ? 1 : 0);
476        dest.writeInt(mDontAllowHtml ? 1 : 0);
477        dest.writeInt(mMaxAttachmentSize);
478        dest.writeInt(mMaxTextTruncationSize);
479        dest.writeInt(mMaxHtmlTruncationSize);
480        dest.writeInt(mMaxEmailLookback);
481        dest.writeInt(mMaxCalendarLookback);
482        dest.writeInt(mPasswordRecoveryEnabled ? 1 : 0);
483        dest.writeString(mProtocolPoliciesEnforced);
484        dest.writeString(mProtocolPoliciesUnsupported);
485    }
486
487    /**
488     * Supports Parcelable
489     */
490    public Policy(Parcel in) {
491        mBaseUri = CONTENT_URI;
492        mId = in.readLong();
493        mPasswordMode = in.readInt();
494        mPasswordMinLength = in.readInt();
495        mPasswordMaxFails = in.readInt();
496        mPasswordHistory = in.readInt();
497        mPasswordExpirationDays = in.readInt();
498        mPasswordComplexChars = in.readInt();
499        mMaxScreenLockTime = in.readInt();
500        mRequireRemoteWipe = in.readInt() == 1;
501        mRequireEncryption = in.readInt() == 1;
502        mRequireEncryptionExternal = in.readInt() == 1;
503        mRequireManualSyncWhenRoaming = in.readInt() == 1;
504        mDontAllowCamera = in.readInt() == 1;
505        mDontAllowAttachments = in.readInt() == 1;
506        mDontAllowHtml = in.readInt() == 1;
507        mMaxAttachmentSize = in.readInt();
508        mMaxTextTruncationSize = in.readInt();
509        mMaxHtmlTruncationSize = in.readInt();
510        mMaxEmailLookback = in.readInt();
511        mMaxCalendarLookback = in.readInt();
512        mPasswordRecoveryEnabled = in.readInt() == 1;
513        mProtocolPoliciesEnforced = in.readString();
514        mProtocolPoliciesUnsupported = in.readString();
515    }
516}