Merge "Delete secured accounts if device admin is disabled"
This commit is contained in:
commit
45b15734d8
@ -650,6 +650,11 @@ save attachment.</string>
|
||||
being supported -->
|
||||
<string name="account_setup_failed_security_policies_unsupported">
|
||||
This server requires security features your phone does not support.</string>
|
||||
<!-- Warning given to users when they request disabling device administration (i.e. that their
|
||||
administered accounts will be deleted) [CHAR LIMIT=none] -->
|
||||
<string name="disable_admin_warning">WARNING: Deactivating the Email application\'s authority
|
||||
to administer your device will delete all Email accounts that require it, along with their
|
||||
email, contacts, calendar events, and other data.</string>
|
||||
|
||||
<!-- Notification ticker when device security required -->
|
||||
<string name="security_notification_ticker_fmt">
|
||||
|
@ -942,7 +942,7 @@ public class Controller {
|
||||
public void deleteAccount(final long accountId) {
|
||||
Utility.runAsync(new Runnable() {
|
||||
public void run() {
|
||||
deleteAccountSync(accountId);
|
||||
deleteAccountSync(accountId, mContext);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -950,19 +950,19 @@ public class Controller {
|
||||
/**
|
||||
* Delete an account synchronously. Intended to be used only by unit tests.
|
||||
*/
|
||||
public void deleteAccountSync(long accountId) {
|
||||
public void deleteAccountSync(long accountId, Context context) {
|
||||
try {
|
||||
mLegacyControllerMap.remove(accountId);
|
||||
// Get the account URI.
|
||||
final Account account = Account.restoreAccountWithId(mContext, accountId);
|
||||
final Account account = Account.restoreAccountWithId(context, accountId);
|
||||
if (account == null) {
|
||||
return; // Already deleted?
|
||||
}
|
||||
|
||||
final String accountUri = account.getStoreUri(mContext);
|
||||
final String accountUri = account.getStoreUri(context);
|
||||
// Delete Remote store at first.
|
||||
if (!TextUtils.isEmpty(accountUri)) {
|
||||
Store.getInstance(accountUri, mContext, null).delete();
|
||||
Store.getInstance(accountUri, context, null).delete();
|
||||
// Remove the Store instance from cache.
|
||||
Store.removeInstance(accountUri);
|
||||
}
|
||||
@ -972,12 +972,12 @@ public class Controller {
|
||||
mContext.getContentResolver().delete(uri, null, null);
|
||||
|
||||
// Update the backup (side copy) of the accounts
|
||||
AccountBackupRestore.backupAccounts(mContext);
|
||||
AccountBackupRestore.backupAccounts(context);
|
||||
|
||||
// Release or relax device administration, if relevant
|
||||
SecurityPolicy.getInstance(mContext).reducePolicies();
|
||||
SecurityPolicy.getInstance(context).reducePolicies();
|
||||
|
||||
Email.setServicesEnabled(mContext);
|
||||
Email.setServicesEnabled(context);
|
||||
} catch (Exception e) {
|
||||
Log.w(Email.LOG_TAG, "Exception while deleting account", e);
|
||||
} finally {
|
||||
|
@ -20,7 +20,6 @@ import com.android.email.activity.setup.AccountSecurity;
|
||||
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.MailService;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
@ -28,6 +27,7 @@ import android.app.PendingIntent;
|
||||
import android.app.admin.DeviceAdminReceiver;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@ -43,7 +43,7 @@ import android.util.Log;
|
||||
* into and out of various security states.
|
||||
*/
|
||||
public class SecurityPolicy {
|
||||
|
||||
private static final String TAG = "SecurityPolicy";
|
||||
private static SecurityPolicy sInstance = null;
|
||||
private Context mContext;
|
||||
private DevicePolicyManager mDPM;
|
||||
@ -703,19 +703,40 @@ public class SecurityPolicy {
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal handler for enabled->disabled transitions. Resets all security keys
|
||||
* forcing EAS to resync security state.
|
||||
* Delete all accounts whose security flags aren't zero (i.e. they have security enabled).
|
||||
* This method is synchronous, so it should normally be called within a worker thread (the
|
||||
* exception being for unit tests)
|
||||
*
|
||||
* @param context the caller's context
|
||||
*/
|
||||
/* package */ void onAdminEnabled(boolean isEnabled) {
|
||||
/*package*/ void deleteSecuredAccounts(Context context) {
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
// Find all accounts with security and delete them
|
||||
Cursor c = cr.query(Account.CONTENT_URI, EmailContent.ID_PROJECTION,
|
||||
AccountColumns.SECURITY_FLAGS + "!=0", null, null);
|
||||
try {
|
||||
Log.w(TAG, "Email administration disabled; deleting " + c.getCount() +
|
||||
" secured account(s)");
|
||||
while (c.moveToNext()) {
|
||||
Controller.getInstance(context).deleteAccountSync(
|
||||
c.getLong(EmailContent.ID_PROJECTION_COLUMN), context);
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
updatePolicies(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal handler for enabled->disabled transitions. Deletes all secured accounts.
|
||||
*/
|
||||
/*package*/ void onAdminEnabled(boolean isEnabled) {
|
||||
if (!isEnabled) {
|
||||
// transition to disabled state
|
||||
// Response: clear *all* security state information from the accounts, forcing
|
||||
// them back to the initial configurations requiring policy administration
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(AccountColumns.SECURITY_FLAGS, 0);
|
||||
cv.putNull(AccountColumns.SECURITY_SYNC_KEY);
|
||||
mContext.getContentResolver().update(Account.CONTENT_URI, cv, null, null);
|
||||
updatePolicies(-1);
|
||||
Utility.runAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
deleteSecuredAccounts(mContext);
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@ -743,6 +764,15 @@ public class SecurityPolicy {
|
||||
SecurityPolicy.getInstance(context).onAdminEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user asks to disable administration; we return a warning string that
|
||||
* will be presented to the user
|
||||
*/
|
||||
@Override
|
||||
public CharSequence onDisableRequested(Context context, Intent intent) {
|
||||
return context.getString(R.string.disable_admin_warning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the user has changed their password.
|
||||
*/
|
||||
|
@ -379,6 +379,12 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
||||
assertEquals(Account.FLAGS_VIBRATE_ALWAYS, a2b.mFlags);
|
||||
}
|
||||
|
||||
private static class MockController extends Controller {
|
||||
protected MockController(Context context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the response to disabling DeviceAdmin status
|
||||
*/
|
||||
@ -410,15 +416,22 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
||||
Account a3a = Account.restoreAccountWithId(mMockContext, a3.mId);
|
||||
assertNull(a3a.mSecuritySyncKey);
|
||||
|
||||
// Revoke device admin status. In the accounts we set up, security values should be reset
|
||||
sp.onAdminEnabled(false); // "disabled" should clear policies
|
||||
PolicySet after2 = sp.getAggregatePolicy();
|
||||
assertEquals(SecurityPolicy.NO_POLICY_SET, after2);
|
||||
Account a1b = Account.restoreAccountWithId(mMockContext, a1.mId);
|
||||
assertNull(a1b.mSecuritySyncKey);
|
||||
Account a2b = Account.restoreAccountWithId(mMockContext, a2.mId);
|
||||
assertNull(a2b.mSecuritySyncKey);
|
||||
Account a3b = Account.restoreAccountWithId(mMockContext, a3.mId);
|
||||
assertNull(a3b.mSecuritySyncKey);
|
||||
// Simulate revoke of device admin; directly call deleteSecuredAccounts, which is normally
|
||||
// called from a background thread
|
||||
MockController mockController = new MockController(mMockContext);
|
||||
Controller.injectMockControllerForTest(mockController);
|
||||
try {
|
||||
sp.deleteSecuredAccounts(mMockContext);
|
||||
PolicySet after2 = sp.getAggregatePolicy();
|
||||
assertEquals(SecurityPolicy.NO_POLICY_SET, after2);
|
||||
Account a1b = Account.restoreAccountWithId(mMockContext, a1.mId);
|
||||
assertNull(a1b);
|
||||
Account a2b = Account.restoreAccountWithId(mMockContext, a2.mId);
|
||||
assertNull(a2b);
|
||||
Account a3b = Account.restoreAccountWithId(mMockContext, a3.mId);
|
||||
assertNull(a3b.mSecuritySyncKey);
|
||||
} finally {
|
||||
Controller.injectMockControllerForTest(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user