AccountSecurity.java revision 45bfac00e58059eed71fd22e2767c8e0437b6251
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email.activity.setup; 18 19import com.android.email.R; 20import com.android.email.SecurityPolicy; 21import com.android.email.Utility; 22import com.android.email.activity.ActivityHelper; 23import com.android.email.provider.EmailContent.Account; 24import com.android.email.provider.EmailContent.HostAuth; 25 26import android.app.Activity; 27import android.app.admin.DevicePolicyManager; 28import android.content.Context; 29import android.content.Intent; 30import android.os.AsyncTask; 31import android.os.Bundle; 32 33/** 34 * Psuedo-activity (no UI) to bootstrap the user up to a higher desired security level. This 35 * bootstrap requires the following steps. 36 * 37 * 1. Confirm the account of interest has any security policies defined - exit early if not 38 * 2. If not actively administrating the device, ask Device Policy Manager to start that 39 * 3. When we are actively administrating, check current policies and see if they're sufficient 40 * 4. If not, set policies 41 * 5. If necessary, request for user to update device password 42 * 6. If necessary, request for user to activate device encryption 43 */ 44public class AccountSecurity extends Activity { 45 46 private static final String EXTRA_ACCOUNT_ID = "com.android.email.activity.setup.ACCOUNT_ID"; 47 48 private static final int REQUEST_ENABLE = 1; 49 private static final int REQUEST_PASSWORD = 2; 50 private static final int REQUEST_ENCRYPTION = 3; 51 52 private boolean mTriedAddAdministrator = false; 53 private boolean mTriedSetPassword = false; 54 private boolean mTriedSetEncryption = false; 55 private Account mAccount; 56 57 /** 58 * Used for generating intent for this activity (which is intended to be launched 59 * from a notification.) 60 * 61 * @param context Calling context for building the intent 62 * @param accountId The account of interest 63 * @return an Intent which can be used to view that account 64 */ 65 public static Intent actionUpdateSecurityIntent(Context context, long accountId) { 66 Intent intent = new Intent(context, AccountSecurity.class); 67 intent.putExtra(EXTRA_ACCOUNT_ID, accountId); 68 return intent; 69 } 70 71 @Override 72 public void onCreate(Bundle savedInstanceState) { 73 super.onCreate(savedInstanceState); 74 ActivityHelper.debugSetWindowFlags(this); 75 76 Intent i = getIntent(); 77 final long accountId = i.getLongExtra(EXTRA_ACCOUNT_ID, -1); 78 SecurityPolicy security = SecurityPolicy.getInstance(this); 79 security.clearNotification(accountId); 80 if (accountId == -1) { 81 finish(); 82 return; 83 } 84 85 // Let onCreate exit, while background thread retrieves account. 86 // Then start the security check/bootstrap process. 87 new AsyncTask<Void, Void, Account>() { 88 @Override 89 protected Account doInBackground(Void... params) { 90 return Account.restoreAccountWithId(AccountSecurity.this, accountId); 91 } 92 93 @Override 94 protected void onPostExecute(Account result) { 95 mAccount = result; 96 if (mAccount != null && mAccount.mSecurityFlags != 0) { 97 // This account wants to control security 98 tryAdvanceSecurity(mAccount); 99 return; 100 } 101 finish(); 102 } 103 }.execute(); 104 } 105 106 /** 107 * After any of the activities return, try to advance to the "next step" 108 */ 109 @Override 110 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 111 tryAdvanceSecurity(mAccount); 112 super.onActivityResult(requestCode, resultCode, data); 113 } 114 115 /** 116 * Walk the user through the required steps to become an active administrator and with 117 * the requisite security settings for the given account. 118 * 119 * These steps will be repeated each time we return from a given attempt (e.g. asking the 120 * user to choose a device pin/password). In a typical activation, we may repeat these 121 * steps a few times. It may go as far as step 5 (password) or step 6 (encryption), but it 122 * will terminate when step 2 (isActive()) succeeds. 123 * 124 * If at any point we do not advance beyond a given user step, (e.g. the user cancels 125 * instead of setting a password) we simply repost the security notification, and exit. 126 * We never want to loop here. 127 */ 128 private void tryAdvanceSecurity(Account account) { 129 SecurityPolicy security = SecurityPolicy.getInstance(this); 130 131 // Step 1. Check if we are an active device administrator, and stop here to activate 132 if (!security.isActiveAdmin()) { 133 if (mTriedAddAdministrator) { 134 repostNotification(account, security); 135 finish(); 136 } else { 137 mTriedAddAdministrator = true; 138 // retrieve name of server for the format string 139 HostAuth hostAuth = HostAuth.restoreHostAuthWithId(this, account.mHostAuthKeyRecv); 140 if (hostAuth == null) { 141 repostNotification(account, security); 142 finish(); 143 } else { 144 // try to become active - must happen here in activity, to get result 145 Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); 146 intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, 147 security.getAdminComponent()); 148 intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, 149 this.getString(R.string.account_security_policy_explanation_fmt, 150 hostAuth.mAddress)); 151 startActivityForResult(intent, REQUEST_ENABLE); 152 } 153 } 154 return; 155 } 156 157 // Step 2. Check if the current aggregate security policy is being satisfied by the 158 // DevicePolicyManager (the current system security level). 159 if (security.isActive(null)) { 160 Account.clearSecurityHoldOnAllAccounts(this); 161 finish(); 162 return; 163 } 164 165 // Step 3. Try to assert the current aggregate security requirements with the system. 166 security.setActivePolicies(); 167 168 // Step 4. Recheck the security policy, and determine what changes are needed (if any) 169 // to satisfy the requirements. 170 int inactiveReasons = security.getInactiveReasons(null); 171 172 // Step 5. If password is needed, try to have the user set it 173 if ((inactiveReasons & SecurityPolicy.INACTIVE_NEED_PASSWORD) != 0) { 174 if (mTriedSetPassword) { 175 repostNotification(account, security); 176 finish(); 177 } else { 178 mTriedSetPassword = true; 179 // launch the activity to have the user set a new password. 180 Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); 181 startActivityForResult(intent, REQUEST_PASSWORD); 182 } 183 return; 184 } 185 186 // Step 6. If encryption is needed, try to have the user set it 187 if ((inactiveReasons & SecurityPolicy.INACTIVE_NEED_ENCRYPTION) != 0) { 188 if (mTriedSetEncryption) { 189 repostNotification(account, security); 190 finish(); 191 } else { 192 mTriedSetEncryption = true; 193 // launch the activity to start up encryption. 194 Intent intent = new Intent(DevicePolicyManager.ACTION_START_ENCRYPTION); 195 startActivityForResult(intent, REQUEST_ENCRYPTION); 196 } 197 return; 198 } 199 200 // Step 7. No problems were found, so clear holds and exit 201 Account.clearSecurityHoldOnAllAccounts(this); 202 finish(); 203 } 204 205 /** 206 * Mark an account as not-ready-for-sync and post a notification to bring the user back here 207 * eventually. 208 */ 209 private void repostNotification(final Account account, final SecurityPolicy security) { 210 Utility.runAsync(new Runnable() { 211 @Override 212 public void run() { 213 security.policiesRequired(account.mId); 214 } 215 }); 216 } 217} 218