Delete secured accounts if device admin is disabled
* Update unit test Bug: 2817683 Change-Id: Ia7117c34e7bbba13ac4f2ff375d19b3ef94ef49c
This commit is contained in:
parent
53e9f81822
commit
02d59d2194
@ -650,6 +650,11 @@ save attachment.</string>
|
|||||||
being supported -->
|
being supported -->
|
||||||
<string name="account_setup_failed_security_policies_unsupported">
|
<string name="account_setup_failed_security_policies_unsupported">
|
||||||
This server requires security features your phone does not support.</string>
|
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 -->
|
<!-- Notification ticker when device security required -->
|
||||||
<string name="security_notification_ticker_fmt">
|
<string name="security_notification_ticker_fmt">
|
||||||
|
@ -942,7 +942,7 @@ public class Controller {
|
|||||||
public void deleteAccount(final long accountId) {
|
public void deleteAccount(final long accountId) {
|
||||||
Utility.runAsync(new Runnable() {
|
Utility.runAsync(new Runnable() {
|
||||||
public void run() {
|
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.
|
* 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 {
|
try {
|
||||||
mLegacyControllerMap.remove(accountId);
|
mLegacyControllerMap.remove(accountId);
|
||||||
// Get the account URI.
|
// Get the account URI.
|
||||||
final Account account = Account.restoreAccountWithId(mContext, accountId);
|
final Account account = Account.restoreAccountWithId(context, accountId);
|
||||||
if (account == null) {
|
if (account == null) {
|
||||||
return; // Already deleted?
|
return; // Already deleted?
|
||||||
}
|
}
|
||||||
|
|
||||||
final String accountUri = account.getStoreUri(mContext);
|
final String accountUri = account.getStoreUri(context);
|
||||||
// Delete Remote store at first.
|
// Delete Remote store at first.
|
||||||
if (!TextUtils.isEmpty(accountUri)) {
|
if (!TextUtils.isEmpty(accountUri)) {
|
||||||
Store.getInstance(accountUri, mContext, null).delete();
|
Store.getInstance(accountUri, context, null).delete();
|
||||||
// Remove the Store instance from cache.
|
// Remove the Store instance from cache.
|
||||||
Store.removeInstance(accountUri);
|
Store.removeInstance(accountUri);
|
||||||
}
|
}
|
||||||
@ -972,12 +972,12 @@ public class Controller {
|
|||||||
mContext.getContentResolver().delete(uri, null, null);
|
mContext.getContentResolver().delete(uri, null, null);
|
||||||
|
|
||||||
// Update the backup (side copy) of the accounts
|
// Update the backup (side copy) of the accounts
|
||||||
AccountBackupRestore.backupAccounts(mContext);
|
AccountBackupRestore.backupAccounts(context);
|
||||||
|
|
||||||
// Release or relax device administration, if relevant
|
// Release or relax device administration, if relevant
|
||||||
SecurityPolicy.getInstance(mContext).reducePolicies();
|
SecurityPolicy.getInstance(context).reducePolicies();
|
||||||
|
|
||||||
Email.setServicesEnabled(mContext);
|
Email.setServicesEnabled(context);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.w(Email.LOG_TAG, "Exception while deleting account", e);
|
Log.w(Email.LOG_TAG, "Exception while deleting account", e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -20,7 +20,6 @@ import com.android.email.activity.setup.AccountSecurity;
|
|||||||
import com.android.email.provider.EmailContent;
|
import com.android.email.provider.EmailContent;
|
||||||
import com.android.email.provider.EmailContent.Account;
|
import com.android.email.provider.EmailContent.Account;
|
||||||
import com.android.email.provider.EmailContent.AccountColumns;
|
import com.android.email.provider.EmailContent.AccountColumns;
|
||||||
import com.android.email.service.MailService;
|
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
@ -28,6 +27,7 @@ import android.app.PendingIntent;
|
|||||||
import android.app.admin.DeviceAdminReceiver;
|
import android.app.admin.DeviceAdminReceiver;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -43,7 +43,7 @@ import android.util.Log;
|
|||||||
* into and out of various security states.
|
* into and out of various security states.
|
||||||
*/
|
*/
|
||||||
public class SecurityPolicy {
|
public class SecurityPolicy {
|
||||||
|
private static final String TAG = "SecurityPolicy";
|
||||||
private static SecurityPolicy sInstance = null;
|
private static SecurityPolicy sInstance = null;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private DevicePolicyManager mDPM;
|
private DevicePolicyManager mDPM;
|
||||||
@ -703,19 +703,40 @@ public class SecurityPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal handler for enabled->disabled transitions. Resets all security keys
|
* Delete all accounts whose security flags aren't zero (i.e. they have security enabled).
|
||||||
* forcing EAS to resync security state.
|
* 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) {
|
if (!isEnabled) {
|
||||||
// transition to disabled state
|
Utility.runAsync(new Runnable() {
|
||||||
// Response: clear *all* security state information from the accounts, forcing
|
@Override
|
||||||
// them back to the initial configurations requiring policy administration
|
public void run() {
|
||||||
ContentValues cv = new ContentValues();
|
deleteSecuredAccounts(mContext);
|
||||||
cv.put(AccountColumns.SECURITY_FLAGS, 0);
|
}});
|
||||||
cv.putNull(AccountColumns.SECURITY_SYNC_KEY);
|
|
||||||
mContext.getContentResolver().update(Account.CONTENT_URI, cv, null, null);
|
|
||||||
updatePolicies(-1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,6 +764,15 @@ public class SecurityPolicy {
|
|||||||
SecurityPolicy.getInstance(context).onAdminEnabled(false);
|
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.
|
* 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);
|
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
|
* Test the response to disabling DeviceAdmin status
|
||||||
*/
|
*/
|
||||||
@ -410,15 +416,22 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||||||
Account a3a = Account.restoreAccountWithId(mMockContext, a3.mId);
|
Account a3a = Account.restoreAccountWithId(mMockContext, a3.mId);
|
||||||
assertNull(a3a.mSecuritySyncKey);
|
assertNull(a3a.mSecuritySyncKey);
|
||||||
|
|
||||||
// Revoke device admin status. In the accounts we set up, security values should be reset
|
// Simulate revoke of device admin; directly call deleteSecuredAccounts, which is normally
|
||||||
sp.onAdminEnabled(false); // "disabled" should clear policies
|
// called from a background thread
|
||||||
PolicySet after2 = sp.getAggregatePolicy();
|
MockController mockController = new MockController(mMockContext);
|
||||||
assertEquals(SecurityPolicy.NO_POLICY_SET, after2);
|
Controller.injectMockControllerForTest(mockController);
|
||||||
Account a1b = Account.restoreAccountWithId(mMockContext, a1.mId);
|
try {
|
||||||
assertNull(a1b.mSecuritySyncKey);
|
sp.deleteSecuredAccounts(mMockContext);
|
||||||
Account a2b = Account.restoreAccountWithId(mMockContext, a2.mId);
|
PolicySet after2 = sp.getAggregatePolicy();
|
||||||
assertNull(a2b.mSecuritySyncKey);
|
assertEquals(SecurityPolicy.NO_POLICY_SET, after2);
|
||||||
Account a3b = Account.restoreAccountWithId(mMockContext, a3.mId);
|
Account a1b = Account.restoreAccountWithId(mMockContext, a1.mId);
|
||||||
assertNull(a3b.mSecuritySyncKey);
|
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