Adding security hold flag to accounts
* Add hold flag to Account flags * Add code to set it (when EAS reports policy failure) * Add code to clear it when we see changes from the device admin side * unit tests This should be sufficient to restart sync of an account which is on hold due to security policy requirements. Note, this is considered a "retry", and if the account still does not meet requirements for some reason, it is expected that EAS sync will call policiesRequired() again.
This commit is contained in:
parent
f3332ddac8
commit
2a5eeea921
|
@ -28,6 +28,8 @@ import android.app.Notification;
|
|||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
@ -38,7 +40,6 @@ import android.net.Uri;
|
|||
* Utility functions to support reading and writing security policies
|
||||
*
|
||||
* STOPSHIP - these TODO items are all part of finishing the feature
|
||||
* TODO: When user sets password, and conditions are now satisfied, restart syncs
|
||||
* TODO: When accounts are deleted, reduce policy and/or give up admin status
|
||||
* TODO: Provide a way to check for policy issues at synchronous times such as entering
|
||||
* message list or folder list.
|
||||
|
@ -50,7 +51,7 @@ public class SecurityPolicy {
|
|||
/** STOPSHIP - ok to check in true for now, but must be false for shipping */
|
||||
/** DO NOT CHECK IN WHILE 'true' */
|
||||
// Until everything is connected, allow syncs to work
|
||||
private static final boolean DEBUG_ALWAYS_ACTIVE = true;
|
||||
private static final boolean DEBUG_ALWAYS_ACTIVE = false;
|
||||
|
||||
private static SecurityPolicy sInstance = null;
|
||||
private Context mContext;
|
||||
|
@ -67,13 +68,23 @@ public class SecurityPolicy {
|
|||
* This projection on Account is for scanning/reading
|
||||
*/
|
||||
private static final String[] ACCOUNT_SECURITY_PROJECTION = new String[] {
|
||||
Account.RECORD_ID, Account.SECURITY_FLAGS
|
||||
AccountColumns.ID, AccountColumns.SECURITY_FLAGS
|
||||
};
|
||||
private static final int ACCOUNT_SECURITY_COLUMN_FLAGS = 1;
|
||||
// Note, this handles the NULL case to deal with older accounts where the column was added
|
||||
private static final String WHERE_ACCOUNT_SECURITY_NONZERO =
|
||||
Account.SECURITY_FLAGS + " IS NOT NULL AND " + Account.SECURITY_FLAGS + "!=0";
|
||||
|
||||
/**
|
||||
* This projection on Account is for clearing the "security hold" column. Also includes
|
||||
* the security flags column, so we can use it for selecting.
|
||||
*/
|
||||
private static final String[] ACCOUNT_FLAGS_PROJECTION = new String[] {
|
||||
AccountColumns.ID, AccountColumns.FLAGS, AccountColumns.SECURITY_FLAGS
|
||||
};
|
||||
private static final int ACCOUNT_FLAGS_COLUMN_ID = 0;
|
||||
private static final int ACCOUNT_FLAGS_COLUMN_FLAGS = 1;
|
||||
|
||||
/**
|
||||
* These are hardcoded limits based on knowledge of the current DevicePolicyManager
|
||||
* and screen lock mechanisms. Wherever possible, these should be replaced with queries of
|
||||
|
@ -265,6 +276,9 @@ 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)
|
||||
|
||||
// making it this far means we passed!
|
||||
return true;
|
||||
}
|
||||
// return false, not active - unless debugging enabled
|
||||
return DEBUG_ALWAYS_ACTIVE;
|
||||
|
@ -295,6 +309,46 @@ public class SecurityPolicy {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void setAccountHoldFlag(Account account, boolean newState) {
|
||||
if (newState) {
|
||||
account.mFlags |= Account.FLAGS_SECURITY_HOLD;
|
||||
} else {
|
||||
account.mFlags &= ~Account.FLAGS_SECURITY_HOLD;
|
||||
}
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(AccountColumns.FLAGS, account.mFlags);
|
||||
account.update(mContext, cv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all account hold flags that are set. This will trigger watchers, and in particular
|
||||
* will cause EAS to try and resync the account(s).
|
||||
*/
|
||||
public void clearAccountHoldFlags() {
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
Cursor c = resolver.query(Account.CONTENT_URI, ACCOUNT_FLAGS_PROJECTION,
|
||||
WHERE_ACCOUNT_SECURITY_NONZERO, null, null);
|
||||
try {
|
||||
while (c.moveToNext()) {
|
||||
int flags = c.getInt(ACCOUNT_FLAGS_COLUMN_FLAGS);
|
||||
if (0 != (flags & Account.FLAGS_SECURITY_HOLD)) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(AccountColumns.FLAGS, flags & ~Account.FLAGS_SECURITY_HOLD);
|
||||
long accountId = c.getLong(ACCOUNT_FLAGS_COLUMN_ID);
|
||||
Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
|
||||
resolver.update(uri, cv, null, null);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API: Sync service should call this any time a sync fails due to isActive() returning false.
|
||||
* This will kick off the notify-acquire-admin-state process and/or increase the security level.
|
||||
|
@ -304,6 +358,12 @@ public class SecurityPolicy {
|
|||
* @param accountId the account for which sync cannot proceed
|
||||
*/
|
||||
public void policiesRequired(long accountId) {
|
||||
Account account = EmailContent.Account.restoreAccountWithId(mContext, accountId);
|
||||
|
||||
// Mark the account as "on hold".
|
||||
setAccountHoldFlag(account, true);
|
||||
|
||||
// Put up a notification (unless there already is one)
|
||||
synchronized (this) {
|
||||
if (mNotificationActive) {
|
||||
// no need to do anything - we've already been notified, and we've already
|
||||
|
@ -316,7 +376,6 @@ public class SecurityPolicy {
|
|||
}
|
||||
}
|
||||
// At this point, we will put up a notification
|
||||
Account account = EmailContent.Account.restoreAccountWithId(mContext, accountId);
|
||||
|
||||
String tickerText = mContext.getString(R.string.security_notification_ticker_fmt,
|
||||
account.getDisplayName());
|
||||
|
@ -595,7 +654,7 @@ public class SecurityPolicy {
|
|||
*/
|
||||
@Override
|
||||
public void onPasswordChanged(Context context, Intent intent) {
|
||||
// do something
|
||||
SecurityPolicy.getInstance(context).clearAccountHoldFlags();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -118,12 +118,14 @@ public class AccountSecurity extends Activity {
|
|||
SecurityPolicy sp = SecurityPolicy.getInstance(this);
|
||||
// check current security level - if sufficient, we're done!
|
||||
if (sp.isActive(null)) {
|
||||
sp.clearAccountHoldFlags();
|
||||
return;
|
||||
}
|
||||
// set current security level
|
||||
sp.setActivePolicies();
|
||||
// check current security level - if sufficient, we're done!
|
||||
if (sp.isActive(null)) {
|
||||
sp.clearAccountHoldFlags();
|
||||
return;
|
||||
}
|
||||
// if not sufficient, launch the activity to have the user set a new password.
|
||||
|
|
|
@ -791,6 +791,7 @@ public abstract class EmailContent {
|
|||
public static final int FLAGS_DELETE_POLICY_MASK = 4+8;
|
||||
public static final int FLAGS_DELETE_POLICY_SHIFT = 2;
|
||||
public static final int FLAGS_INCOMPLETE = 16;
|
||||
public static final int FLAGS_SECURITY_HOLD = 32;
|
||||
|
||||
public static final int DELETE_POLICY_NEVER = 0;
|
||||
public static final int DELETE_POLICY_7DAYS = 1; // not supported
|
||||
|
|
|
@ -242,4 +242,57 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
assertFalse(p2.hashCode() == p3.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the API to set/clear policy hold flags in an account
|
||||
*/
|
||||
public void testSetClearHoldFlag() {
|
||||
SecurityPolicy sp = getSecurityPolicy();
|
||||
|
||||
Account a1 = ProviderTestUtils.setupAccount("holdflag-1", false, mMockContext);
|
||||
a1.mFlags = Account.FLAGS_NOTIFY_NEW_MAIL;
|
||||
a1.save(mMockContext);
|
||||
Account a2 = ProviderTestUtils.setupAccount("holdflag-2", false, mMockContext);
|
||||
a2.mFlags = Account.FLAGS_VIBRATE | Account.FLAGS_SECURITY_HOLD;
|
||||
a2.save(mMockContext);
|
||||
|
||||
// confirm clear until set
|
||||
Account a1a = Account.restoreAccountWithId(mMockContext, a1.mId);
|
||||
assertEquals(Account.FLAGS_NOTIFY_NEW_MAIL, a1a.mFlags);
|
||||
sp.setAccountHoldFlag(a1, true);
|
||||
assertEquals(Account.FLAGS_NOTIFY_NEW_MAIL | Account.FLAGS_SECURITY_HOLD, a1.mFlags);
|
||||
Account a1b = Account.restoreAccountWithId(mMockContext, a1.mId);
|
||||
assertEquals(Account.FLAGS_NOTIFY_NEW_MAIL | Account.FLAGS_SECURITY_HOLD, a1b.mFlags);
|
||||
|
||||
// confirm set until cleared
|
||||
Account a2a = Account.restoreAccountWithId(mMockContext, a2.mId);
|
||||
assertEquals(Account.FLAGS_VIBRATE | Account.FLAGS_SECURITY_HOLD, a2a.mFlags);
|
||||
sp.setAccountHoldFlag(a2, false);
|
||||
assertEquals(Account.FLAGS_VIBRATE, a2.mFlags);
|
||||
Account a2b = Account.restoreAccountWithId(mMockContext, a2.mId);
|
||||
assertEquals(Account.FLAGS_VIBRATE, a2b.mFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the API to clear all policy hold flags in all accounts)
|
||||
*/
|
||||
public void testClearHoldFlags() {
|
||||
SecurityPolicy sp = getSecurityPolicy();
|
||||
|
||||
Account a1 = ProviderTestUtils.setupAccount("holdflag-1", false, mMockContext);
|
||||
a1.mFlags = Account.FLAGS_NOTIFY_NEW_MAIL;
|
||||
a1.save(mMockContext);
|
||||
Account a2 = ProviderTestUtils.setupAccount("holdflag-2", false, mMockContext);
|
||||
a2.mFlags = Account.FLAGS_VIBRATE | Account.FLAGS_SECURITY_HOLD;
|
||||
a2.save(mMockContext);
|
||||
|
||||
// bulk clear
|
||||
sp.clearAccountHoldFlags();
|
||||
|
||||
// confirm new values as expected - no hold flags; other flags unmolested
|
||||
Account a1a = Account.restoreAccountWithId(mMockContext, a1.mId);
|
||||
assertEquals(Account.FLAGS_NOTIFY_NEW_MAIL, a1a.mFlags);
|
||||
Account a2a = Account.restoreAccountWithId(mMockContext, a2.mId);
|
||||
assertEquals(Account.FLAGS_VIBRATE, a2a.mFlags);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue