Remove disk access from DeviceAdminReceiver callbacks

* DeviceAdminReceiver is actually a BroadcastReceiver, must follow
  guidelines to prevent ANR or early process kill.
* Remove all uses of AsyncTask from DeviceAdminReceiver
* Pass all calls through EmailBroadcastProcessorService
* Minor restructuring of EmailBroadcastProcessorService to support
  this use.

Change-Id: Ic6257ea5eff1bd466a736e0f93cb89b1cf8aa73e
This commit is contained in:
Andy Stadler 2010-12-30 00:16:55 -08:00
parent 5498296287
commit a2269e84c6
2 changed files with 96 additions and 44 deletions

View File

@ -20,6 +20,7 @@ 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.EmailBroadcastProcessorService;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DeviceAdminReceiver;
@ -58,12 +59,18 @@ public class SecurityPolicy {
private static final int ACCOUNT_SECURITY_COLUMN_ID = 0;
private static final int ACCOUNT_SECURITY_COLUMN_FLAGS = 1;
// Messages used for DevicePolicyManager callbacks
private static final int DEVICE_ADMIN_MESSAGE_ENABLED = 1;
private static final int DEVICE_ADMIN_MESSAGE_DISABLED = 2;
private static final int DEVICE_ADMIN_MESSAGE_PASSWORD_CHANGED = 3;
private static final int DEVICE_ADMIN_MESSAGE_PASSWORD_EXPIRING = 4;
/**
* Get the security policy instance
*/
public synchronized static SecurityPolicy getInstance(Context context) {
if (sInstance == null) {
sInstance = new SecurityPolicy(context);
sInstance = new SecurityPolicy(context.getApplicationContext());
}
return sInstance;
}
@ -751,28 +758,14 @@ public class SecurityPolicy {
/**
* Internal handler for enabled->disabled transitions. Deletes all secured accounts.
* Must call from worker thread, not on UI thread.
*/
/*package*/ void onAdminEnabled(boolean isEnabled) {
if (!isEnabled) {
Utility.runAsync(new Runnable() {
@Override
public void run() {
deleteSecuredAccounts(mContext);
}});
deleteSecuredAccounts(mContext);
}
}
/**
* Internal handler for device password expirations.
*/
private void onPasswordExpiring() {
Utility.runAsync(new Runnable() {
@Override
public void run() {
onPasswordExpiringSync(mContext);
}});
}
/**
* Handle password expiration - if any accounts appear to have triggered this, put up
* warnings, or even shut them down.
@ -783,7 +776,7 @@ public class SecurityPolicy {
* the expiration. In other words, all accounts (that require expiration) will run/stop
* based on the requirements of the account with the shortest interval.
*/
/* package */ void onPasswordExpiringSync(Context context) {
private void onPasswordExpiring(Context context) {
// 1. Do we have any accounts that matter here?
long nextExpiringAccountId = findShortestExpiration(context);
@ -895,9 +888,40 @@ public class SecurityPolicy {
return result;
}
/**
* Callback from EmailBroadcastProcessorService. This provides the workers for the
* DeviceAdminReceiver calls. These should perform the work directly and not use async
* threads for completion.
*/
public static void onDeviceAdminReceiverMessage(Context context, int message) {
SecurityPolicy instance = SecurityPolicy.getInstance(context);
switch (message) {
case DEVICE_ADMIN_MESSAGE_ENABLED:
instance.onAdminEnabled(true);
break;
case DEVICE_ADMIN_MESSAGE_DISABLED:
instance.onAdminEnabled(false);
break;
case DEVICE_ADMIN_MESSAGE_PASSWORD_CHANGED:
// TODO make a small helper for this
// Clear security holds (if any)
Account.clearSecurityHoldOnAllAccounts(context);
// Cancel any active notifications (if any are posted)
NotificationController nc = NotificationController.getInstance(context);
nc.cancelNotification(NotificationController.NOTIFICATION_ID_PASSWORD_EXPIRING);
nc.cancelNotification(NotificationController.NOTIFICATION_ID_PASSWORD_EXPIRED);
break;
case DEVICE_ADMIN_MESSAGE_PASSWORD_EXPIRING:
instance.onPasswordExpiring(instance.mContext);
break;
}
}
/**
* Device Policy administrator. This is primarily a listener for device state changes.
* Note: This is instantiated by incoming messages.
* Note: This is actually a BroadcastReceiver and must remain within the guidelines required
* for proper behavior, including avoidance of ANRs.
* Note: We do not implement onPasswordFailed() because the default behavior of the
* DevicePolicyManager - complete local wipe after 'n' failures - is sufficient.
*/
@ -908,7 +932,8 @@ public class SecurityPolicy {
*/
@Override
public void onEnabled(Context context, Intent intent) {
SecurityPolicy.getInstance(context).onAdminEnabled(true);
EmailBroadcastProcessorService.processDevicePolicyMessage(context,
DEVICE_ADMIN_MESSAGE_ENABLED);
}
/**
@ -916,7 +941,8 @@ public class SecurityPolicy {
*/
@Override
public void onDisabled(Context context, Intent intent) {
SecurityPolicy.getInstance(context).onAdminEnabled(false);
EmailBroadcastProcessorService.processDevicePolicyMessage(context,
DEVICE_ADMIN_MESSAGE_DISABLED);
}
/**
@ -933,12 +959,8 @@ public class SecurityPolicy {
*/
@Override
public void onPasswordChanged(Context context, Intent intent) {
// Clear security holds (if any)
Account.clearSecurityHoldOnAllAccounts(context);
// Cancel any active notifications (if any are posted)
NotificationController nc = NotificationController.getInstance(context);
nc.cancelNotification(NotificationController.NOTIFICATION_ID_PASSWORD_EXPIRING);
nc.cancelNotification(NotificationController.NOTIFICATION_ID_PASSWORD_EXPIRED);
EmailBroadcastProcessorService.processDevicePolicyMessage(context,
DEVICE_ADMIN_MESSAGE_PASSWORD_CHANGED);
}
/**
@ -946,7 +968,8 @@ public class SecurityPolicy {
*/
@Override
public void onPasswordExpiring(Context context, Intent intent) {
SecurityPolicy.getInstance(context).onPasswordExpiring();
EmailBroadcastProcessorService.processDevicePolicyMessage(context,
DEVICE_ADMIN_MESSAGE_PASSWORD_EXPIRING);
}
}
}

View File

@ -19,8 +19,8 @@ package com.android.email.service;
import com.android.email.Email;
import com.android.email.ExchangeUtils;
import com.android.email.Preferences;
import com.android.email.SecurityPolicy;
import com.android.email.VendorPolicyLoader;
import com.android.email.activity.ActivityHelper;
import com.android.email.activity.setup.AccountSettingsXL;
import android.app.IntentService;
@ -28,7 +28,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.util.Config;
import android.util.Log;
/**
@ -40,12 +39,22 @@ import android.util.Log;
* <li>Even if it does, the Intent that have started it will be re-delivered by the system,
* and we can start the process again. (Using {@link #setIntentRedelivery}).
* </ul>
*
* This also handles the DeviceAdminReceiver in SecurityPolicy, because it is also
* a BroadcastReceiver and requires the same processing semantics.
*/
public class EmailBroadcastProcessorService extends IntentService {
// Action used for BroadcastReceiver entry point
private static final String ACTION_BROADCAST = "broadcast_receiver";
// Dialing "*#*#36245#*#*" to open the debug screen. "36245" = "email"
private static final String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE";
private static final String ACTION_SECRET_CODE = "android.provider.Telephony.SECRET_CODE";
private static final String SECRET_CODE_HOST_DEBUG_SCREEN = "36245";
// This is a helper used to process DeviceAdminReceiver messages
private static final String ACTION_DEVICE_POLICY_ADMIN = "com.android.email.devicepolicy";
private static final String EXTRA_DEVICE_POLICY_ADMIN = "message_code";
public EmailBroadcastProcessorService() {
// Class name will be the thread name.
super(EmailBroadcastProcessorService.class.getName());
@ -59,30 +68,50 @@ public class EmailBroadcastProcessorService extends IntentService {
*/
public static void processBroadcastIntent(Context context, Intent broadcastIntent) {
Intent i = new Intent(context, EmailBroadcastProcessorService.class);
i.setAction(ACTION_BROADCAST);
i.putExtra(Intent.EXTRA_INTENT, broadcastIntent);
context.startService(i);
}
/**
* Entry point for {@link com.android.email.SecurityPolicy.PolicyAdmin}. These will
* simply callback to {@link
* com.android.email.SecurityPolicy#onDeviceAdminReceiverMessage(Context, int)}.
*/
public static void processDevicePolicyMessage(Context context, int message) {
Intent i = new Intent(context, EmailBroadcastProcessorService.class);
i.setAction(ACTION_DEVICE_POLICY_ADMIN);
i.putExtra(EXTRA_DEVICE_POLICY_ADMIN, message);
context.startService(i);
}
@Override
protected void onHandleIntent(Intent intent) {
// This method is called on a worker thread.
final Intent original = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final String action = original.getAction();
// Dispatch from entry point
final String action = intent.getAction();
if (ACTION_BROADCAST.equals(action)) {
final Intent broadcastIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final String broadcastAction = broadcastIntent.getAction();
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
onBootCompleted();
if (Intent.ACTION_BOOT_COMPLETED.equals(broadcastAction)) {
onBootCompleted();
// TODO: Do a better job when we get ACTION_DEVICE_STORAGE_LOW.
// The code below came from very old code....
} else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
// Stop IMAP/POP3 poll.
MailService.actionCancel(this);
} else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
enableComponentsIfNecessary();
} else if (SECRET_CODE_ACTION.equals(action)
&& SECRET_CODE_HOST_DEBUG_SCREEN.equals(original.getData().getHost())) {
AccountSettingsXL.actionSettingsWithDebug(this);
// TODO: Do a better job when we get ACTION_DEVICE_STORAGE_LOW.
// The code below came from very old code....
} else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(broadcastAction)) {
// Stop IMAP/POP3 poll.
MailService.actionCancel(this);
} else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(broadcastAction)) {
enableComponentsIfNecessary();
} else if (ACTION_SECRET_CODE.equals(broadcastAction)
&& SECRET_CODE_HOST_DEBUG_SCREEN.equals(broadcastIntent.getData().getHost())) {
AccountSettingsXL.actionSettingsWithDebug(this);
}
} else if (ACTION_DEVICE_POLICY_ADMIN.equals(action)) {
int message = intent.getIntExtra(EXTRA_DEVICE_POLICY_ADMIN, -1);
SecurityPolicy.onDeviceAdminReceiverMessage(this, message);
}
}