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