Merge "Rewrite of security policy handling and service code"
This commit is contained in:
commit
ad921d01ca
|
@ -671,10 +671,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce
|
|||
*/
|
||||
@Override
|
||||
public int update(Context context, ContentValues cv) {
|
||||
if (mPolicy != null && mPolicyKey <= 0) {
|
||||
// If a policy is set and there's no policy, link it to the account
|
||||
Policy.setAccountPolicy(context, this, mPolicy, null);
|
||||
}
|
||||
if (cv.containsKey(AccountColumns.IS_DEFAULT) &&
|
||||
cv.getAsBoolean(AccountColumns.IS_DEFAULT)) {
|
||||
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
|
||||
|
|
|
@ -1437,5 +1437,8 @@ public abstract class EmailContent {
|
|||
public static final String MAX_CALENDAR_LOOKBACK = "maxCalendarLookback";
|
||||
// Indicates that the server allows password recovery, not that we support it
|
||||
public static final String PASSWORD_RECOVERY_ENABLED = "passwordRecoveryEnabled";
|
||||
// Tokenized strings indicating protocol specific policies enforced/unsupported
|
||||
public static final String PROTOCOL_POLICIES_ENFORCED = "protocolPoliciesEnforced";
|
||||
public static final String PROTOCOL_POLICIES_UNSUPPORTED = "protocolPoliciesUnsupported";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,21 +16,17 @@
|
|||
|
||||
package com.android.emailcommon.provider;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.emailcommon.utility.TextUtilities;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
@ -56,6 +52,8 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
public static final int PASSWORD_MODE_SIMPLE = 1;
|
||||
public static final int PASSWORD_MODE_STRONG = 2;
|
||||
|
||||
public static final char POLICY_STRING_DELIMITER = '\1';
|
||||
|
||||
public int mPasswordMode;
|
||||
public int mPasswordMinLength;
|
||||
public int mPasswordMaxFails;
|
||||
|
@ -76,6 +74,8 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
public int mMaxEmailLookback;
|
||||
public int mMaxCalendarLookback;
|
||||
public boolean mPasswordRecoveryEnabled;
|
||||
public String mProtocolPoliciesEnforced;
|
||||
public String mProtocolPoliciesUnsupported;
|
||||
|
||||
public static final int CONTENT_ID_COLUMN = 0;
|
||||
public static final int CONTENT_PASSWORD_MODE_COLUMN = 1;
|
||||
|
@ -98,6 +98,8 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
public static final int CONTENT_MAX_EMAIL_LOOKBACK_COLUMN = 18;
|
||||
public static final int CONTENT_MAX_CALENDAR_LOOKBACK_COLUMN = 19;
|
||||
public static final int CONTENT_PASSWORD_RECOVERY_ENABLED_COLUMN = 20;
|
||||
public static final int CONTENT_PROTOCOL_POLICIES_ENFORCED_COLUMN = 21;
|
||||
public static final int CONTENT_PROTOCOL_POLICIES_UNSUPPORTED_COLUMN = 22;
|
||||
|
||||
public static final String[] CONTENT_PROJECTION = new String[] {RECORD_ID,
|
||||
PolicyColumns.PASSWORD_MODE, PolicyColumns.PASSWORD_MIN_LENGTH,
|
||||
|
@ -109,7 +111,8 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
PolicyColumns.DONT_ALLOW_ATTACHMENTS, PolicyColumns.DONT_ALLOW_HTML,
|
||||
PolicyColumns.MAX_ATTACHMENT_SIZE, PolicyColumns.MAX_TEXT_TRUNCATION_SIZE,
|
||||
PolicyColumns.MAX_HTML_TRUNCATION_SIZE, PolicyColumns.MAX_EMAIL_LOOKBACK,
|
||||
PolicyColumns.MAX_CALENDAR_LOOKBACK, PolicyColumns.PASSWORD_RECOVERY_ENABLED
|
||||
PolicyColumns.MAX_CALENDAR_LOOKBACK, PolicyColumns.PASSWORD_RECOVERY_ENABLED,
|
||||
PolicyColumns.PROTOCOL_POLICIES_ENFORCED, PolicyColumns.PROTOCOL_POLICIES_UNSUPPORTED
|
||||
};
|
||||
|
||||
public static final Policy NO_POLICY = new Policy();
|
||||
|
@ -139,6 +142,24 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
Account.ID_PROJECTION_COLUMN, Account.NO_ACCOUNT);
|
||||
}
|
||||
|
||||
public static ArrayList<String> addPolicyStringToList(String policyString,
|
||||
ArrayList<String> policyList) {
|
||||
if (policyString != null) {
|
||||
int start = 0;
|
||||
int len = policyString.length();
|
||||
while(start < len) {
|
||||
int end = policyString.indexOf(POLICY_STRING_DELIMITER, start);
|
||||
if (end > start) {
|
||||
policyList.add(policyString.substring(start, end));
|
||||
start = end + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return policyList;
|
||||
}
|
||||
|
||||
// We override this method to insure that we never write invalid policy data to the provider
|
||||
@Override
|
||||
public Uri save(Context context) {
|
||||
|
@ -146,76 +167,6 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
return super.save(context);
|
||||
}
|
||||
|
||||
public static void clearAccountPolicy(Context context, Account account) {
|
||||
setAccountPolicy(context, account, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for {@link #setAccountPolicy(Context, Account, Policy, String)}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static void setAccountPolicy(Context context, long accountId, Policy policy,
|
||||
String securitySyncKey) {
|
||||
setAccountPolicy(context, Account.restoreAccountWithId(context, accountId),
|
||||
policy, securitySyncKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the policy for an account atomically; this also removes any other policy associated with
|
||||
* the account and sets the policy key for the account. If policy is null, the policyKey is
|
||||
* set to 0 and the securitySyncKey to null. Also, update the account object to reflect the
|
||||
* current policyKey and securitySyncKey
|
||||
* @param context the caller's context
|
||||
* @param account the account whose policy is to be set
|
||||
* @param policy the policy to set, or null if we're clearing the policy
|
||||
* @param securitySyncKey the security sync key for this account (ignored if policy is null)
|
||||
*/
|
||||
public static void setAccountPolicy(Context context, Account account, Policy policy,
|
||||
String securitySyncKey) {
|
||||
if (DEBUG_POLICY) {
|
||||
Log.d(TAG, "Set policy for account " + account.mDisplayName + ": " +
|
||||
((policy == null) ? "none" : policy.toString()));
|
||||
}
|
||||
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
|
||||
|
||||
// Make sure this is a valid policy set
|
||||
if (policy != null) {
|
||||
policy.normalize();
|
||||
// Add the new policy (no account will yet reference this)
|
||||
ops.add(ContentProviderOperation.newInsert(
|
||||
Policy.CONTENT_URI).withValues(policy.toContentValues()).build());
|
||||
// Make the policyKey of the account our newly created policy, and set the sync key
|
||||
ops.add(ContentProviderOperation.newUpdate(
|
||||
ContentUris.withAppendedId(Account.CONTENT_URI, account.mId))
|
||||
.withValueBackReference(AccountColumns.POLICY_KEY, 0)
|
||||
.withValue(AccountColumns.SECURITY_SYNC_KEY, securitySyncKey)
|
||||
.build());
|
||||
} else {
|
||||
ops.add(ContentProviderOperation.newUpdate(
|
||||
ContentUris.withAppendedId(Account.CONTENT_URI, account.mId))
|
||||
.withValue(AccountColumns.SECURITY_SYNC_KEY, null)
|
||||
.withValue(AccountColumns.POLICY_KEY, 0)
|
||||
.build());
|
||||
}
|
||||
|
||||
// Delete the previous policy associated with this account, if any
|
||||
if (account.mPolicyKey > 0) {
|
||||
ops.add(ContentProviderOperation.newDelete(
|
||||
ContentUris.withAppendedId(
|
||||
Policy.CONTENT_URI, account.mPolicyKey)).build());
|
||||
}
|
||||
|
||||
try {
|
||||
context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops);
|
||||
account.refresh(context);
|
||||
} catch (RemoteException e) {
|
||||
// This is fatal to a remote process
|
||||
throw new IllegalStateException("Exception setting account policy.");
|
||||
} catch (OperationApplicationException e) {
|
||||
// Can't happen; our provider doesn't throw this exception
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Review all attachment records for this account, and reset the "don't allow download" flag
|
||||
* as required by the account's new security policies
|
||||
|
@ -286,6 +237,7 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Policy)) return false;
|
||||
Policy otherPolicy = (Policy)other;
|
||||
// Policies here are enforced by the DPM
|
||||
if (mRequireEncryption != otherPolicy.mRequireEncryption) return false;
|
||||
if (mRequireEncryptionExternal != otherPolicy.mRequireEncryptionExternal) return false;
|
||||
if (mRequireRemoteWipe != otherPolicy.mRequireRemoteWipe) return false;
|
||||
|
@ -296,10 +248,13 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
if (mPasswordMaxFails != otherPolicy.mPasswordMaxFails) return false;
|
||||
if (mPasswordMinLength != otherPolicy.mPasswordMinLength) return false;
|
||||
if (mPasswordMode != otherPolicy.mPasswordMode) return false;
|
||||
if (mDontAllowCamera != otherPolicy.mDontAllowCamera) return false;
|
||||
|
||||
// Policies here are enforced by the Exchange sync manager
|
||||
// They should eventually be removed from Policy and replaced with some opaque data
|
||||
if (mRequireManualSyncWhenRoaming != otherPolicy.mRequireManualSyncWhenRoaming) {
|
||||
return false;
|
||||
}
|
||||
if (mDontAllowCamera != otherPolicy.mDontAllowCamera) return false;
|
||||
if (mDontAllowAttachments != otherPolicy.mDontAllowAttachments) return false;
|
||||
if (mDontAllowHtml != otherPolicy.mDontAllowHtml) return false;
|
||||
if (mMaxAttachmentSize != otherPolicy.mMaxAttachmentSize) return false;
|
||||
|
@ -308,6 +263,15 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
if (mMaxEmailLookback != otherPolicy.mMaxEmailLookback) return false;
|
||||
if (mMaxCalendarLookback != otherPolicy.mMaxCalendarLookback) return false;
|
||||
if (mPasswordRecoveryEnabled != otherPolicy.mPasswordRecoveryEnabled) return false;
|
||||
|
||||
if (!TextUtilities.stringOrNullEquals(mProtocolPoliciesEnforced,
|
||||
otherPolicy.mProtocolPoliciesEnforced)) {
|
||||
return false;
|
||||
}
|
||||
if (!TextUtilities.stringOrNullEquals(mProtocolPoliciesUnsupported,
|
||||
otherPolicy.mProtocolPoliciesUnsupported)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -353,6 +317,9 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
mMaxEmailLookback = cursor.getInt(CONTENT_MAX_EMAIL_LOOKBACK_COLUMN);
|
||||
mMaxCalendarLookback = cursor.getInt(CONTENT_MAX_CALENDAR_LOOKBACK_COLUMN);
|
||||
mPasswordRecoveryEnabled = cursor.getInt(CONTENT_PASSWORD_RECOVERY_ENABLED_COLUMN) == 1;
|
||||
mProtocolPoliciesEnforced = cursor.getString(CONTENT_PROTOCOL_POLICIES_ENFORCED_COLUMN);
|
||||
mProtocolPoliciesUnsupported =
|
||||
cursor.getString(CONTENT_PROTOCOL_POLICIES_UNSUPPORTED_COLUMN);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -378,6 +345,8 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
values.put(PolicyColumns.MAX_EMAIL_LOOKBACK, mMaxEmailLookback);
|
||||
values.put(PolicyColumns.MAX_CALENDAR_LOOKBACK, mMaxCalendarLookback);
|
||||
values.put(PolicyColumns.PASSWORD_RECOVERY_ENABLED, mPasswordRecoveryEnabled);
|
||||
values.put(PolicyColumns.PROTOCOL_POLICIES_ENFORCED, mProtocolPoliciesEnforced);
|
||||
values.put(PolicyColumns.PROTOCOL_POLICIES_UNSUPPORTED, mProtocolPoliciesUnsupported);
|
||||
return values;
|
||||
}
|
||||
|
||||
|
@ -508,6 +477,8 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
dest.writeInt(mMaxEmailLookback);
|
||||
dest.writeInt(mMaxCalendarLookback);
|
||||
dest.writeInt(mPasswordRecoveryEnabled ? 1 : 0);
|
||||
dest.writeString(mProtocolPoliciesEnforced);
|
||||
dest.writeString(mProtocolPoliciesUnsupported);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -536,5 +507,7 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
mMaxEmailLookback = in.readInt();
|
||||
mMaxCalendarLookback = in.readInt();
|
||||
mPasswordRecoveryEnabled = in.readInt() == 1;
|
||||
mProtocolPoliciesEnforced = in.readString();
|
||||
mProtocolPoliciesUnsupported = in.readString();
|
||||
}
|
||||
}
|
|
@ -19,12 +19,7 @@ import com.android.emailcommon.provider.Policy;
|
|||
|
||||
interface IPolicyService {
|
||||
boolean isActive(in Policy policies);
|
||||
void policiesRequired(long accountId);
|
||||
void policiesUpdated(long accountId);
|
||||
void setAccountHoldFlag(long accountId, boolean newState);
|
||||
boolean isActiveAdmin();
|
||||
// This is about as oneway as you can get
|
||||
void setAccountPolicy(long accountId, in Policy policy, String securityKey);
|
||||
oneway void remoteWipe();
|
||||
boolean isSupported(in Policy policies);
|
||||
Policy clearUnsupportedPolicies(in Policy policies);
|
||||
}
|
107
emailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java
Normal file → Executable file
107
emailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java
Normal file → Executable file
|
@ -48,24 +48,6 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy clearUnsupportedPolicies(final Policy 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) {
|
||||
throw new ServiceUnavailableException("clearUnsupportedPolicies");
|
||||
} else {
|
||||
return (Policy)mReturn;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive(final Policy arg0) throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
|
@ -85,48 +67,14 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isActiveAdmin() throws RemoteException {
|
||||
public void setAccountPolicy(final long accountId, final Policy policy,
|
||||
final String securityKey) throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
public void run() throws RemoteException {
|
||||
mReturn = mService.isActiveAdmin();
|
||||
mService.setAccountPolicy(accountId, policy, securityKey);
|
||||
}
|
||||
}, "isActiveAdmin");
|
||||
}, "setAccountPolicy");
|
||||
waitForCompletion();
|
||||
if (DEBUG_PROXY) {
|
||||
Log.v(TAG, "isActiveAdmin: " + ((mReturn == null) ? "null" : mReturn));
|
||||
}
|
||||
if (mReturn == null) {
|
||||
throw new ServiceUnavailableException("isActiveAdmin");
|
||||
} else {
|
||||
return (Boolean)mReturn;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported(final Policy 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) {
|
||||
throw new ServiceUnavailableException("isSupported");
|
||||
} 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
|
||||
|
@ -147,15 +95,6 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
|
|||
}, "setAccountHoldFlag");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void policiesUpdated(final long arg0) throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
public void run() throws RemoteException {
|
||||
mService.policiesUpdated(arg0);
|
||||
}
|
||||
}, "policiesUpdated");
|
||||
}
|
||||
|
||||
// Static methods that encapsulate the proxy calls above
|
||||
public static boolean isActive(Context context, Policy policies) {
|
||||
try {
|
||||
|
@ -165,22 +104,6 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
|
|||
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 policiesUpdated(Context context, long accountId) {
|
||||
try {
|
||||
new PolicyServiceProxy(context).policiesUpdated(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);
|
||||
|
@ -189,14 +112,6 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -205,17 +120,11 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean isSupported(Context context, Policy policy) {
|
||||
public static void setAccountPolicy(Context context, long accountId, Policy policy,
|
||||
String securityKey) {
|
||||
try {
|
||||
return new PolicyServiceProxy(context).isSupported(policy);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Policy clearUnsupportedPolicies(Context context, Policy policy) {
|
||||
try {
|
||||
return new PolicyServiceProxy(context).clearUnsupportedPolicies(policy);
|
||||
new PolicyServiceProxy(context).setAccountPolicy(accountId, policy, securityKey);
|
||||
return;
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
throw new IllegalStateException("PolicyService transaction failed");
|
||||
|
|
|
@ -714,4 +714,15 @@ public class TextUtilities {
|
|||
|
||||
return (CharSequence)sb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether two Strings (either of which might be null) are the same; this is true
|
||||
* when both are null or both are Strings that are equal.
|
||||
*/
|
||||
public static boolean stringOrNullEquals(String a, String b) {
|
||||
if (a == null && b == null) return true;
|
||||
if (a != null && b != null && a.equals(b)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -856,14 +856,30 @@ as <xliff:g id="filename">%s</xliff:g>.</string>
|
|||
provisioning, just before jumping into system settings such as Device Policy grant,
|
||||
PIN/password, or encryption setup. [CHAR LIMIT=none] -->
|
||||
<string name="account_security_dialog_content_fmt">
|
||||
<xliff:g id="account">%s</xliff:g> requires that you update your security settings.</string>
|
||||
<xliff:g id="account">%s</xliff:g> requires that you update your security
|
||||
settings.</string>
|
||||
|
||||
<!-- Notification ticker when device security required (note: unused in Holo XL) -->
|
||||
<string name="security_notification_ticker_fmt">
|
||||
<string name="security_unsupported_ticker_fmt">
|
||||
Account \"<xliff:g id="account">%s</xliff:g>\" cannot be synced due to security
|
||||
requirements.</string>
|
||||
<!-- Notification ticker when device security required (note: unused in Holo XL) -->
|
||||
<string name="security_needed_ticker_fmt">
|
||||
Account \"<xliff:g id="account">%s</xliff:g>\" requires security settings update.
|
||||
</string>
|
||||
<!-- Notification ticker when device security required (note: unused in Holo XL) -->
|
||||
<string name="security_changed_ticker_fmt">
|
||||
Account \"<xliff:g id="account">%s</xliff:g>\" changed its security settings; no user
|
||||
action is required.
|
||||
</string>
|
||||
<!-- Notification content title when device security required [CHAR_LIMIT=30] -->
|
||||
<string name="security_notification_content_title">Security update required</string>
|
||||
<string name="security_notification_content_update_title">Security update required</string>
|
||||
<!-- Notification content title when device security policies have changed [CHAR_LIMIT=30] -->
|
||||
<string name="security_notification_content_change_title">Security policies have
|
||||
changed</string>
|
||||
<!-- Notification content title when device security policies cannot be met [CHAR_LIMIT=30] -->
|
||||
<string name="security_notification_content_unsupported_title">Security policies cannot be
|
||||
met</string>
|
||||
<!-- Title of the activity that dispatches changes to device security. Not normally seen. -->
|
||||
<string name="account_security_title">Device security</string>
|
||||
<!-- Additional diagnostic text when the email app asserts control of the phone.
|
||||
|
@ -937,15 +953,27 @@ as <xliff:g id="filename">%s</xliff:g>.</string>
|
|||
<string name="account_settings_mail_check_frequency_label">Inbox check frequency</string>
|
||||
<!-- On Settings screen, setting option name -->
|
||||
<string name="account_settings_incoming_label">Incoming settings</string>
|
||||
<!-- On Settings screen, setting option summary [CHAR LIMIT=64] -->
|
||||
<!-- On Settings screen, setting option summary [CHAR LIMIT=64] -->
|
||||
<string name="account_settings_incoming_summary">
|
||||
Username, password, and other incoming server settings</string>
|
||||
<!-- On Settings screen, setting option name -->
|
||||
<string name="account_settings_outgoing_label">Outgoing settings</string>
|
||||
<!-- On Settings screen, setting option summary [CHAR LIMIT=64] -->
|
||||
<!-- On Settings screen, setting option summary [CHAR LIMIT=64] -->
|
||||
<string name="account_settings_outgoing_summary">
|
||||
Username, password, and other outgoing server settings</string>
|
||||
<!-- On Settings screen, setting option name -->
|
||||
<string name="account_settings_enforced_label">Policies enforced</string>
|
||||
<!-- On Settings screen, setting option summary [CHAR LIMIT=64] -->
|
||||
<string name="account_settings_enforced_summary">None</string>
|
||||
<!-- On Settings screen, setting option name -->
|
||||
<string name="account_settings_unsupported_label">Unsupported policies</string>
|
||||
<!-- On Settings screen, setting option summary [CHAR LIMIT=64] -->
|
||||
<string name="account_settings_unsupported_summary">None</string>
|
||||
<!-- On Settings screen, label for button that attempts to sync the account -->
|
||||
<string name="account_settings_retry_label">Attempt sync</string>
|
||||
<!-- On Settings screen, summmary for button that attempts to sync an account [CHAR LIMIT=64] -->
|
||||
<string name="account_settings_retry_summary">Tap here to attempt to sync this account (i.e. if server settings have changed)</string>
|
||||
<!-- On Settings screen, setting option name -->
|
||||
<string name="account_settings_description_label">Account name</string>
|
||||
<!-- On Settings screen, setting option name -->
|
||||
<string name="account_settings_name_label">Your name</string>
|
||||
|
@ -963,6 +991,8 @@ as <xliff:g id="filename">%s</xliff:g>.</string>
|
|||
<string name="account_settings_notifications">Notification settings</string>
|
||||
<!-- On Settings screen, section heading for data usage [CHAR LIMIT=70] -->
|
||||
<string name="account_settings_data_usage">Data usage</string>
|
||||
<!-- On Settings screen, section heading -->
|
||||
<string name="account_settings_policies">Security policies</string>
|
||||
|
||||
<!-- On settings screen, dialog heading informing user to edit a quick response -->
|
||||
<string name="edit_quick_response_dialog">Edit quick response</string>
|
||||
|
@ -1215,4 +1245,17 @@ as <xliff:g id="filename">%s</xliff:g>.</string>
|
|||
<!-- A long placeholder string to be used in template XML files message_list_item_*.xml.
|
||||
Used only in layout computation, and never actually exposed to the user. -->
|
||||
<string name="long_string" translatable="false">looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong</string>
|
||||
|
||||
<!-- A policy disallowing the user of the device's camera [CHAR LIMIT=40] -->
|
||||
<string name="policy_dont_allow_camera">Disallow use of the device\'s camera</string>
|
||||
<!-- A policy requiring a device lock screen password [CHAR LIMIT=40] -->
|
||||
<string name="policy_require_password">Require device password</string>
|
||||
<!-- A policy disallowing the reuse of recent passwords [CHAR LIMIT=40] -->
|
||||
<string name="policy_password_history">Restrict the reuse of recent passwords</string>
|
||||
<!-- A policy that forces a password to expire after a set period of time [CHAR LIMIT=40] -->
|
||||
<string name="policy_password_expiration">Require passwords to expire</string>
|
||||
<!-- A policy requiring a maximum amount of time the device can sit idle before the lock screen
|
||||
is activated [CHAR LIMIT=40] -->
|
||||
<string name="policy_screen_timeout">Require an idle device to lock its screen</string>
|
||||
</resources>
|
||||
|
||||
|
|
|
@ -135,6 +135,26 @@
|
|||
android:summary="@string/account_settings_outgoing_summary" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="account_policies"
|
||||
android:title="@string/account_settings_policies">
|
||||
|
||||
<com.android.email.activity.setup.PolicyListPreference
|
||||
android:key="policies_enforced"
|
||||
android:title="@string/account_settings_enforced_label"
|
||||
android:summary="@string/account_settings_enforced_summary" />
|
||||
|
||||
<com.android.email.activity.setup.PolicyListPreference
|
||||
android:key="policies_unsupported"
|
||||
android:title="@string/account_settings_unsupported_label"
|
||||
android:summary="@string/account_settings_unsupported_summary" />
|
||||
|
||||
<Preference
|
||||
android:key="policies_retry_account"
|
||||
android:title="@string/account_settings_retry_label"
|
||||
android:summary="@string/account_settings_retry_summary" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/account_settings_category_delete_account">
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ import com.android.emailcommon.provider.EmailContent.MailboxColumns;
|
|||
import com.android.emailcommon.provider.EmailContent.Message;
|
||||
import com.android.emailcommon.provider.EmailContent.MessageColumns;
|
||||
import com.android.emailcommon.provider.Mailbox;
|
||||
import com.android.emailcommon.utility.EmailAsyncTask;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
|
@ -62,7 +63,6 @@ import java.util.HashSet;
|
|||
* Class that manages notifications.
|
||||
*/
|
||||
public class NotificationController {
|
||||
private static final int NOTIFICATION_ID_SECURITY_NEEDED = 1;
|
||||
/** Reserved for {@link com.android.exchange.CalendarSyncEnabler} */
|
||||
@SuppressWarnings("unused")
|
||||
private static final int NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED = 2;
|
||||
|
@ -70,8 +70,11 @@ public class NotificationController {
|
|||
private static final int NOTIFICATION_ID_PASSWORD_EXPIRING = 4;
|
||||
private static final int NOTIFICATION_ID_PASSWORD_EXPIRED = 5;
|
||||
|
||||
private static final int NOTIFICATION_ID_BASE_MASK = 0xF0000000;
|
||||
private static final int NOTIFICATION_ID_BASE_NEW_MESSAGES = 0x10000000;
|
||||
private static final int NOTIFICATION_ID_BASE_LOGIN_WARNING = 0x20000000;
|
||||
private static final int NOTIFICATION_ID_BASE_SECURITY_NEEDED = 0x30000000;
|
||||
private static final int NOTIFICATION_ID_BASE_SECURITY_CHANGED = 0x40000000;
|
||||
|
||||
/** Selection to retrieve accounts that should we notify user for changes */
|
||||
private final static String NOTIFIED_ACCOUNT_SELECTION =
|
||||
|
@ -144,7 +147,7 @@ public class NotificationController {
|
|||
private boolean needsOngoingNotification(int notificationId) {
|
||||
// "Security needed" must be ongoing so that the user doesn't close it; otherwise, sync will
|
||||
// be prevented until a reboot. Consider also doing this for password expired.
|
||||
return notificationId == NOTIFICATION_ID_SECURITY_NEEDED;
|
||||
return (notificationId & NOTIFICATION_ID_BASE_MASK) == NOTIFICATION_ID_BASE_SECURITY_NEEDED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -591,24 +594,67 @@ public class NotificationController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Show (or update) a security needed notification. The given account is used to update
|
||||
* the display text, but, all accounts share the same notification ID.
|
||||
* Show (or update) a security needed notification. If tapped, the user is taken to a
|
||||
* dialog asking whether he wants to update his settings.
|
||||
*/
|
||||
public void showSecurityNeededNotification(Account account) {
|
||||
Intent intent = AccountSecurity.actionUpdateSecurityIntent(mContext, account.mId, true);
|
||||
String accountName = account.getDisplayName();
|
||||
String ticker =
|
||||
mContext.getString(R.string.security_notification_ticker_fmt, accountName);
|
||||
String title = mContext.getString(R.string.security_notification_content_title);
|
||||
mContext.getString(R.string.security_needed_ticker_fmt, accountName);
|
||||
String title = mContext.getString(R.string.security_notification_content_update_title);
|
||||
showAccountNotification(account, ticker, title, accountName, intent,
|
||||
NOTIFICATION_ID_SECURITY_NEEDED);
|
||||
(int)(NOTIFICATION_ID_BASE_SECURITY_NEEDED + account.mId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the security needed notification.
|
||||
* Show (or update) a security changed notification. If tapped, the user is taken to the
|
||||
* account settings screen where he can view the list of enforced policies
|
||||
*/
|
||||
public void showSecurityChangedNotification(Account account) {
|
||||
Intent intent = AccountSettings.createAccountSettingsIntent(mContext, account.mId, null);
|
||||
String accountName = account.getDisplayName();
|
||||
String ticker =
|
||||
mContext.getString(R.string.security_changed_ticker_fmt, accountName);
|
||||
String title = mContext.getString(R.string.security_notification_content_change_title);
|
||||
showAccountNotification(account, ticker, title, accountName, intent,
|
||||
(int)(NOTIFICATION_ID_BASE_SECURITY_CHANGED + account.mId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show (or update) a security unsupported notification. If tapped, the user is taken to the
|
||||
* account settings screen where he can view the list of unsupported policies
|
||||
*/
|
||||
public void showSecurityUnsupportedNotification(Account account) {
|
||||
Intent intent = AccountSettings.createAccountSettingsIntent(mContext, account.mId, null);
|
||||
String accountName = account.getDisplayName();
|
||||
String ticker =
|
||||
mContext.getString(R.string.security_unsupported_ticker_fmt, accountName);
|
||||
String title = mContext.getString(R.string.security_notification_content_unsupported_title);
|
||||
showAccountNotification(account, ticker, title, accountName, intent,
|
||||
(int)(NOTIFICATION_ID_BASE_SECURITY_NEEDED + account.mId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all security needed notifications.
|
||||
*/
|
||||
public void cancelSecurityNeededNotification() {
|
||||
mNotificationManager.cancel(NOTIFICATION_ID_SECURITY_NEEDED);
|
||||
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Cursor c = mContext.getContentResolver().query(Account.CONTENT_URI,
|
||||
Account.ID_PROJECTION, null, null, null);
|
||||
try {
|
||||
while (c.moveToNext()) {
|
||||
long id = c.getLong(Account.ID_PROJECTION_COLUMN);
|
||||
mNotificationManager.cancel(
|
||||
(int)(NOTIFICATION_ID_BASE_SECURITY_NEEDED + id));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
c.close();
|
||||
}
|
||||
}});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,11 +20,15 @@ import android.app.admin.DeviceAdminInfo;
|
|||
import android.app.admin.DeviceAdminReceiver;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.service.EmailBroadcastProcessorService;
|
||||
|
@ -34,9 +38,12 @@ import com.android.emailcommon.provider.EmailContent;
|
|||
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
||||
import com.android.emailcommon.provider.EmailContent.PolicyColumns;
|
||||
import com.android.emailcommon.provider.Policy;
|
||||
import com.android.emailcommon.utility.TextUtilities;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Utility functions to support reading and writing security policies, and handshaking the device
|
||||
* into and out of various security states.
|
||||
|
@ -204,11 +211,12 @@ public class SecurityPolicy {
|
|||
}
|
||||
|
||||
/**
|
||||
* API: Report that policies may have been updated due to rewriting values in an Account.
|
||||
* @param accountId the account that has been updated, -1 if unknown/deleted
|
||||
* API: Report that policies may have been updated due to rewriting values in an Account; we
|
||||
* clear the aggregate policy (so it can be recomputed) and set the policies in the DPM
|
||||
*/
|
||||
public synchronized void policiesUpdated(long accountId) {
|
||||
public synchronized void policiesUpdated() {
|
||||
mAggregatePolicy = null;
|
||||
setActivePolicies();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -221,58 +229,7 @@ public class SecurityPolicy {
|
|||
if (Email.DEBUG) {
|
||||
Log.d(TAG, "reducePolicies");
|
||||
}
|
||||
policiesUpdated(-1);
|
||||
setActivePolicies();
|
||||
}
|
||||
|
||||
/**
|
||||
* API: Query if the proposed set of policies are supported on the device.
|
||||
*
|
||||
* @param policy the polices that were requested
|
||||
* @return boolean if supported
|
||||
*/
|
||||
public boolean isSupported(Policy policy) {
|
||||
// IMPLEMENTATION: At this time, the only policy which might not be supported is
|
||||
// encryption (which requires low-level systems support). Other policies are fully
|
||||
// supported by the framework and do not need to be checked.
|
||||
if (policy.mRequireEncryption) {
|
||||
int encryptionStatus = getDPM().getStorageEncryptionStatus();
|
||||
if (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we ever support devices that can't disable cameras for any reason, we should
|
||||
// indicate as such in the mDontAllowCamera policy
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* API: Remove any unsupported policies
|
||||
*
|
||||
* This is used when we have a set of polices that have been requested, but the server
|
||||
* is willing to allow unsupported policies to be considered optional.
|
||||
*
|
||||
* @param policy the polices that were requested
|
||||
* @return the same PolicySet if all are supported; A replacement PolicySet if any
|
||||
* unsupported policies were removed
|
||||
*/
|
||||
public Policy clearUnsupportedPolicies(Policy policy) {
|
||||
// IMPLEMENTATION: At this time, the only policy which might not be supported is
|
||||
// encryption (which requires low-level systems support). Other policies are fully
|
||||
// supported by the framework and do not need to be checked.
|
||||
if (policy.mRequireEncryption) {
|
||||
int encryptionStatus = getDPM().getStorageEncryptionStatus();
|
||||
if (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
|
||||
policy.mRequireEncryption = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we ever support devices that can't disable cameras for any reason, we should
|
||||
// clear the mDontAllowCamera policy
|
||||
|
||||
return policy;
|
||||
policiesUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -303,6 +260,9 @@ public class SecurityPolicy {
|
|||
if ((reasons & INACTIVE_NEED_ENCRYPTION) != 0) {
|
||||
sb.append("encryption ");
|
||||
}
|
||||
if ((reasons & INACTIVE_PROTOCOL_POLICIES) != 0) {
|
||||
sb.append("protocol ");
|
||||
}
|
||||
Log.d(TAG, sb.toString());
|
||||
}
|
||||
return reasons == 0;
|
||||
|
@ -328,6 +288,11 @@ public class SecurityPolicy {
|
|||
*/
|
||||
public final static int INACTIVE_NEED_ENCRYPTION = 8;
|
||||
|
||||
/**
|
||||
* Return bits from isActive: Protocol-specific policies cannot be enforced
|
||||
*/
|
||||
public final static int INACTIVE_PROTOCOL_POLICIES = 16;
|
||||
|
||||
/**
|
||||
* API: Query used to determine if a given policy is "active" (the device is operating at
|
||||
* the required security level).
|
||||
|
@ -418,6 +383,10 @@ public class SecurityPolicy {
|
|||
// password failures are counted locally - no test required here
|
||||
// no check required for remote wipe (it's supported, if we're the admin)
|
||||
|
||||
if (policy.mProtocolPoliciesUnsupported != null) {
|
||||
reasons |= INACTIVE_PROTOCOL_POLICIES;
|
||||
}
|
||||
|
||||
// If we made it all the way, reasons == 0 here. Otherwise it's a list of grievances.
|
||||
return reasons;
|
||||
}
|
||||
|
@ -514,24 +483,122 @@ public class SecurityPolicy {
|
|||
Account account = Account.restoreAccountWithId(mContext, accountId);
|
||||
// In case the account has been deleted, just return
|
||||
if (account == null) return;
|
||||
if (account.mPolicyKey == 0) return;
|
||||
Policy policy = Policy.restorePolicyWithId(mContext, account.mPolicyKey);
|
||||
if (policy == null) return;
|
||||
if (Email.DEBUG) {
|
||||
if (account.mPolicyKey == 0) {
|
||||
Log.d(TAG, "policiesRequired for " + account.mDisplayName + ": none");
|
||||
} else {
|
||||
Policy policy = Policy.restorePolicyWithId(mContext, account.mPolicyKey);
|
||||
if (policy == null) {
|
||||
Log.w(TAG, "No policy??");
|
||||
} else {
|
||||
Log.d(TAG, "policiesRequired for " + account.mDisplayName + ": " + policy);
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "policiesRequired for " + account.mDisplayName + ": " + policy);
|
||||
}
|
||||
|
||||
// Mark the account as "on hold".
|
||||
setAccountHoldFlag(mContext, account, true);
|
||||
|
||||
// Put up a notification
|
||||
NotificationController.getInstance(mContext).showSecurityNeededNotification(account);
|
||||
// Put up an appropriate notification
|
||||
if (policy.mProtocolPoliciesUnsupported == null) {
|
||||
NotificationController.getInstance(mContext).showSecurityNeededNotification(account);
|
||||
} else {
|
||||
NotificationController.getInstance(mContext).showSecurityUnsupportedNotification(
|
||||
account);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearAccountPolicy(Context context, Account account) {
|
||||
setAccountPolicy(context, account, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the policy for an account atomically; this also removes any other policy associated with
|
||||
* the account and sets the policy key for the account. If policy is null, the policyKey is
|
||||
* set to 0 and the securitySyncKey to null. Also, update the account object to reflect the
|
||||
* current policyKey and securitySyncKey
|
||||
* @param context the caller's context
|
||||
* @param account the account whose policy is to be set
|
||||
* @param policy the policy to set, or null if we're clearing the policy
|
||||
* @param securitySyncKey the security sync key for this account (ignored if policy is null)
|
||||
*/
|
||||
public static void setAccountPolicy(Context context, Account account, Policy policy,
|
||||
String securitySyncKey) {
|
||||
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
|
||||
|
||||
// Make sure this is a valid policy set
|
||||
if (policy != null) {
|
||||
policy.normalize();
|
||||
// Add the new policy (no account will yet reference this)
|
||||
ops.add(ContentProviderOperation.newInsert(
|
||||
Policy.CONTENT_URI).withValues(policy.toContentValues()).build());
|
||||
// Make the policyKey of the account our newly created policy, and set the sync key
|
||||
ops.add(ContentProviderOperation.newUpdate(
|
||||
ContentUris.withAppendedId(Account.CONTENT_URI, account.mId))
|
||||
.withValueBackReference(AccountColumns.POLICY_KEY, 0)
|
||||
.withValue(AccountColumns.SECURITY_SYNC_KEY, securitySyncKey)
|
||||
.build());
|
||||
} else {
|
||||
ops.add(ContentProviderOperation.newUpdate(
|
||||
ContentUris.withAppendedId(Account.CONTENT_URI, account.mId))
|
||||
.withValue(AccountColumns.SECURITY_SYNC_KEY, null)
|
||||
.withValue(AccountColumns.POLICY_KEY, 0)
|
||||
.build());
|
||||
}
|
||||
|
||||
// Delete the previous policy associated with this account, if any
|
||||
if (account.mPolicyKey > 0) {
|
||||
ops.add(ContentProviderOperation.newDelete(
|
||||
ContentUris.withAppendedId(
|
||||
Policy.CONTENT_URI, account.mPolicyKey)).build());
|
||||
}
|
||||
|
||||
try {
|
||||
context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops);
|
||||
account.refresh(context);
|
||||
} catch (RemoteException e) {
|
||||
// This is fatal to a remote process
|
||||
throw new IllegalStateException("Exception setting account policy.");
|
||||
} catch (OperationApplicationException e) {
|
||||
// Can't happen; our provider doesn't throw this exception
|
||||
}
|
||||
}
|
||||
|
||||
public void setAccountPolicy(long accountId, Policy policy, String securityKey) {
|
||||
Account account = Account.restoreAccountWithId(mContext, accountId);
|
||||
Policy oldPolicy = null;
|
||||
if (account.mPolicyKey > 0) {
|
||||
oldPolicy = Policy.restorePolicyWithId(mContext, account.mPolicyKey);
|
||||
}
|
||||
boolean policyChanged = !oldPolicy.equals(policy);
|
||||
if (!policyChanged && (TextUtilities.stringOrNullEquals(securityKey,
|
||||
account.mSecuritySyncKey))) {
|
||||
Log.d(Logging.LOG_TAG, "setAccountPolicy; policy unchanged");
|
||||
} else {
|
||||
setAccountPolicy(mContext, account, policy, securityKey);
|
||||
policiesUpdated();
|
||||
}
|
||||
|
||||
boolean setHold = false;
|
||||
if (policy.mProtocolPoliciesUnsupported != null) {
|
||||
// We can't support this, reasons in unsupportedRemotePolicies
|
||||
Log.d(Logging.LOG_TAG,
|
||||
"Notify policies for " + account.mDisplayName + " not supported.");
|
||||
setHold = true;
|
||||
NotificationController.getInstance(mContext).showSecurityUnsupportedNotification(
|
||||
account);
|
||||
// Erase data
|
||||
Controller.getInstance(mContext).deleteSyncedDataSync(accountId);
|
||||
} else if (isActive(policy)) {
|
||||
if (policyChanged) {
|
||||
Log.d(Logging.LOG_TAG, "Notify policies for " + account.mDisplayName + " changed.");
|
||||
// Notify that policies changed
|
||||
NotificationController.getInstance(mContext).showSecurityChangedNotification(
|
||||
account);
|
||||
}
|
||||
} else {
|
||||
setHold = true;
|
||||
Log.d(Logging.LOG_TAG, "Notify policies for " + account.mDisplayName +
|
||||
" are not being enforced.");
|
||||
// Put up a notification
|
||||
NotificationController.getInstance(mContext).showSecurityNeededNotification(account);
|
||||
}
|
||||
// Set/clear the account hold.
|
||||
setAccountHoldFlag(mContext, account, setHold);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -600,7 +667,7 @@ public class SecurityPolicy {
|
|||
} finally {
|
||||
c.close();
|
||||
}
|
||||
policiesUpdated(-1);
|
||||
policiesUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,6 +41,7 @@ import com.android.emailcommon.provider.Account;
|
|||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.emailcommon.provider.EmailContent.Message;
|
||||
import com.android.emailcommon.provider.Mailbox;
|
||||
import com.android.emailcommon.provider.Policy;
|
||||
import com.android.emailcommon.utility.EmailAsyncTask;
|
||||
import com.android.emailcommon.utility.IntentUtilities;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
|
@ -414,7 +415,19 @@ public class Welcome extends Activity {
|
|||
@Override
|
||||
public void onAccountSecurityHold(long accountId) {
|
||||
cleanUp();
|
||||
|
||||
// If we can't find the account, we know what to do
|
||||
Account account = Account.restoreAccountWithId(Welcome.this, accountId);
|
||||
if (account == null) {
|
||||
onAccountNotFound();
|
||||
return;
|
||||
}
|
||||
// If there's no policy or it's "unsupported", act like the account doesn't exist
|
||||
Policy policy = Policy.restorePolicyWithId(Welcome.this, account.mPolicyKey);
|
||||
if (policy == null || (policy.mProtocolPoliciesUnsupported != null)) {
|
||||
onAccountNotFound();
|
||||
return;
|
||||
}
|
||||
// Otherwise, try advancing security
|
||||
ActivityHelper.showSecurityHoldDialog(Welcome.this, accountId);
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -469,8 +469,10 @@ public class AccountCheckSettingsFragment extends Fragment {
|
|||
EmailServiceProxy.VALIDATE_BUNDLE_POLICY_SET));
|
||||
return new MessagingException(resultCode, mStoreHost);
|
||||
} else if (resultCode == MessagingException.SECURITY_POLICIES_UNSUPPORTED) {
|
||||
String[] data = bundle.getStringArray(
|
||||
EmailServiceProxy.VALIDATE_BUNDLE_UNSUPPORTED_POLICIES);
|
||||
Policy policy = (Policy)bundle.getParcelable(
|
||||
EmailServiceProxy.VALIDATE_BUNDLE_POLICY_SET);
|
||||
String unsupported = policy.mProtocolPoliciesUnsupported;
|
||||
String[] data = unsupported.split("" + Policy.POLICY_STRING_DELIMITER);
|
||||
return new MessagingException(resultCode, mStoreHost, data);
|
||||
} else if (resultCode != MessagingException.NO_ERROR) {
|
||||
String errorMessage =
|
||||
|
|
|
@ -119,6 +119,7 @@ public class AccountSecurity extends Activity {
|
|||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Special handling for password expiration events
|
||||
if (passwordExpiring || passwordExpired) {
|
||||
FragmentManager fm = getFragmentManager();
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.content.ContentValues;
|
|||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
|
@ -43,6 +44,7 @@ import android.util.Log;
|
|||
|
||||
import com.android.email.Email;
|
||||
import com.android.email.R;
|
||||
import com.android.email.SecurityPolicy;
|
||||
import com.android.email.mail.Sender;
|
||||
import com.android.emailcommon.AccountManagerTypes;
|
||||
import com.android.emailcommon.CalendarProviderStub;
|
||||
|
@ -51,8 +53,11 @@ import com.android.emailcommon.mail.MessagingException;
|
|||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.provider.Policy;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Fragment containing the main logic for account settings. This also calls out to other
|
||||
* fragments for server settings.
|
||||
|
@ -81,6 +86,10 @@ public class AccountSettingsFragment extends PreferenceFragment {
|
|||
private static final String PREFERENCE_VIBRATE_WHEN = "account_settings_vibrate_when";
|
||||
private static final String PREFERENCE_RINGTONE = "account_ringtone";
|
||||
private static final String PREFERENCE_CATEGORY_SERVER = "account_servers";
|
||||
private static final String PREFERENCE_CATEGORY_POLICIES = "account_policies";
|
||||
private static final String PREFERENCE_POLICIES_ENFORCED = "policies_enforced";
|
||||
private static final String PREFERENCE_POLICIES_UNSUPPORTED = "policies_unsupported";
|
||||
private static final String PREFERENCE_POLICIES_RETRY_ACCOUNT = "policies_retry_account";
|
||||
private static final String PREFERENCE_INCOMING = "incoming";
|
||||
private static final String PREFERENCE_OUTGOING = "outgoing";
|
||||
private static final String PREFERENCE_SYNC_CONTACTS = "account_sync_contacts";
|
||||
|
@ -351,6 +360,46 @@ public class AccountSettingsFragment extends PreferenceFragment {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From a Policy, create and return an ArrayList of Strings that describe (simply) those
|
||||
* policies that are supported by the OS. At the moment, the strings are simple (e.g.
|
||||
* "password required"); we should probably add more information (# characters, etc.), though
|
||||
*/
|
||||
private ArrayList<String> getSystemPoliciesList(Policy policy) {
|
||||
Resources res = mContext.getResources();
|
||||
ArrayList<String> policies = new ArrayList<String>();
|
||||
if (policy.mPasswordMode != Policy.PASSWORD_MODE_NONE) {
|
||||
policies.add(res.getString(R.string.policy_require_password));
|
||||
}
|
||||
if (policy.mPasswordHistory > 0) {
|
||||
policies.add(res.getString(R.string.policy_password_history));
|
||||
}
|
||||
if (policy.mPasswordExpirationDays > 0) {
|
||||
policies.add(res.getString(R.string.policy_password_expiration));
|
||||
}
|
||||
if (policy.mMaxScreenLockTime > 0) {
|
||||
policies.add(res.getString(R.string.policy_screen_timeout));
|
||||
}
|
||||
if (policy.mDontAllowCamera) {
|
||||
policies.add(res.getString(R.string.policy_dont_allow_camera));
|
||||
}
|
||||
return policies;
|
||||
}
|
||||
|
||||
private void setPolicyListSummary(ArrayList<String> policies, String policiesToAdd,
|
||||
String preferenceName) {
|
||||
Policy.addPolicyStringToList(policiesToAdd, policies);
|
||||
if (policies.size() > 0) {
|
||||
Preference p = findPreference(preferenceName);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String desc: policies) {
|
||||
sb.append(desc);
|
||||
sb.append('\n');
|
||||
}
|
||||
p.setSummary(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load account data into preference UI
|
||||
*/
|
||||
|
@ -397,7 +446,6 @@ public class AccountSettingsFragment extends PreferenceFragment {
|
|||
});
|
||||
|
||||
mAccountSignature = (EditTextPreference) findPreference(PREFERENCE_SIGNATURE);
|
||||
String signature = mAccount.getSignature();
|
||||
mAccountSignature.setText(mAccount.getSignature());
|
||||
mAccountSignature.setOnPreferenceChangeListener(
|
||||
new Preference.OnPreferenceChangeListener() {
|
||||
|
@ -520,6 +568,45 @@ public class AccountSettingsFragment extends PreferenceFragment {
|
|||
notificationsCategory.removePreference(mAccountVibrateWhen);
|
||||
}
|
||||
|
||||
final Preference retryAccount = findPreference(PREFERENCE_POLICIES_RETRY_ACCOUNT);
|
||||
final PreferenceCategory policiesCategory = (PreferenceCategory) findPreference(
|
||||
PREFERENCE_CATEGORY_POLICIES);
|
||||
if (mAccount.mPolicyKey > 0) {
|
||||
// Make sure we have most recent data from account
|
||||
mAccount.refresh(mContext);
|
||||
Policy policy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey);
|
||||
if (policy == null) {
|
||||
// The account has been deleted? Crazy, but not impossible
|
||||
return;
|
||||
}
|
||||
if (policy.mProtocolPoliciesEnforced != null) {
|
||||
ArrayList<String> policies = getSystemPoliciesList(policy);
|
||||
setPolicyListSummary(policies, policy.mProtocolPoliciesEnforced,
|
||||
PREFERENCE_POLICIES_ENFORCED);
|
||||
}
|
||||
if (policy.mProtocolPoliciesUnsupported != null) {
|
||||
ArrayList<String> policies = new ArrayList<String>();
|
||||
setPolicyListSummary(policies, policy.mProtocolPoliciesUnsupported,
|
||||
PREFERENCE_POLICIES_UNSUPPORTED);
|
||||
} else {
|
||||
// Don't show "retry" unless we have unsupported policies
|
||||
policiesCategory.removePreference(retryAccount);
|
||||
}
|
||||
} else {
|
||||
// Remove the category completely if there are no policies
|
||||
getPreferenceScreen().removePreference(policiesCategory);
|
||||
}
|
||||
|
||||
retryAccount.setOnPreferenceClickListener(
|
||||
new Preference.OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
// Release the account
|
||||
SecurityPolicy.setAccountHoldFlag(mContext, mAccount, false);
|
||||
// Remove the preference
|
||||
policiesCategory.removePreference(retryAccount);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener(
|
||||
new Preference.OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.activity.setup;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* Simple text preference allowing a large number of lines
|
||||
*/
|
||||
public class PolicyListPreference extends Preference {
|
||||
// Arbitrary, but large number (we don't, and won't, have nearly this many)
|
||||
public static final int MAX_POLICIES = 24;
|
||||
|
||||
public PolicyListPreference(Context ctx, AttributeSet attrs, int defStyle) {
|
||||
super(ctx, attrs, defStyle);
|
||||
}
|
||||
|
||||
public PolicyListPreference(Context ctx, AttributeSet attrs) {
|
||||
super(ctx, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(View view) {
|
||||
super.onBindView(view);
|
||||
((TextView)view.findViewById(android.R.id.summary)).setMaxLines(MAX_POLICIES);
|
||||
}
|
||||
}
|
|
@ -160,8 +160,9 @@ public class EmailProvider extends ContentProvider {
|
|||
// Version 26: Update IMAP accounts to add FLAG_SUPPORTS_SEARCH flag
|
||||
// Version 27: Add protocolSearchInfo to Message table
|
||||
// Version 28: Add notifiedMessageId and notifiedMessageCount to Account
|
||||
// Version 29: Add protocolPoliciesEnforced and protocolPoliciesUnsupported to Policy
|
||||
|
||||
public static final int DATABASE_VERSION = 28;
|
||||
public static final int DATABASE_VERSION = 29;
|
||||
|
||||
// Any changes to the database format *must* include update-in-place code.
|
||||
// Original version: 2
|
||||
|
@ -651,7 +652,9 @@ public class EmailProvider extends ContentProvider {
|
|||
+ PolicyColumns.MAX_HTML_TRUNCATION_SIZE + " integer, "
|
||||
+ PolicyColumns.MAX_EMAIL_LOOKBACK + " integer, "
|
||||
+ PolicyColumns.MAX_CALENDAR_LOOKBACK + " integer, "
|
||||
+ PolicyColumns.PASSWORD_RECOVERY_ENABLED + " integer"
|
||||
+ PolicyColumns.PASSWORD_RECOVERY_ENABLED + " integer, "
|
||||
+ PolicyColumns.PROTOCOL_POLICIES_ENFORCED + " text, "
|
||||
+ PolicyColumns.PROTOCOL_POLICIES_UNSUPPORTED + " text"
|
||||
+ ");";
|
||||
db.execSQL("create table " + Policy.TABLE_NAME + s);
|
||||
}
|
||||
|
@ -1341,10 +1344,22 @@ public class EmailProvider extends ContentProvider {
|
|||
+ " add column " + Account.NOTIFIED_MESSAGE_COUNT + " integer;");
|
||||
} catch (SQLException e) {
|
||||
// Shouldn't be needed unless we're debugging and interrupt the process
|
||||
Log.w(TAG, "Exception upgrading EmailProvider.db from 27 to 27 " + e);
|
||||
Log.w(TAG, "Exception upgrading EmailProvider.db from 27 to 28 " + e);
|
||||
}
|
||||
oldVersion = 28;
|
||||
}
|
||||
if (oldVersion == 28) {
|
||||
try {
|
||||
db.execSQL("alter table " + Policy.TABLE_NAME
|
||||
+ " add column " + Policy.PROTOCOL_POLICIES_ENFORCED + " text;");
|
||||
db.execSQL("alter table " + Policy.TABLE_NAME
|
||||
+ " add column " + Policy.PROTOCOL_POLICIES_UNSUPPORTED + " text;");
|
||||
} catch (SQLException e) {
|
||||
// Shouldn't be needed unless we're debugging and interrupt the process
|
||||
Log.w(TAG, "Exception upgrading EmailProvider.db from 28 to 29 " + e);
|
||||
}
|
||||
oldVersion = 29;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
|
||||
package com.android.email.service;
|
||||
|
||||
import com.android.email.SecurityPolicy;
|
||||
import com.android.emailcommon.provider.Policy;
|
||||
import com.android.emailcommon.service.IPolicyService;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import com.android.email.SecurityPolicy;
|
||||
import com.android.emailcommon.provider.Policy;
|
||||
import com.android.emailcommon.service.IPolicyService;
|
||||
|
||||
public class PolicyService extends Service {
|
||||
|
||||
private SecurityPolicy mSecurityPolicy;
|
||||
|
@ -35,32 +35,16 @@ public class PolicyService extends Service {
|
|||
return mSecurityPolicy.isActive(policy);
|
||||
}
|
||||
|
||||
public void policiesRequired(long accountId) {
|
||||
mSecurityPolicy.policiesRequired(accountId);
|
||||
}
|
||||
|
||||
public void policiesUpdated(long accountId) {
|
||||
mSecurityPolicy.policiesUpdated(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(Policy policy) {
|
||||
return mSecurityPolicy.isSupported(policy);
|
||||
}
|
||||
|
||||
public Policy clearUnsupportedPolicies(Policy policy) {
|
||||
return mSecurityPolicy.clearUnsupportedPolicies(policy);
|
||||
public void setAccountPolicy(long accountId, Policy policy, String securityKey) {
|
||||
mSecurityPolicy.setAccountPolicy(accountId, policy, securityKey);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
Account a3 = ProviderTestUtils.setupAccount("sec-3", true, mMockContext);
|
||||
Policy p3ain = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0,
|
||||
false, false);
|
||||
Policy.setAccountPolicy(mMockContext, a3, p3ain, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a3, p3ain, null);
|
||||
Policy p3aout = mSecurityPolicy.computeAggregatePolicy();
|
||||
assertNotNull(p3aout);
|
||||
assertEquals(p3ain, p3aout);
|
||||
|
@ -150,7 +150,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
// Repeat that test with fully-populated policies
|
||||
Policy p3bin = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 15, 16, false, 6, 2, 3,
|
||||
false, false);
|
||||
Policy.setAccountPolicy(mMockContext, a3, p3bin, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a3, p3bin, null);
|
||||
Policy p3bout = mSecurityPolicy.computeAggregatePolicy();
|
||||
assertNotNull(p3bout);
|
||||
assertEquals(p3bin, p3bout);
|
||||
|
@ -166,7 +166,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
Policy p4in = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 0, 5, 7,
|
||||
false, true);
|
||||
Account a4 = ProviderTestUtils.setupAccount("sec-4", true, mMockContext);
|
||||
Policy.setAccountPolicy(mMockContext, a4, p4in, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a4, p4in, null);
|
||||
Policy p4out = mSecurityPolicy.computeAggregatePolicy();
|
||||
assertNotNull(p4out);
|
||||
assertEquals(20, p4out.mPasswordMinLength);
|
||||
|
@ -192,7 +192,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
Policy p5in = setupPolicy(4, Policy.PASSWORD_MODE_SIMPLE, 5, 6, true, 1, 0, 0,
|
||||
true, false);
|
||||
Account a5 = ProviderTestUtils.setupAccount("sec-5", true, mMockContext);
|
||||
Policy.setAccountPolicy(mMockContext, a5, p5in, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a5, p5in, null);
|
||||
Policy p5out = mSecurityPolicy.computeAggregatePolicy();
|
||||
assertNotNull(p5out);
|
||||
assertEquals(20, p5out.mPasswordMinLength);
|
||||
|
@ -236,17 +236,17 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
long accountId = account.mId;
|
||||
Policy initial = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0,
|
||||
false, false);
|
||||
Policy.setAccountPolicy(mMockContext, accountId, initial, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, account, initial, null);
|
||||
|
||||
long oldKey = assertAccountPolicyConsistent(account.mId, 0);
|
||||
|
||||
Policy updated = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0,
|
||||
false, false);
|
||||
Policy.setAccountPolicy(mMockContext, accountId, updated, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, account, updated, null);
|
||||
oldKey = assertAccountPolicyConsistent(account.mId, oldKey);
|
||||
|
||||
// Remove the policy
|
||||
Policy.clearAccountPolicy(
|
||||
SecurityPolicy.clearAccountPolicy(
|
||||
mMockContext, Account.restoreAccountWithId(mMockContext, accountId));
|
||||
assertNull("old policy not cleaned up",
|
||||
Policy.restorePolicyWithId(mMockContext, oldKey));
|
||||
|
@ -306,15 +306,15 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
Account a1 = ProviderTestUtils.setupAccount("disable-1", true, mMockContext);
|
||||
Policy p1 = setupPolicy(10, Policy.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0,
|
||||
false, false);
|
||||
Policy.setAccountPolicy(mMockContext, a1, p1, "security-sync-key-1");
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a1, p1, "security-sync-key-1");
|
||||
|
||||
Account a2 = ProviderTestUtils.setupAccount("disable-2", true, mMockContext);
|
||||
Policy p2 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 0, 0, 0,
|
||||
false, false);
|
||||
Policy.setAccountPolicy(mMockContext, a2, p2, "security-sync-key-2");
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a2, p2, "security-sync-key-2");
|
||||
|
||||
Account a3 = ProviderTestUtils.setupAccount("disable-3", true, mMockContext);
|
||||
Policy.clearAccountPolicy(mMockContext, a3);
|
||||
SecurityPolicy.clearAccountPolicy(mMockContext, a3);
|
||||
|
||||
mSecurityPolicy = SecurityPolicy.getInstance(mMockContext);
|
||||
|
||||
|
@ -359,7 +359,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
ProviderTestUtils.setupAccount("expiring-2", true, mMockContext);
|
||||
Policy p2 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 30, 0, 0,
|
||||
false, true);
|
||||
Policy.setAccountPolicy(mMockContext, a2, p2, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a2, p2, null);
|
||||
|
||||
// The expiring account should be returned
|
||||
nextExpiringAccountId = SecurityPolicy.findShortestExpiration(mMockContext);
|
||||
|
@ -369,7 +369,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
Account a3 = ProviderTestUtils.setupAccount("expiring-3", true, mMockContext);
|
||||
Policy p3 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 60, 0, 0,
|
||||
false, true);
|
||||
Policy.setAccountPolicy(mMockContext, a3, p3, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a3, p3, null);
|
||||
|
||||
// The original expiring account (a2) should be returned
|
||||
nextExpiringAccountId = SecurityPolicy.findShortestExpiration(mMockContext);
|
||||
|
@ -379,7 +379,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
Account a4 = ProviderTestUtils.setupAccount("expiring-4", true, mMockContext);
|
||||
Policy p4 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 15, 0, 0,
|
||||
false, true);
|
||||
Policy.setAccountPolicy(mMockContext, a4, p4, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a4, p4, null);
|
||||
|
||||
// The new expiring account (a4) should be returned
|
||||
nextExpiringAccountId = SecurityPolicy.findShortestExpiration(mMockContext);
|
||||
|
@ -409,7 +409,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
Account a2 = ProviderTestUtils.setupAccount("expired-2", true, mMockContext);
|
||||
Policy p2 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 0, 0, 0,
|
||||
false, true);
|
||||
Policy.setAccountPolicy(mMockContext, a2, p2, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a2, p2, null);
|
||||
|
||||
// Add a mailbox & messages to each account
|
||||
long account1Id = a1.mId;
|
||||
|
@ -435,7 +435,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
Account a3 = ProviderTestUtils.setupAccount("expired-3", true, mMockContext);
|
||||
Policy p3 = setupPolicy(20, Policy.PASSWORD_MODE_STRONG, 25, 26, false, 30, 0, 0,
|
||||
false, true);
|
||||
Policy.setAccountPolicy(mMockContext, a3, p3, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a3, p3, null);
|
||||
|
||||
// Add mailbox & messages to 3rd account
|
||||
long account3Id = a3.mId;
|
||||
|
@ -466,38 +466,6 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
assertEquals(Account.FLAGS_SECURITY_HOLD, account.mFlags & Account.FLAGS_SECURITY_HOLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the code that clears unsupported policies
|
||||
* TODO inject a mock DPM so we can directly control & test all cases, no matter what device
|
||||
*/
|
||||
public void testClearUnsupportedPolicies() {
|
||||
Policy p1 =
|
||||
setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false, false);
|
||||
Policy p2 =
|
||||
setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, true, false);
|
||||
|
||||
mSecurityPolicy = SecurityPolicy.getInstance(mMockContext);
|
||||
DevicePolicyManager dpm = mSecurityPolicy.getDPM();
|
||||
boolean hasEncryption =
|
||||
dpm.getStorageEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
|
||||
|
||||
Policy p1Result = mSecurityPolicy.clearUnsupportedPolicies(p1);
|
||||
Policy p2Result = mSecurityPolicy.clearUnsupportedPolicies(p2);
|
||||
|
||||
// No changes expected when encryptionRequested was false
|
||||
assertEquals(p1, p1Result);
|
||||
if (hasEncryption) {
|
||||
// No changes expected
|
||||
assertEquals(p2, p2Result);
|
||||
} else {
|
||||
// If encryption is unsupported, encryption policy bits are cleared
|
||||
Policy policyExpect =
|
||||
setupPolicy(1, Policy.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9, false,
|
||||
false);
|
||||
assertEquals(policyExpect, p2Result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the code that converts from exchange-style quality to DPM/Lockscreen style quality.
|
||||
*/
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
|
||||
package com.android.email.provider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.test.ProviderTestCase2;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
|
||||
import com.android.email.SecurityPolicy;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.emailcommon.provider.EmailContent.Attachment;
|
||||
|
@ -24,11 +30,6 @@ import com.android.emailcommon.provider.EmailContent.Message;
|
|||
import com.android.emailcommon.provider.Mailbox;
|
||||
import com.android.emailcommon.provider.Policy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.test.ProviderTestCase2;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
|
@ -68,10 +69,10 @@ public class PolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
// Setup two accounts with policies
|
||||
Account account1 = ProviderTestUtils.setupAccount("acct1", true, mMockContext);
|
||||
Policy policy1 = new Policy();
|
||||
Policy.setAccountPolicy(mMockContext, account1, policy1, securitySyncKey);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, account1, policy1, securitySyncKey);
|
||||
Account account2 = ProviderTestUtils.setupAccount("acct2", true, mMockContext);
|
||||
Policy policy2 = new Policy();
|
||||
Policy.setAccountPolicy(mMockContext, account2, policy2, securitySyncKey);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, account2, policy2, securitySyncKey);
|
||||
// Get the accounts back from the database
|
||||
account1.refresh(mMockContext);
|
||||
account2.refresh(mMockContext);
|
||||
|
@ -92,7 +93,7 @@ public class PolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
assertEquals(0, account.mPolicyKey);
|
||||
assertEquals(0, EmailContent.count(mMockContext, Policy.CONTENT_URI));
|
||||
Policy policy = new Policy();
|
||||
Policy.setAccountPolicy(mMockContext, account, policy, securitySyncKey);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, account, policy, securitySyncKey);
|
||||
account.refresh(mMockContext);
|
||||
// We should have a policyKey now
|
||||
assertTrue(account.mPolicyKey > 0);
|
||||
|
@ -103,7 +104,7 @@ public class PolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
assertEquals(policy, dbPolicy);
|
||||
// The account should have the security sync key set
|
||||
assertEquals(securitySyncKey, account.mSecuritySyncKey);
|
||||
Policy.clearAccountPolicy(mMockContext, account);
|
||||
SecurityPolicy.clearAccountPolicy(mMockContext, account);
|
||||
account.refresh(mMockContext);
|
||||
// Make sure policyKey is cleared and policy is deleted
|
||||
assertEquals(0, account.mPolicyKey);
|
||||
|
@ -118,11 +119,12 @@ public class PolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
att.mAccountKey = acct.mId;
|
||||
return att;
|
||||
}
|
||||
|
||||
public void testSetAttachmentFlagsForNewPolicy() {
|
||||
Account acct = ProviderTestUtils.setupAccount("acct1", true, mMockContext);
|
||||
Policy policy1 = new Policy();
|
||||
policy1.mDontAllowAttachments = true;
|
||||
Policy.setAccountPolicy(mMockContext, acct, policy1, null);
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, acct, policy1, null);
|
||||
Mailbox box = ProviderTestUtils.setupMailbox("box1", acct.mId, true, mMockContext);
|
||||
Message msg1 = ProviderTestUtils.setupMessage("message1", acct.mId, box.mId, false, false,
|
||||
mMockContext);
|
||||
|
|
|
@ -36,6 +36,7 @@ import android.test.suitebuilder.annotation.LargeTest;
|
|||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import com.android.email.SecurityPolicy;
|
||||
import com.android.email.provider.EmailProvider.AttachmentService;
|
||||
import com.android.emailcommon.AccountManagerTypes;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
|
@ -2529,7 +2530,7 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
|
|||
Policy p2 = new Policy();
|
||||
p2.save(mMockContext);
|
||||
Policy p3 = new Policy();
|
||||
Policy.setAccountPolicy(mMockContext, a.mId, p3, "0");
|
||||
SecurityPolicy.setAccountPolicy(mMockContext, a, p3, "0");
|
||||
|
||||
// We don't want anything cached or the tests below won't work. Note that
|
||||
// deleteUnlinked is only called by EmailProvider when the caches are empty
|
||||
|
|
Loading…
Reference in New Issue