1 /* 2 * Copyright (C) 2008 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 17 package com.android.settings.accounts; 18 19 import android.accounts.AccountManager; 20 import android.accounts.AccountManagerCallback; 21 import android.accounts.AccountManagerFuture; 22 import android.accounts.AuthenticatorException; 23 import android.accounts.OperationCanceledException; 24 import android.app.Activity; 25 import android.app.PendingIntent; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.os.Bundle; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.util.Log; 33 import android.widget.Toast; 34 35 import com.android.settings.ChooseLockSettingsHelper; 36 import com.android.settings.R; 37 import com.android.settings.Settings; 38 import com.android.settings.Utils; 39 40 import java.io.IOException; 41 42 import static android.content.Intent.EXTRA_USER; 43 /** 44 * Entry point Activity for account setup. Works as follows 45 * 46 * 1) When the other Activities launch this Activity, it launches {@link ChooseAccountActivity} 47 * without showing anything. 48 * 2) After receiving an account type from ChooseAccountActivity, this Activity launches the 49 * account setup specified by AccountManager. 50 * 3) After the account setup, this Activity finishes without showing anything. 51 * 52 * Note: 53 * Previously this Activity did what {@link ChooseAccountActivity} does right now, but we 54 * currently delegate the work to the other Activity. When we let this Activity do that work, users 55 * would see the list of account types when leaving this Activity, since the UI is already ready 56 * when returning from each account setup, which doesn't look good. 57 * 58 * An extra {@link UserHandle} can be specified in the intent as {@link EXTRA_USER}, if the user for 59 * which the action needs to be performed is different to the one the Settings App will run in. 60 */ 61 public class AddAccountSettings extends Activity { 62 /** 63 * 64 */ 65 private static final String KEY_ADD_CALLED = "AddAccountCalled"; 66 67 /** 68 * Extra parameter to identify the caller. Applications may display a 69 * different UI if the calls is made from Settings or from a specific 70 * application. 71 */ 72 private static final String KEY_CALLER_IDENTITY = "pendingIntent"; 73 private static final String SHOULD_NOT_RESOLVE = "SHOULDN'T RESOLVE!"; 74 75 private static final String TAG = "AddAccountSettings"; 76 77 /* package */ static final String EXTRA_SELECTED_ACCOUNT = "selected_account"; 78 79 // show additional info regarding the use of a device with multiple users 80 static final String EXTRA_HAS_MULTIPLE_USERS = "hasMultipleUsers"; 81 82 private static final int CHOOSE_ACCOUNT_REQUEST = 1; 83 private static final int ADD_ACCOUNT_REQUEST = 2; 84 private static final int UNLOCK_WORK_PROFILE_REQUEST = 3; 85 86 private PendingIntent mPendingIntent; 87 88 private final AccountManagerCallback<Bundle> mCallback = new AccountManagerCallback<Bundle>() { 89 @Override 90 public void run(AccountManagerFuture<Bundle> future) { 91 boolean done = true; 92 try { 93 Bundle bundle = future.getResult(); 94 //bundle.keySet(); 95 Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT); 96 if (intent != null) { 97 done = false; 98 Bundle addAccountOptions = new Bundle(); 99 addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent); 100 addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, 101 Utils.hasMultipleUsers(AddAccountSettings.this)); 102 addAccountOptions.putParcelable(EXTRA_USER, mUserHandle); 103 intent.putExtras(addAccountOptions); 104 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 105 startActivityForResultAsUser(intent, ADD_ACCOUNT_REQUEST, mUserHandle); 106 } else { 107 setResult(RESULT_OK); 108 if (mPendingIntent != null) { 109 mPendingIntent.cancel(); 110 mPendingIntent = null; 111 } 112 } 113 114 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "account added: " + bundle); 115 } catch (OperationCanceledException e) { 116 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount was canceled"); 117 } catch (IOException e) { 118 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e); 119 } catch (AuthenticatorException e) { 120 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e); 121 } finally { 122 if (done) { 123 finish(); 124 } 125 } 126 } 127 }; 128 129 private boolean mAddAccountCalled = false; 130 private UserHandle mUserHandle; 131 132 @Override 133 public void onCreate(Bundle savedInstanceState) { 134 super.onCreate(savedInstanceState); 135 136 if (savedInstanceState != null) { 137 mAddAccountCalled = savedInstanceState.getBoolean(KEY_ADD_CALLED); 138 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "restored"); 139 } 140 141 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 142 mUserHandle = Utils.getSecureTargetUser(getActivityToken(), um, null /* arguments */, 143 getIntent().getExtras()); 144 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle)) { 145 // We aren't allowed to add an account. 146 Toast.makeText(this, R.string.user_cannot_add_accounts_message, Toast.LENGTH_LONG) 147 .show(); 148 finish(); 149 return; 150 } 151 if (mAddAccountCalled) { 152 // We already called add account - maybe the callback was lost. 153 finish(); 154 return; 155 } 156 if (Utils.startQuietModeDialogIfNecessary(this, um, mUserHandle.getIdentifier())) { 157 finish(); 158 return; 159 } 160 if (um.isUserUnlocked(mUserHandle)) { 161 requestChooseAccount(); 162 } else { 163 // If the user is locked by fbe: we couldn't start the authenticator. So we must ask the 164 // user to unlock it first. 165 ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this); 166 if (!helper.launchConfirmationActivity(UNLOCK_WORK_PROFILE_REQUEST, 167 getString(R.string.unlock_set_unlock_launch_picker_title), 168 false, 169 mUserHandle.getIdentifier())) { 170 requestChooseAccount(); 171 } 172 } 173 } 174 175 @Override 176 public void onActivityResult(int requestCode, int resultCode, Intent data) { 177 switch (requestCode) { 178 case UNLOCK_WORK_PROFILE_REQUEST: 179 if (resultCode == Activity.RESULT_OK) { 180 requestChooseAccount(); 181 } else { 182 finish(); 183 } 184 break; 185 case CHOOSE_ACCOUNT_REQUEST: 186 if (resultCode == RESULT_CANCELED) { 187 if (data != null) { 188 startActivityAsUser(data, mUserHandle); 189 } 190 setResult(resultCode); 191 finish(); 192 return; 193 } 194 // Go to account setup screen. finish() is called inside mCallback. 195 addAccount(data.getStringExtra(EXTRA_SELECTED_ACCOUNT)); 196 break; 197 case ADD_ACCOUNT_REQUEST: 198 setResult(resultCode); 199 if (mPendingIntent != null) { 200 mPendingIntent.cancel(); 201 mPendingIntent = null; 202 } 203 finish(); 204 break; 205 } 206 } 207 208 @Override 209 protected void onSaveInstanceState(Bundle outState) { 210 super.onSaveInstanceState(outState); 211 outState.putBoolean(KEY_ADD_CALLED, mAddAccountCalled); 212 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "saved"); 213 } 214 215 private void requestChooseAccount() { 216 final String[] authorities = 217 getIntent().getStringArrayExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY); 218 final String[] accountTypes = 219 getIntent().getStringArrayExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY); 220 final Intent intent = new Intent(this, Settings.ChooseAccountActivity.class); 221 if (authorities != null) { 222 intent.putExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY, authorities); 223 } 224 if (accountTypes != null) { 225 intent.putExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY, accountTypes); 226 } 227 intent.putExtra(EXTRA_USER, mUserHandle); 228 startActivityForResult(intent, CHOOSE_ACCOUNT_REQUEST); 229 } 230 231 private void addAccount(String accountType) { 232 Bundle addAccountOptions = new Bundle(); 233 /* 234 * The identityIntent is for the purposes of establishing the identity 235 * of the caller and isn't intended for launching activities, services 236 * or broadcasts. 237 * 238 * Unfortunately for legacy reasons we still need to support this. But 239 * we can cripple the intent so that 3rd party authenticators can't 240 * fill in addressing information and launch arbitrary actions. 241 */ 242 Intent identityIntent = new Intent(); 243 identityIntent.setComponent(new ComponentName(SHOULD_NOT_RESOLVE, SHOULD_NOT_RESOLVE)); 244 identityIntent.setAction(SHOULD_NOT_RESOLVE); 245 identityIntent.addCategory(SHOULD_NOT_RESOLVE); 246 247 mPendingIntent = PendingIntent.getBroadcast(this, 0, identityIntent, 0); 248 addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent); 249 addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, Utils.hasMultipleUsers(this)); 250 AccountManager.get(this).addAccountAsUser( 251 accountType, 252 null, /* authTokenType */ 253 null, /* requiredFeatures */ 254 addAccountOptions, 255 null, 256 mCallback, 257 null /* handler */, 258 mUserHandle); 259 mAddAccountCalled = true; 260 } 261 } 262