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