From d71d0b223a5cd02e2a8f1ec5c3f8cebab170d65f Mon Sep 17 00:00:00 2001 From: Andrew Stadler Date: Tue, 9 Feb 2010 17:24:55 -0800 Subject: [PATCH] Force security activation after new account create On new accounts, we can accelerate the process of setting up security by explicitly checking (at the end of the security process). The user is not required to "answer" the asynchronous notification. This is an imperfect solution, as a slow initial sync could leave the user in a non-synced Inbox (with a notification waiting for them), but we can come back to this after we evaluate real-world performance. Bug: 2387961 --- src/com/android/email/SecurityPolicy.java | 17 +--- .../email/activity/setup/AccountSecurity.java | 1 - .../activity/setup/AccountSetupNames.java | 98 ++++++++++++++++++- 3 files changed, 100 insertions(+), 16 deletions(-) diff --git a/src/com/android/email/SecurityPolicy.java b/src/com/android/email/SecurityPolicy.java index 2c077c99c..46156dd88 100644 --- a/src/com/android/email/SecurityPolicy.java +++ b/src/com/android/email/SecurityPolicy.java @@ -38,20 +38,11 @@ import android.net.Uri; import android.util.Log; /** - * Utility functions to support reading and writing security policies - * - * STOPSHIP - these TODO items are all part of finishing the feature - * 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. + * Utility functions to support reading and writing security policies, and handshaking the device + * into and out of various security states. */ 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 = false; - private static SecurityPolicy sInstance = null; private Context mContext; private DevicePolicyManager mDPM; @@ -295,8 +286,8 @@ public class SecurityPolicy { // making it this far means we passed! return true; } - // return false, not active - unless debugging enabled - return DEBUG_ALWAYS_ACTIVE; + // return false, not active + return false; } /** diff --git a/src/com/android/email/activity/setup/AccountSecurity.java b/src/com/android/email/activity/setup/AccountSecurity.java index 442a68f4f..1d1311d1a 100644 --- a/src/com/android/email/activity/setup/AccountSecurity.java +++ b/src/com/android/email/activity/setup/AccountSecurity.java @@ -25,7 +25,6 @@ import android.app.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.util.Log; /** * Psuedo-activity (no UI) to bootstrap the user up to a higher desired security level. This diff --git a/src/com/android/email/activity/setup/AccountSetupNames.java b/src/com/android/email/activity/setup/AccountSetupNames.java index 6efbe3a3f..1aa4a84db 100644 --- a/src/com/android/email/activity/setup/AccountSetupNames.java +++ b/src/com/android/email/activity/setup/AccountSetupNames.java @@ -20,11 +20,15 @@ import com.android.email.AccountBackupRestore; import com.android.email.R; import com.android.email.Utility; import com.android.email.provider.EmailContent; +import com.android.email.provider.EmailContent.Account; import com.android.email.provider.EmailContent.AccountColumns; import android.app.Activity; +import android.content.ContentUris; import android.content.ContentValues; import android.content.Intent; +import android.database.Cursor; +import android.os.AsyncTask; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; @@ -38,12 +42,20 @@ import android.widget.EditText; public class AccountSetupNames extends Activity implements OnClickListener { private static final String EXTRA_ACCOUNT_ID = "accountId"; private static final String EXTRA_EAS_FLOW = "easFlow"; + private static final int REQUEST_SECURITY = 0; private EditText mDescription; private EditText mName; - private EmailContent.Account mAccount; + private Account mAccount; private Button mDoneButton; + private CheckAccountStateTask mCheckAccountStateTask; + + private static final int ACCOUNT_INFO_COLUMN_FLAGS = 0; + private static final int ACCOUNT_INFO_COLUMN_SECURITY_FLAGS = 1; + private static final String[] ACCOUNT_INFO_PROJECTION = new String[] { + AccountColumns.FLAGS, AccountColumns.SECURITY_FLAGS }; + public static void actionSetNames(Activity fromActivity, long accountId, boolean easFlowMode) { Intent i = new Intent(fromActivity, AccountSetupNames.class); i.putExtra(EXTRA_ACCOUNT_ID, accountId); @@ -92,6 +104,17 @@ public class AccountSetupNames extends Activity implements OnClickListener { } } + @Override + protected void onDestroy() { + super.onDestroy(); + + if (mCheckAccountStateTask != null && + mCheckAccountStateTask.getStatus() != CheckAccountStateTask.Status.FINISHED) { + mCheckAccountStateTask.cancel(true); + mCheckAccountStateTask = null; + } + } + /** * TODO: Validator should also trim the name string before checking it. */ @@ -130,7 +153,11 @@ public class AccountSetupNames extends Activity implements OnClickListener { mAccount.update(this, cv); // Update the backup (side copy) of the accounts AccountBackupRestore.backupAccounts(this); - onBackPressed(); + + // Before proceeding, launch an AsyncTask to test the account for any syncing problems, + // and if there's a problem, bring up the UI to update the security level. + mCheckAccountStateTask = new CheckAccountStateTask(mAccount.mId); + mCheckAccountStateTask.execute(); } public void onClick(View v) { @@ -140,4 +167,71 @@ public class AccountSetupNames extends Activity implements OnClickListener { break; } } + + /** + * This async task is launched just before exiting. It's a last chance test, before leaving + * this activity, for the account being in a "hold" state, and gives the user a chance to + * update security, enter a device PIN, etc. for a more seamless account setup experience. + * + * TODO: If there was *any* indication that security might be required, we could at least + * force the DeviceAdmin activation step, without waiting for the initial sync/handshake + * to fail. + * TODO: If the user doesn't update the security, don't go to the MessageList. + */ + private class CheckAccountStateTask extends AsyncTask { + + private long mAccountId; + + public CheckAccountStateTask(long accountId) { + mAccountId = accountId; + } + + @Override + protected Boolean doInBackground(Void... params) { + Cursor c = AccountSetupNames.this.getContentResolver().query( + ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId), + ACCOUNT_INFO_PROJECTION, null, null, null); + try { + if (c.moveToFirst()) { + int flags = c.getInt(ACCOUNT_INFO_COLUMN_FLAGS); + int securityFlags = c.getInt(ACCOUNT_INFO_COLUMN_SECURITY_FLAGS); + if ((flags & Account.FLAGS_SECURITY_HOLD) != 0) { + return Boolean.TRUE; + } + } + } finally { + c.close(); + } + + return Boolean.FALSE; + } + + @Override + protected void onPostExecute(Boolean isSecurityHold) { + if (!isCancelled()) { + if (isSecurityHold) { + Intent i = AccountSecurity.actionUpdateSecurityIntent( + AccountSetupNames.this, mAccountId); + AccountSetupNames.this.startActivityForResult(i, REQUEST_SECURITY); + } else { + onBackPressed(); + } + } + } + } + + /** + * Handle the eventual result from the security update activity + * + * TODO: If the user doesn't update the security, don't go to the MessageList. + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_SECURITY: + onBackPressed(); + } + super.onActivityResult(requestCode, resultCode, data); + } + }