Merge "Email split, part deux: PolicyService"
This commit is contained in:
commit
e86789b66e
|
@ -20,7 +20,8 @@ LOCAL_MODULE_TAGS := optional
|
|||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_SRC_FILES += \
|
||||
src/com/android/emailcommon/service/IEmailService.aidl \
|
||||
src/com/android/emailcommon/service/IEmailServiceCallback.aidl
|
||||
src/com/android/emailcommon/service/IEmailServiceCallback.aidl \
|
||||
src/com/android/emailcommon/service/IPolicyService.aidl
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := android-common
|
||||
# Revive this when the app is unbundled.
|
||||
|
|
|
@ -358,6 +358,18 @@
|
|||
android:resource="@xml/syncadapter_pop_imap" />
|
||||
</service>
|
||||
|
||||
<!-- Require provider permission to use our Policy and Account services -->
|
||||
<service
|
||||
android:name=".service.PolicyService"
|
||||
android:enabled="true"
|
||||
android:permission="com.android.email.permission.ACCESS_PROVIDER"
|
||||
>
|
||||
<intent-filter>
|
||||
<action
|
||||
android:name="com.android.email.POLICY_INTENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<!--EXCHANGE-REMOVE-SECTION-START-->
|
||||
<!--Required stanza to register the EAS EmailSyncAdapterService with SyncManager -->
|
||||
<service
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.android.email.provider.EmailContent;
|
|||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.email.provider.EmailContent.AccountColumns;
|
||||
import com.android.email.service.EmailBroadcastProcessorService;
|
||||
import com.android.emailcommon.service.PolicySet;
|
||||
|
||||
import android.app.admin.DeviceAdminInfo;
|
||||
import android.app.admin.DeviceAdminReceiver;
|
||||
|
@ -31,8 +32,6 @@ import android.content.ContentValues;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
|
@ -418,12 +417,22 @@ public class SecurityPolicy {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method; see javadoc below
|
||||
*/
|
||||
public static void setAccountHoldFlag(Context context, long accountId, boolean newState) {
|
||||
Account account = Account.restoreAccountWithId(context, accountId);
|
||||
if (account != null) {
|
||||
setAccountHoldFlag(context, account, newState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API: Set/Clear the "hold" flag in any account. This flag serves a dual purpose:
|
||||
* Setting it gives us an indication that it was blocked, and clearing it gives EAS a
|
||||
* signal to try syncing again.
|
||||
* @param context
|
||||
* @param account The account to update
|
||||
* @param account the account whose hold flag is to be set/cleared
|
||||
* @param newState true = security hold, false = free to sync
|
||||
*/
|
||||
public static void setAccountHoldFlag(Context context, Account account, boolean newState) {
|
||||
|
@ -485,336 +494,6 @@ public class SecurityPolicy {
|
|||
Log.d(Email.LOG_TAG, "Could not remote wipe because not device admin.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for tracking policies and reading/writing into accounts
|
||||
*/
|
||||
public static class PolicySet implements Parcelable {
|
||||
|
||||
// Security (provisioning) flags
|
||||
// bits 0..4: password length (0=no password required)
|
||||
private static final int PASSWORD_LENGTH_MASK = 31;
|
||||
private static final int PASSWORD_LENGTH_SHIFT = 0;
|
||||
public static final int PASSWORD_LENGTH_MAX = 30;
|
||||
// bits 5..8: password mode
|
||||
private static final int PASSWORD_MODE_SHIFT = 5;
|
||||
private static final int PASSWORD_MODE_MASK = 15 << PASSWORD_MODE_SHIFT;
|
||||
public static final int PASSWORD_MODE_NONE = 0 << PASSWORD_MODE_SHIFT;
|
||||
public static final int PASSWORD_MODE_SIMPLE = 1 << PASSWORD_MODE_SHIFT;
|
||||
public static final int PASSWORD_MODE_STRONG = 2 << PASSWORD_MODE_SHIFT;
|
||||
// bits 9..13: password failures -> wipe device (0=disabled)
|
||||
private static final int PASSWORD_MAX_FAILS_SHIFT = 9;
|
||||
private static final int PASSWORD_MAX_FAILS_MASK = 31 << PASSWORD_MAX_FAILS_SHIFT;
|
||||
public static final int PASSWORD_MAX_FAILS_MAX = 31;
|
||||
// bits 14..24: seconds to screen lock (0=not required)
|
||||
private static final int SCREEN_LOCK_TIME_SHIFT = 14;
|
||||
private static final int SCREEN_LOCK_TIME_MASK = 2047 << SCREEN_LOCK_TIME_SHIFT;
|
||||
public static final int SCREEN_LOCK_TIME_MAX = 2047;
|
||||
// bit 25: remote wipe capability required
|
||||
private static final int REQUIRE_REMOTE_WIPE = 1 << 25;
|
||||
// bit 26..35: password expiration (days; 0=not required)
|
||||
private static final int PASSWORD_EXPIRATION_SHIFT = 26;
|
||||
private static final long PASSWORD_EXPIRATION_MASK = 1023L << PASSWORD_EXPIRATION_SHIFT;
|
||||
public static final int PASSWORD_EXPIRATION_MAX = 1023;
|
||||
// bit 36..43: password history (length; 0=not required)
|
||||
private static final int PASSWORD_HISTORY_SHIFT = 36;
|
||||
private static final long PASSWORD_HISTORY_MASK = 255L << PASSWORD_HISTORY_SHIFT;
|
||||
public static final int PASSWORD_HISTORY_MAX = 255;
|
||||
// bit 44..48: min complex characters (0=not required)
|
||||
private static final int PASSWORD_COMPLEX_CHARS_SHIFT = 44;
|
||||
private static final long PASSWORD_COMPLEX_CHARS_MASK = 31L << PASSWORD_COMPLEX_CHARS_SHIFT;
|
||||
public static final int PASSWORD_COMPLEX_CHARS_MAX = 31;
|
||||
// bit 49: requires device encryption
|
||||
private static final long REQUIRE_ENCRYPTION = 1L << 49;
|
||||
|
||||
/* Convert days to mSec (used for password expiration) */
|
||||
private static final long DAYS_TO_MSEC = 24 * 60 * 60 * 1000;
|
||||
/* Small offset (2 minutes) added to policy expiration to make user testing easier. */
|
||||
private static final long EXPIRATION_OFFSET_MSEC = 2 * 60 * 1000;
|
||||
|
||||
/*package*/ final int mMinPasswordLength;
|
||||
/*package*/ final int mPasswordMode;
|
||||
/*package*/ final int mMaxPasswordFails;
|
||||
/*package*/ final int mMaxScreenLockTime;
|
||||
/*package*/ final boolean mRequireRemoteWipe;
|
||||
/*package*/ final int mPasswordExpirationDays;
|
||||
/*package*/ final int mPasswordHistory;
|
||||
/*package*/ final int mPasswordComplexChars;
|
||||
/*package*/ final boolean mRequireEncryption;
|
||||
|
||||
public int getMinPasswordLengthForTest() {
|
||||
return mMinPasswordLength;
|
||||
}
|
||||
|
||||
public int getPasswordModeForTest() {
|
||||
return mPasswordMode;
|
||||
}
|
||||
|
||||
public int getMaxPasswordFailsForTest() {
|
||||
return mMaxPasswordFails;
|
||||
}
|
||||
|
||||
public int getMaxScreenLockTimeForTest() {
|
||||
return mMaxScreenLockTime;
|
||||
}
|
||||
|
||||
public boolean isRequireRemoteWipeForTest() {
|
||||
return mRequireRemoteWipe;
|
||||
}
|
||||
|
||||
public boolean isRequireEncryptionForTest() {
|
||||
return mRequireEncryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from raw values.
|
||||
* @param minPasswordLength (0=not enforced)
|
||||
* @param passwordMode
|
||||
* @param maxPasswordFails (0=not enforced)
|
||||
* @param maxScreenLockTime in seconds (0=not enforced)
|
||||
* @param requireRemoteWipe
|
||||
* @param passwordExpirationDays in days (0=not enforced)
|
||||
* @param passwordHistory (0=not enforced)
|
||||
* @param passwordComplexChars (0=not enforced)
|
||||
* @throws IllegalArgumentException for illegal arguments.
|
||||
*/
|
||||
public PolicySet(int minPasswordLength, int passwordMode, int maxPasswordFails,
|
||||
int maxScreenLockTime, boolean requireRemoteWipe, int passwordExpirationDays,
|
||||
int passwordHistory, int passwordComplexChars, boolean requireEncryption)
|
||||
throws IllegalArgumentException {
|
||||
// If we're not enforcing passwords, make sure we clean up related values, since EAS
|
||||
// can send non-zero values for any or all of these
|
||||
if (passwordMode == PASSWORD_MODE_NONE) {
|
||||
maxPasswordFails = 0;
|
||||
maxScreenLockTime = 0;
|
||||
minPasswordLength = 0;
|
||||
passwordComplexChars = 0;
|
||||
passwordHistory = 0;
|
||||
passwordExpirationDays = 0;
|
||||
} else {
|
||||
if ((passwordMode != PASSWORD_MODE_SIMPLE) &&
|
||||
(passwordMode != PASSWORD_MODE_STRONG)) {
|
||||
throw new IllegalArgumentException("password mode");
|
||||
}
|
||||
// If we're only requiring a simple password, set complex chars to zero; note
|
||||
// that EAS can erroneously send non-zero values in this case
|
||||
if (passwordMode == PASSWORD_MODE_SIMPLE) {
|
||||
passwordComplexChars = 0;
|
||||
}
|
||||
// The next four values have hard limits which cannot be supported if exceeded.
|
||||
if (minPasswordLength > PASSWORD_LENGTH_MAX) {
|
||||
throw new IllegalArgumentException("password length");
|
||||
}
|
||||
if (passwordExpirationDays > PASSWORD_EXPIRATION_MAX) {
|
||||
throw new IllegalArgumentException("password expiration");
|
||||
}
|
||||
if (passwordHistory > PASSWORD_HISTORY_MAX) {
|
||||
throw new IllegalArgumentException("password history");
|
||||
}
|
||||
if (passwordComplexChars > PASSWORD_COMPLEX_CHARS_MAX) {
|
||||
throw new IllegalArgumentException("complex chars");
|
||||
}
|
||||
// This value can be reduced (which actually increases security) if necessary
|
||||
if (maxPasswordFails > PASSWORD_MAX_FAILS_MAX) {
|
||||
maxPasswordFails = PASSWORD_MAX_FAILS_MAX;
|
||||
}
|
||||
// This value can be reduced (which actually increases security) if necessary
|
||||
if (maxScreenLockTime > SCREEN_LOCK_TIME_MAX) {
|
||||
maxScreenLockTime = SCREEN_LOCK_TIME_MAX;
|
||||
}
|
||||
}
|
||||
mMinPasswordLength = minPasswordLength;
|
||||
mPasswordMode = passwordMode;
|
||||
mMaxPasswordFails = maxPasswordFails;
|
||||
mMaxScreenLockTime = maxScreenLockTime;
|
||||
mRequireRemoteWipe = requireRemoteWipe;
|
||||
mPasswordExpirationDays = passwordExpirationDays;
|
||||
mPasswordHistory = passwordHistory;
|
||||
mPasswordComplexChars = passwordComplexChars;
|
||||
mRequireEncryption = requireEncryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from values encoded in an account
|
||||
* @param account
|
||||
*/
|
||||
public PolicySet(Account account) {
|
||||
this(account.mSecurityFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from values encoded in an account flags int
|
||||
*/
|
||||
public PolicySet(long flags) {
|
||||
mMinPasswordLength =
|
||||
(int) ((flags & PASSWORD_LENGTH_MASK) >> PASSWORD_LENGTH_SHIFT);
|
||||
mPasswordMode =
|
||||
(int) (flags & PASSWORD_MODE_MASK);
|
||||
mMaxPasswordFails =
|
||||
(int) ((flags & PASSWORD_MAX_FAILS_MASK) >> PASSWORD_MAX_FAILS_SHIFT);
|
||||
mMaxScreenLockTime =
|
||||
(int) ((flags & SCREEN_LOCK_TIME_MASK) >> SCREEN_LOCK_TIME_SHIFT);
|
||||
mRequireRemoteWipe = 0 != (flags & REQUIRE_REMOTE_WIPE);
|
||||
mPasswordExpirationDays =
|
||||
(int) ((flags & PASSWORD_EXPIRATION_MASK) >> PASSWORD_EXPIRATION_SHIFT);
|
||||
mPasswordHistory =
|
||||
(int) ((flags & PASSWORD_HISTORY_MASK) >> PASSWORD_HISTORY_SHIFT);
|
||||
mPasswordComplexChars =
|
||||
(int) ((flags & PASSWORD_COMPLEX_CHARS_MASK) >> PASSWORD_COMPLEX_CHARS_SHIFT);
|
||||
mRequireEncryption = 0 != (flags & REQUIRE_ENCRYPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to map our internal encoding to DevicePolicyManager password modes.
|
||||
*/
|
||||
public int getDPManagerPasswordQuality() {
|
||||
switch (mPasswordMode) {
|
||||
case PASSWORD_MODE_SIMPLE:
|
||||
return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
|
||||
case PASSWORD_MODE_STRONG:
|
||||
return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
|
||||
default:
|
||||
return DevicePolicyManager .PASSWORD_QUALITY_UNSPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to map expiration times to the millisecond values used by DevicePolicyManager.
|
||||
*/
|
||||
public long getDPManagerPasswordExpirationTimeout() {
|
||||
long result = mPasswordExpirationDays * DAYS_TO_MSEC;
|
||||
// Add a small offset to the password expiration. This makes it easier to test
|
||||
// by changing (for example) 1 day to 1 day + 5 minutes. If you set an expiration
|
||||
// that is within the warning period, you should get a warning fairly quickly.
|
||||
if (result > 0) {
|
||||
result += EXPIRATION_OFFSET_MSEC;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record flags (and a sync key for the flags) into an Account
|
||||
* Note: the hash code is defined as the encoding used in Account
|
||||
*
|
||||
* @param account to write the values mSecurityFlags and mSecuritySyncKey
|
||||
* @param syncKey the value to write into the account's mSecuritySyncKey
|
||||
* @param update if true, also writes the account back to the provider (updating only
|
||||
* the fields changed by this API)
|
||||
* @param context a context for writing to the provider
|
||||
* @return true if the actual policies changed, false if no change (note, sync key
|
||||
* does not affect this)
|
||||
*/
|
||||
public boolean writeAccount(Account account, String syncKey, boolean update,
|
||||
Context context) {
|
||||
long newFlags = getSecurityCode();
|
||||
boolean dirty = (newFlags != account.mSecurityFlags);
|
||||
account.mSecurityFlags = newFlags;
|
||||
account.mSecuritySyncKey = syncKey;
|
||||
if (update) {
|
||||
if (account.isSaved()) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(AccountColumns.SECURITY_FLAGS, account.mSecurityFlags);
|
||||
cv.put(AccountColumns.SECURITY_SYNC_KEY, account.mSecuritySyncKey);
|
||||
account.update(context, cv);
|
||||
} else {
|
||||
account.save(context);
|
||||
}
|
||||
}
|
||||
return dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof PolicySet) {
|
||||
PolicySet other = (PolicySet)o;
|
||||
return (this.getSecurityCode() == other.getSecurityCode());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports Parcelable
|
||||
*/
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports Parcelable
|
||||
*/
|
||||
public static final Parcelable.Creator<PolicySet> CREATOR
|
||||
= new Parcelable.Creator<PolicySet>() {
|
||||
public PolicySet createFromParcel(Parcel in) {
|
||||
return new PolicySet(in);
|
||||
}
|
||||
|
||||
public PolicySet[] newArray(int size) {
|
||||
return new PolicySet[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Supports Parcelable
|
||||
*/
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mMinPasswordLength);
|
||||
dest.writeInt(mPasswordMode);
|
||||
dest.writeInt(mMaxPasswordFails);
|
||||
dest.writeInt(mMaxScreenLockTime);
|
||||
dest.writeInt(mRequireRemoteWipe ? 1 : 0);
|
||||
dest.writeInt(mPasswordExpirationDays);
|
||||
dest.writeInt(mPasswordHistory);
|
||||
dest.writeInt(mPasswordComplexChars);
|
||||
dest.writeInt(mRequireEncryption ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports Parcelable
|
||||
*/
|
||||
public PolicySet(Parcel in) {
|
||||
mMinPasswordLength = in.readInt();
|
||||
mPasswordMode = in.readInt();
|
||||
mMaxPasswordFails = in.readInt();
|
||||
mMaxScreenLockTime = in.readInt();
|
||||
mRequireRemoteWipe = in.readInt() == 1;
|
||||
mPasswordExpirationDays = in.readInt();
|
||||
mPasswordHistory = in.readInt();
|
||||
mPasswordComplexChars = in.readInt();
|
||||
mRequireEncryption = in.readInt() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
long code = getSecurityCode();
|
||||
return (int) code;
|
||||
}
|
||||
|
||||
public long getSecurityCode() {
|
||||
long flags = 0;
|
||||
flags = (long)mMinPasswordLength << PASSWORD_LENGTH_SHIFT;
|
||||
flags |= mPasswordMode;
|
||||
flags |= (long)mMaxPasswordFails << PASSWORD_MAX_FAILS_SHIFT;
|
||||
flags |= (long)mMaxScreenLockTime << SCREEN_LOCK_TIME_SHIFT;
|
||||
if (mRequireRemoteWipe) flags |= REQUIRE_REMOTE_WIPE;
|
||||
flags |= (long)mPasswordHistory << PASSWORD_HISTORY_SHIFT;
|
||||
flags |= (long)mPasswordExpirationDays << PASSWORD_EXPIRATION_SHIFT;
|
||||
flags |= (long)mPasswordComplexChars << PASSWORD_COMPLEX_CHARS_SHIFT;
|
||||
if (mRequireEncryption) flags |= REQUIRE_ENCRYPTION;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{ " + "pw-len-min=" + mMinPasswordLength + " pw-mode=" + mPasswordMode
|
||||
+ " pw-fails-max=" + mMaxPasswordFails + " screenlock-max="
|
||||
+ mMaxScreenLockTime + " remote-wipe-req=" + mRequireRemoteWipe
|
||||
+ " pw-expiration=" + mPasswordExpirationDays
|
||||
+ " pw-history=" + mPasswordHistory
|
||||
+ " pw-complex-chars=" + mPasswordComplexChars
|
||||
+ " require-encryption=" + mRequireEncryption + "}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are not the active device admin, try to become so.
|
||||
*
|
||||
|
|
|
@ -19,13 +19,13 @@ package com.android.email.activity.setup;
|
|||
import com.android.email.Email;
|
||||
import com.android.email.R;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.SecurityPolicy.PolicySet;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.Sender;
|
||||
import com.android.email.mail.Store;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.email.provider.EmailContent.HostAuth;
|
||||
import com.android.emailcommon.service.EmailServiceProxy;
|
||||
import com.android.emailcommon.service.PolicySet;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
|
|
|
@ -19,13 +19,13 @@ package com.android.email.activity.setup;
|
|||
import com.android.email.Email;
|
||||
import com.android.email.ExchangeUtils;
|
||||
import com.android.email.R;
|
||||
import com.android.email.SecurityPolicy.PolicySet;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.activity.ActivityHelper;
|
||||
import com.android.email.mail.Store;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.email.service.MailService;
|
||||
import com.android.emailcommon.service.PolicySet;
|
||||
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.accounts.AccountManager;
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
package com.android.email.activity.setup;
|
||||
|
||||
import com.android.email.SecurityPolicy.PolicySet;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.emailcommon.service.PolicySet;
|
||||
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.os.Bundle;
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.service;
|
||||
|
||||
import com.android.email.SecurityPolicy;
|
||||
import com.android.emailcommon.service.IPolicyService;
|
||||
import com.android.emailcommon.service.PolicySet;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
public class PolicyService extends Service {
|
||||
|
||||
private SecurityPolicy mSecurityPolicy;
|
||||
private Context mContext;
|
||||
|
||||
private final IPolicyService.Stub mBinder = new IPolicyService.Stub() {
|
||||
public boolean isActive(PolicySet policies) {
|
||||
return mSecurityPolicy.isActive(policies);
|
||||
}
|
||||
|
||||
public void policiesRequired(long accountId) {
|
||||
mSecurityPolicy.policiesRequired(accountId);
|
||||
}
|
||||
|
||||
public void updatePolicies(long accountId) {
|
||||
mSecurityPolicy.updatePolicies(accountId);
|
||||
}
|
||||
|
||||
public void setAccountHoldFlag(long accountId, boolean newState) {
|
||||
SecurityPolicy.setAccountHoldFlag(mContext, accountId, newState);
|
||||
}
|
||||
|
||||
public boolean isActiveAdmin() {
|
||||
return mSecurityPolicy.isActiveAdmin();
|
||||
}
|
||||
|
||||
public void remoteWipe() {
|
||||
mSecurityPolicy.remoteWipe();
|
||||
}
|
||||
|
||||
public boolean isSupported(PolicySet policies) {
|
||||
return mSecurityPolicy.isSupported(policies);
|
||||
}
|
||||
|
||||
public PolicySet clearUnsupportedPolicies(PolicySet policies) {
|
||||
return mSecurityPolicy.clearUnsupportedPolicies(policies);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
// When we bind this service, save the context and SecurityPolicy singleton
|
||||
mContext = this;
|
||||
mSecurityPolicy = SecurityPolicy.getInstance(this);
|
||||
return mBinder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.emailcommon.service;
|
||||
|
||||
import com.android.emailcommon.service.PolicySet;
|
||||
|
||||
interface IPolicyService {
|
||||
boolean isActive(in PolicySet policies);
|
||||
void policiesRequired(long accountId);
|
||||
void updatePolicies(long accountId);
|
||||
void setAccountHoldFlag(long accountId, boolean newState);
|
||||
boolean isActiveAdmin();
|
||||
// This is about as oneway as you can get
|
||||
oneway void remoteWipe();
|
||||
boolean isSupported(in PolicySet policies);
|
||||
PolicySet clearUnsupportedPolicies(in PolicySet policies);
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.emailcommon.service;
|
||||
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
|
||||
private static final boolean DEBUG_PROXY = true; // STOPSHIP DO NOT CHECK THIS IN SET TO TRUE
|
||||
private static final String TAG = "PolicyServiceProxy";
|
||||
|
||||
// The intent used by sync adapter services to connect to the PolicyService
|
||||
public static final String POLICY_INTENT = "com.android.email.POLICY_INTENT";
|
||||
|
||||
private IPolicyService mService = null;
|
||||
private Object mReturn = null;
|
||||
|
||||
public PolicyServiceProxy(Context _context) {
|
||||
super(_context, new Intent(POLICY_INTENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(IBinder binder) {
|
||||
mService = IPolicyService.Stub.asInterface(binder);
|
||||
}
|
||||
|
||||
public IBinder asBinder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PolicySet clearUnsupportedPolicies(final PolicySet arg0) throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
public void run() throws RemoteException {
|
||||
mReturn = mService.clearUnsupportedPolicies(arg0);
|
||||
}
|
||||
}, "clearUnsupportedPolicies");
|
||||
waitForCompletion();
|
||||
if (DEBUG_PROXY) {
|
||||
Log.v(TAG, "clearUnsupportedPolicies: " + ((mReturn == null) ? "null" : mReturn));
|
||||
}
|
||||
if (mReturn == null) {
|
||||
// Can this happen?
|
||||
return null;
|
||||
} else {
|
||||
return (PolicySet)mReturn;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive(final PolicySet arg0) throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
public void run() throws RemoteException {
|
||||
mReturn = mService.isActive(arg0);
|
||||
}
|
||||
}, "isActive");
|
||||
waitForCompletion();
|
||||
if (DEBUG_PROXY) {
|
||||
Log.v(TAG, "isActive: " + ((mReturn == null) ? "null" : mReturn));
|
||||
}
|
||||
if (mReturn == null) {
|
||||
// Can this happen?
|
||||
return false;
|
||||
} else {
|
||||
return (Boolean)mReturn;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActiveAdmin() throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
public void run() throws RemoteException {
|
||||
mReturn = mService.isActiveAdmin();
|
||||
}
|
||||
}, "isActiveAdmin");
|
||||
waitForCompletion();
|
||||
if (DEBUG_PROXY) {
|
||||
Log.v(TAG, "isActiveAdmin: " + ((mReturn == null) ? "null" : mReturn));
|
||||
}
|
||||
if (mReturn == null) {
|
||||
// Can this happen?
|
||||
return false;
|
||||
} else {
|
||||
return (Boolean)mReturn;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported(final PolicySet arg0) throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
public void run() throws RemoteException {
|
||||
mReturn = mService.isSupported(arg0);
|
||||
}
|
||||
}, "isSupported");
|
||||
waitForCompletion();
|
||||
if (DEBUG_PROXY) {
|
||||
Log.v(TAG, "isSupported: " + ((mReturn == null) ? "null" : mReturn));
|
||||
}
|
||||
if (mReturn == null) {
|
||||
// Can this happen?
|
||||
return false;
|
||||
} else {
|
||||
return (Boolean)mReturn;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void policiesRequired(final long arg0) throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
public void run() throws RemoteException {
|
||||
mService.policiesRequired(arg0);
|
||||
}
|
||||
}, "policiesRequired");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remoteWipe() throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
public void run() throws RemoteException {
|
||||
mService.remoteWipe();
|
||||
}
|
||||
}, "remoteWipe");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccountHoldFlag(final long arg0, final boolean arg1) throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
public void run() throws RemoteException {
|
||||
mService.setAccountHoldFlag(arg0, arg1);
|
||||
}
|
||||
}, "setAccountHoldFlag");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePolicies(final long arg0) throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
public void run() throws RemoteException {
|
||||
mService.updatePolicies(arg0);
|
||||
}
|
||||
}, "updatePolicies");
|
||||
}
|
||||
|
||||
// Static methods that encapsulate the proxy calls above
|
||||
public static boolean isActive(Context context, PolicySet policies) {
|
||||
try {
|
||||
return new PolicyServiceProxy(context).isActive(policies);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void policiesRequired(Context context, long accountId) {
|
||||
try {
|
||||
new PolicyServiceProxy(context).policiesRequired(accountId);
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException("PolicyService transaction failed");
|
||||
}
|
||||
}
|
||||
|
||||
public static void updatePolicies(Context context, long accountId) {
|
||||
try {
|
||||
new PolicyServiceProxy(context).updatePolicies(accountId);
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException("PolicyService transaction failed");
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAccountHoldFlag(Context context, Account account, boolean newState) {
|
||||
try {
|
||||
new PolicyServiceProxy(context).setAccountHoldFlag(account.mId, newState);
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException("PolicyService transaction failed");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isActiveAdmin(Context context) {
|
||||
try {
|
||||
return new PolicyServiceProxy(context).isActiveAdmin();
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void remoteWipe(Context context) {
|
||||
try {
|
||||
new PolicyServiceProxy(context).remoteWipe();
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException("PolicyService transaction failed");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSupported(Context context, PolicySet policies) {
|
||||
try {
|
||||
return new PolicyServiceProxy(context).isSupported(policies);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static PolicySet clearUnsupportedPolicies(Context context, PolicySet policies) {
|
||||
try {
|
||||
return new PolicyServiceProxy(context).clearUnsupportedPolicies(policies);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
throw new IllegalStateException("PolicyService transaction failed");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.emailcommon.service;
|
||||
|
||||
parcelable PolicySet;
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
/* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.emailcommon.service;
|
||||
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.email.provider.EmailContent.AccountColumns;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
|
||||
/**
|
||||
* Class for tracking policies and reading/writing into accounts
|
||||
*/
|
||||
public class PolicySet implements Parcelable {
|
||||
|
||||
// Security (provisioning) flags
|
||||
// bits 0..4: password length (0=no password required)
|
||||
private static final int PASSWORD_LENGTH_MASK = 31;
|
||||
private static final int PASSWORD_LENGTH_SHIFT = 0;
|
||||
public static final int PASSWORD_LENGTH_MAX = 30;
|
||||
// bits 5..8: password mode
|
||||
private static final int PASSWORD_MODE_SHIFT = 5;
|
||||
private static final int PASSWORD_MODE_MASK = 15 << PASSWORD_MODE_SHIFT;
|
||||
public static final int PASSWORD_MODE_NONE = 0 << PASSWORD_MODE_SHIFT;
|
||||
public static final int PASSWORD_MODE_SIMPLE = 1 << PASSWORD_MODE_SHIFT;
|
||||
public static final int PASSWORD_MODE_STRONG = 2 << PASSWORD_MODE_SHIFT;
|
||||
// bits 9..13: password failures -> wipe device (0=disabled)
|
||||
private static final int PASSWORD_MAX_FAILS_SHIFT = 9;
|
||||
private static final int PASSWORD_MAX_FAILS_MASK = 31 << PASSWORD_MAX_FAILS_SHIFT;
|
||||
public static final int PASSWORD_MAX_FAILS_MAX = 31;
|
||||
// bits 14..24: seconds to screen lock (0=not required)
|
||||
private static final int SCREEN_LOCK_TIME_SHIFT = 14;
|
||||
private static final int SCREEN_LOCK_TIME_MASK = 2047 << SCREEN_LOCK_TIME_SHIFT;
|
||||
public static final int SCREEN_LOCK_TIME_MAX = 2047;
|
||||
// bit 25: remote wipe capability required
|
||||
private static final int REQUIRE_REMOTE_WIPE = 1 << 25;
|
||||
// bit 26..35: password expiration (days; 0=not required)
|
||||
private static final int PASSWORD_EXPIRATION_SHIFT = 26;
|
||||
private static final long PASSWORD_EXPIRATION_MASK = 1023L << PASSWORD_EXPIRATION_SHIFT;
|
||||
public static final int PASSWORD_EXPIRATION_MAX = 1023;
|
||||
// bit 36..43: password history (length; 0=not required)
|
||||
private static final int PASSWORD_HISTORY_SHIFT = 36;
|
||||
private static final long PASSWORD_HISTORY_MASK = 255L << PASSWORD_HISTORY_SHIFT;
|
||||
public static final int PASSWORD_HISTORY_MAX = 255;
|
||||
// bit 44..48: min complex characters (0=not required)
|
||||
private static final int PASSWORD_COMPLEX_CHARS_SHIFT = 44;
|
||||
private static final long PASSWORD_COMPLEX_CHARS_MASK = 31L << PASSWORD_COMPLEX_CHARS_SHIFT;
|
||||
public static final int PASSWORD_COMPLEX_CHARS_MAX = 31;
|
||||
// bit 49: requires device encryption
|
||||
private static final long REQUIRE_ENCRYPTION = 1L << 49;
|
||||
|
||||
/* Convert days to mSec (used for password expiration) */
|
||||
private static final long DAYS_TO_MSEC = 24 * 60 * 60 * 1000;
|
||||
/* Small offset (2 minutes) added to policy expiration to make user testing easier. */
|
||||
private static final long EXPIRATION_OFFSET_MSEC = 2 * 60 * 1000;
|
||||
|
||||
public final int mMinPasswordLength;
|
||||
public final int mPasswordMode;
|
||||
public final int mMaxPasswordFails;
|
||||
public final int mMaxScreenLockTime;
|
||||
public final boolean mRequireRemoteWipe;
|
||||
public final int mPasswordExpirationDays;
|
||||
public final int mPasswordHistory;
|
||||
public final int mPasswordComplexChars;
|
||||
public final boolean mRequireEncryption;
|
||||
|
||||
public int getMinPasswordLengthForTest() {
|
||||
return mMinPasswordLength;
|
||||
}
|
||||
|
||||
public int getPasswordModeForTest() {
|
||||
return mPasswordMode;
|
||||
}
|
||||
|
||||
public int getMaxPasswordFailsForTest() {
|
||||
return mMaxPasswordFails;
|
||||
}
|
||||
|
||||
public int getMaxScreenLockTimeForTest() {
|
||||
return mMaxScreenLockTime;
|
||||
}
|
||||
|
||||
public boolean isRequireRemoteWipeForTest() {
|
||||
return mRequireRemoteWipe;
|
||||
}
|
||||
|
||||
public boolean isRequireEncryptionForTest() {
|
||||
return mRequireEncryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from raw values.
|
||||
* @param minPasswordLength (0=not enforced)
|
||||
* @param passwordMode
|
||||
* @param maxPasswordFails (0=not enforced)
|
||||
* @param maxScreenLockTime in seconds (0=not enforced)
|
||||
* @param requireRemoteWipe
|
||||
* @param passwordExpirationDays in days (0=not enforced)
|
||||
* @param passwordHistory (0=not enforced)
|
||||
* @param passwordComplexChars (0=not enforced)
|
||||
* @throws IllegalArgumentException for illegal arguments.
|
||||
*/
|
||||
public PolicySet(int minPasswordLength, int passwordMode, int maxPasswordFails,
|
||||
int maxScreenLockTime, boolean requireRemoteWipe, int passwordExpirationDays,
|
||||
int passwordHistory, int passwordComplexChars, boolean requireEncryption)
|
||||
throws IllegalArgumentException {
|
||||
// If we're not enforcing passwords, make sure we clean up related values, since EAS
|
||||
// can send non-zero values for any or all of these
|
||||
if (passwordMode == PASSWORD_MODE_NONE) {
|
||||
maxPasswordFails = 0;
|
||||
maxScreenLockTime = 0;
|
||||
minPasswordLength = 0;
|
||||
passwordComplexChars = 0;
|
||||
passwordHistory = 0;
|
||||
passwordExpirationDays = 0;
|
||||
} else {
|
||||
if ((passwordMode != PASSWORD_MODE_SIMPLE) &&
|
||||
(passwordMode != PASSWORD_MODE_STRONG)) {
|
||||
throw new IllegalArgumentException("password mode");
|
||||
}
|
||||
// If we're only requiring a simple password, set complex chars to zero; note
|
||||
// that EAS can erroneously send non-zero values in this case
|
||||
if (passwordMode == PASSWORD_MODE_SIMPLE) {
|
||||
passwordComplexChars = 0;
|
||||
}
|
||||
// The next four values have hard limits which cannot be supported if exceeded.
|
||||
if (minPasswordLength > PASSWORD_LENGTH_MAX) {
|
||||
throw new IllegalArgumentException("password length");
|
||||
}
|
||||
if (passwordExpirationDays > PASSWORD_EXPIRATION_MAX) {
|
||||
throw new IllegalArgumentException("password expiration");
|
||||
}
|
||||
if (passwordHistory > PASSWORD_HISTORY_MAX) {
|
||||
throw new IllegalArgumentException("password history");
|
||||
}
|
||||
if (passwordComplexChars > PASSWORD_COMPLEX_CHARS_MAX) {
|
||||
throw new IllegalArgumentException("complex chars");
|
||||
}
|
||||
// This value can be reduced (which actually increases security) if necessary
|
||||
if (maxPasswordFails > PASSWORD_MAX_FAILS_MAX) {
|
||||
maxPasswordFails = PASSWORD_MAX_FAILS_MAX;
|
||||
}
|
||||
// This value can be reduced (which actually increases security) if necessary
|
||||
if (maxScreenLockTime > SCREEN_LOCK_TIME_MAX) {
|
||||
maxScreenLockTime = SCREEN_LOCK_TIME_MAX;
|
||||
}
|
||||
}
|
||||
mMinPasswordLength = minPasswordLength;
|
||||
mPasswordMode = passwordMode;
|
||||
mMaxPasswordFails = maxPasswordFails;
|
||||
mMaxScreenLockTime = maxScreenLockTime;
|
||||
mRequireRemoteWipe = requireRemoteWipe;
|
||||
mPasswordExpirationDays = passwordExpirationDays;
|
||||
mPasswordHistory = passwordHistory;
|
||||
mPasswordComplexChars = passwordComplexChars;
|
||||
mRequireEncryption = requireEncryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from values encoded in an account
|
||||
* @param account
|
||||
*/
|
||||
public PolicySet(Account account) {
|
||||
this(account.mSecurityFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from values encoded in an account flags int
|
||||
*/
|
||||
public PolicySet(long flags) {
|
||||
mMinPasswordLength =
|
||||
(int) ((flags & PASSWORD_LENGTH_MASK) >> PASSWORD_LENGTH_SHIFT);
|
||||
mPasswordMode =
|
||||
(int) (flags & PASSWORD_MODE_MASK);
|
||||
mMaxPasswordFails =
|
||||
(int) ((flags & PASSWORD_MAX_FAILS_MASK) >> PASSWORD_MAX_FAILS_SHIFT);
|
||||
mMaxScreenLockTime =
|
||||
(int) ((flags & SCREEN_LOCK_TIME_MASK) >> SCREEN_LOCK_TIME_SHIFT);
|
||||
mRequireRemoteWipe = 0 != (flags & REQUIRE_REMOTE_WIPE);
|
||||
mPasswordExpirationDays =
|
||||
(int) ((flags & PASSWORD_EXPIRATION_MASK) >> PASSWORD_EXPIRATION_SHIFT);
|
||||
mPasswordHistory =
|
||||
(int) ((flags & PASSWORD_HISTORY_MASK) >> PASSWORD_HISTORY_SHIFT);
|
||||
mPasswordComplexChars =
|
||||
(int) ((flags & PASSWORD_COMPLEX_CHARS_MASK) >> PASSWORD_COMPLEX_CHARS_SHIFT);
|
||||
mRequireEncryption = 0 != (flags & REQUIRE_ENCRYPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to map our internal encoding to DevicePolicyManager password modes.
|
||||
*/
|
||||
public int getDPManagerPasswordQuality() {
|
||||
switch (mPasswordMode) {
|
||||
case PASSWORD_MODE_SIMPLE:
|
||||
return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
|
||||
case PASSWORD_MODE_STRONG:
|
||||
return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
|
||||
default:
|
||||
return DevicePolicyManager .PASSWORD_QUALITY_UNSPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to map expiration times to the millisecond values used by DevicePolicyManager.
|
||||
*/
|
||||
public long getDPManagerPasswordExpirationTimeout() {
|
||||
long result = mPasswordExpirationDays * DAYS_TO_MSEC;
|
||||
// Add a small offset to the password expiration. This makes it easier to test
|
||||
// by changing (for example) 1 day to 1 day + 5 minutes. If you set an expiration
|
||||
// that is within the warning period, you should get a warning fairly quickly.
|
||||
if (result > 0) {
|
||||
result += EXPIRATION_OFFSET_MSEC;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record flags (and a sync key for the flags) into an Account
|
||||
* Note: the hash code is defined as the encoding used in Account
|
||||
*
|
||||
* @param account to write the values mSecurityFlags and mSecuritySyncKey
|
||||
* @param syncKey the value to write into the account's mSecuritySyncKey
|
||||
* @param update if true, also writes the account back to the provider (updating only
|
||||
* the fields changed by this API)
|
||||
* @param context a context for writing to the provider
|
||||
* @return true if the actual policies changed, false if no change (note, sync key
|
||||
* does not affect this)
|
||||
*/
|
||||
public boolean writeAccount(Account account, String syncKey, boolean update,
|
||||
Context context) {
|
||||
long newFlags = getSecurityCode();
|
||||
boolean dirty = (newFlags != account.mSecurityFlags);
|
||||
account.mSecurityFlags = newFlags;
|
||||
account.mSecuritySyncKey = syncKey;
|
||||
if (update) {
|
||||
if (account.isSaved()) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(AccountColumns.SECURITY_FLAGS, account.mSecurityFlags);
|
||||
cv.put(AccountColumns.SECURITY_SYNC_KEY, account.mSecuritySyncKey);
|
||||
account.update(context, cv);
|
||||
} else {
|
||||
account.save(context);
|
||||
}
|
||||
}
|
||||
return dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof PolicySet) {
|
||||
PolicySet other = (PolicySet)o;
|
||||
return (this.getSecurityCode() == other.getSecurityCode());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports Parcelable
|
||||
*/
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports Parcelable
|
||||
*/
|
||||
public static final Parcelable.Creator<PolicySet> CREATOR
|
||||
= new Parcelable.Creator<PolicySet>() {
|
||||
public PolicySet createFromParcel(Parcel in) {
|
||||
return new PolicySet(in);
|
||||
}
|
||||
|
||||
public PolicySet[] newArray(int size) {
|
||||
return new PolicySet[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Supports Parcelable
|
||||
*/
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mMinPasswordLength);
|
||||
dest.writeInt(mPasswordMode);
|
||||
dest.writeInt(mMaxPasswordFails);
|
||||
dest.writeInt(mMaxScreenLockTime);
|
||||
dest.writeInt(mRequireRemoteWipe ? 1 : 0);
|
||||
dest.writeInt(mPasswordExpirationDays);
|
||||
dest.writeInt(mPasswordHistory);
|
||||
dest.writeInt(mPasswordComplexChars);
|
||||
dest.writeInt(mRequireEncryption ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports Parcelable
|
||||
*/
|
||||
public PolicySet(Parcel in) {
|
||||
mMinPasswordLength = in.readInt();
|
||||
mPasswordMode = in.readInt();
|
||||
mMaxPasswordFails = in.readInt();
|
||||
mMaxScreenLockTime = in.readInt();
|
||||
mRequireRemoteWipe = in.readInt() == 1;
|
||||
mPasswordExpirationDays = in.readInt();
|
||||
mPasswordHistory = in.readInt();
|
||||
mPasswordComplexChars = in.readInt();
|
||||
mRequireEncryption = in.readInt() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
long code = getSecurityCode();
|
||||
return (int) code;
|
||||
}
|
||||
|
||||
public long getSecurityCode() {
|
||||
long flags = 0;
|
||||
flags = (long)mMinPasswordLength << PASSWORD_LENGTH_SHIFT;
|
||||
flags |= mPasswordMode;
|
||||
flags |= (long)mMaxPasswordFails << PASSWORD_MAX_FAILS_SHIFT;
|
||||
flags |= (long)mMaxScreenLockTime << SCREEN_LOCK_TIME_SHIFT;
|
||||
if (mRequireRemoteWipe) flags |= REQUIRE_REMOTE_WIPE;
|
||||
flags |= (long)mPasswordHistory << PASSWORD_HISTORY_SHIFT;
|
||||
flags |= (long)mPasswordExpirationDays << PASSWORD_EXPIRATION_SHIFT;
|
||||
flags |= (long)mPasswordComplexChars << PASSWORD_COMPLEX_CHARS_SHIFT;
|
||||
if (mRequireEncryption) flags |= REQUIRE_ENCRYPTION;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{ " + "pw-len-min=" + mMinPasswordLength + " pw-mode=" + mPasswordMode
|
||||
+ " pw-fails-max=" + mMaxPasswordFails + " screenlock-max="
|
||||
+ mMaxScreenLockTime + " remote-wipe-req=" + mRequireRemoteWipe
|
||||
+ " pw-expiration=" + mPasswordExpirationDays
|
||||
+ " pw-history=" + mPasswordHistory
|
||||
+ " pw-complex-chars=" + mPasswordComplexChars
|
||||
+ " require-encryption=" + mRequireEncryption + "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
package com.android.exchange;
|
||||
|
||||
import com.android.email.SecurityPolicy;
|
||||
import com.android.email.SecurityPolicy.PolicySet;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.mail.Address;
|
||||
import com.android.email.mail.MeetingInfo;
|
||||
|
@ -37,6 +35,8 @@ import com.android.email.provider.EmailContent.SyncColumns;
|
|||
import com.android.emailcommon.service.EmailServiceConstants;
|
||||
import com.android.emailcommon.service.EmailServiceProxy;
|
||||
import com.android.emailcommon.service.EmailServiceStatus;
|
||||
import com.android.emailcommon.service.PolicyServiceProxy;
|
||||
import com.android.emailcommon.service.PolicySet;
|
||||
import com.android.exchange.adapter.AbstractSyncAdapter;
|
||||
import com.android.exchange.adapter.AccountSyncAdapter;
|
||||
import com.android.exchange.adapter.CalendarSyncAdapter;
|
||||
|
@ -46,11 +46,11 @@ import com.android.exchange.adapter.FolderSyncParser;
|
|||
import com.android.exchange.adapter.GalParser;
|
||||
import com.android.exchange.adapter.MeetingResponseParser;
|
||||
import com.android.exchange.adapter.MoveItemsParser;
|
||||
import com.android.exchange.adapter.Parser.EasParserException;
|
||||
import com.android.exchange.adapter.PingParser;
|
||||
import com.android.exchange.adapter.ProvisionParser;
|
||||
import com.android.exchange.adapter.Serializer;
|
||||
import com.android.exchange.adapter.Tags;
|
||||
import com.android.exchange.adapter.Parser.EasParserException;
|
||||
import com.android.exchange.provider.GalResult;
|
||||
import com.android.exchange.utility.CalendarUtilities;
|
||||
|
||||
|
@ -1435,24 +1435,23 @@ public class EasSyncService extends AbstractSyncService {
|
|||
// by the server
|
||||
ProvisionParser pp = canProvision();
|
||||
if (pp != null) {
|
||||
SecurityPolicy sp = SecurityPolicy.getInstance(mContext);
|
||||
// Get the policies from ProvisionParser
|
||||
PolicySet ps = pp.getPolicySet();
|
||||
// Update the account with a null policyKey (the key we've gotten is
|
||||
// temporary and cannot be used for syncing)
|
||||
ps.writeAccount(mAccount, null, true, mContext);
|
||||
// Make sure that SecurityPolicy is up-to-date
|
||||
sp.updatePolicies(mAccount.mId);
|
||||
PolicyServiceProxy.updatePolicies(mContext, mAccount.mId);
|
||||
if (pp.getRemoteWipe()) {
|
||||
// We've gotten a remote wipe command
|
||||
ExchangeService.alwaysLog("!!! Remote wipe request received");
|
||||
// Start by setting the account to security hold
|
||||
sp.setAccountHoldFlag(mContext, mAccount, true);
|
||||
PolicyServiceProxy.setAccountHoldFlag(mContext, mAccount, true);
|
||||
// Force a stop to any running syncs for this account (except this one)
|
||||
ExchangeService.stopNonAccountMailboxSyncsForAccount(mAccount.mId);
|
||||
|
||||
// If we're not the admin, we can't do the wipe, so just return
|
||||
if (!sp.isActiveAdmin()) {
|
||||
if (!PolicyServiceProxy.isActiveAdmin(mContext)) {
|
||||
ExchangeService.alwaysLog("!!! Not device admin; can't wipe");
|
||||
return false;
|
||||
}
|
||||
|
@ -1468,9 +1467,9 @@ public class EasSyncService extends AbstractSyncService {
|
|||
}
|
||||
// Then, tell SecurityPolicy to wipe the device
|
||||
ExchangeService.alwaysLog("!!! Executing remote wipe");
|
||||
sp.remoteWipe();
|
||||
PolicyServiceProxy.remoteWipe(mContext);
|
||||
return false;
|
||||
} else if (sp.isActive(ps)) {
|
||||
} else if (PolicyServiceProxy.isActive(mContext, ps)) {
|
||||
// See if the required policies are in force; if they are, acknowledge the policies
|
||||
// to the server and get the final policy key
|
||||
String policyKey = acknowledgeProvision(pp.getPolicyKey(), PROVISION_STATUS_OK);
|
||||
|
@ -1483,7 +1482,7 @@ public class EasSyncService extends AbstractSyncService {
|
|||
}
|
||||
} else {
|
||||
// Notify that we are blocked because of policies
|
||||
sp.policiesRequired(mAccount.mId);
|
||||
PolicyServiceProxy.policiesRequired(mContext, mAccount.mId);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -1762,15 +1761,14 @@ public class EasSyncService extends AbstractSyncService {
|
|||
String key = mAccount.mSecuritySyncKey;
|
||||
if (!TextUtils.isEmpty(key)) {
|
||||
PolicySet ps = new PolicySet(mAccount);
|
||||
SecurityPolicy sp = SecurityPolicy.getInstance(mContext);
|
||||
if (!sp.isActive(ps)) {
|
||||
if (!PolicyServiceProxy.isActive(mContext, ps)) {
|
||||
cv.clear();
|
||||
cv.put(AccountColumns.SECURITY_FLAGS, 0);
|
||||
cv.putNull(AccountColumns.SECURITY_SYNC_KEY);
|
||||
long accountId = mAccount.mId;
|
||||
mContentResolver.update(ContentUris.withAppendedId(
|
||||
Account.CONTENT_URI, accountId), cv, null, null);
|
||||
sp.policiesRequired(accountId);
|
||||
PolicyServiceProxy.policiesRequired(mContext, accountId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
|
||||
package com.android.exchange.adapter;
|
||||
|
||||
import com.android.email.SecurityPolicy;
|
||||
import com.android.email.SecurityPolicy.PolicySet;
|
||||
import com.android.emailcommon.service.PolicyServiceProxy;
|
||||
import com.android.emailcommon.service.PolicySet;
|
||||
import com.android.exchange.EasSyncService;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
@ -61,8 +61,7 @@ public class ProvisionParser extends Parser {
|
|||
}
|
||||
|
||||
public void clearUnsupportedPolicies() {
|
||||
mPolicySet = SecurityPolicy.getInstance(mService.mContext)
|
||||
.clearUnsupportedPolicies(mPolicySet);
|
||||
mPolicySet = PolicyServiceProxy.clearUnsupportedPolicies(mService.mContext, mPolicySet);
|
||||
mIsSupportable = true;
|
||||
}
|
||||
|
||||
|
@ -209,11 +208,12 @@ public class ProvisionParser extends Parser {
|
|||
}
|
||||
}
|
||||
|
||||
mPolicySet = new SecurityPolicy.PolicySet(minPasswordLength, passwordMode,
|
||||
mPolicySet = new PolicySet(minPasswordLength, passwordMode,
|
||||
maxPasswordFails, maxScreenLockTime, true, passwordExpirationDays, passwordHistory,
|
||||
passwordComplexChars, encryptionRequired);
|
||||
|
||||
// We can only determine whether encryption is supported on device by using isSupported here
|
||||
if (!SecurityPolicy.getInstance(mService.mContext).isSupported(mPolicySet)) {
|
||||
if (!PolicyServiceProxy.isSupported(mService.mContext, mPolicySet)) {
|
||||
log("SecurityPolicy reports PolicySet not supported.");
|
||||
mIsSupportable = false;
|
||||
}
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
|
||||
package com.android.email;
|
||||
|
||||
import com.android.email.SecurityPolicy.PolicySet;
|
||||
import com.android.email.provider.ContentCache;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailProvider;
|
||||
import com.android.email.provider.ProviderTestUtils;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.email.provider.EmailContent.AccountColumns;
|
||||
import com.android.email.provider.EmailContent.Mailbox;
|
||||
import com.android.email.provider.EmailContent.Message;
|
||||
import com.android.email.provider.EmailProvider;
|
||||
import com.android.email.provider.ProviderTestUtils;
|
||||
import com.android.emailcommon.service.PolicySet;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ContentUris;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package com.android.exchange.adapter;
|
||||
|
||||
import com.android.email.SecurityPolicy.PolicySet;
|
||||
import com.android.emailcommon.service.PolicySet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
|
Loading…
Reference in New Issue