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 static android.content.Intent.EXTRA_USER; 20 21 import android.accounts.AccountManager; 22 import android.accounts.AccountManagerCallback; 23 import android.accounts.AccountManagerFuture; 24 import android.accounts.AuthenticatorException; 25 import android.accounts.OperationCanceledException; 26 import android.app.Activity; 27 import android.app.PendingIntent; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.os.Bundle; 32 import android.os.UserHandle; 33 import android.os.UserManager; 34 import android.util.Log; 35 import android.widget.Toast; 36 37 import com.android.settings.R; 38 import com.android.settings.Settings; 39 import com.android.settings.Utils; 40 import com.android.settings.password.ChooseLockSettingsHelper; 41 42 import java.io.IOException; 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 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 105 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); 106 startActivityForResultAsUser(intent, ADD_ACCOUNT_REQUEST, mUserHandle); 107 } else { 108 setResult(RESULT_OK); 109 if (mPendingIntent != null) { 110 mPendingIntent.cancel(); 111 mPendingIntent = null; 112 } 113 } 114 115 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "account added: " + bundle); 116 } catch (OperationCanceledException e) { 117 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount was canceled"); 118 } catch (IOException e) { 119 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e); 120 } catch (AuthenticatorException e) { 121 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e); 122 } finally { 123 if (done) { 124 finish(); 125 } 126 } 127 } 128 }; 129 130 private boolean mAddAccountCalled = false; 131 private UserHandle mUserHandle; 132 133 @Override onCreate(Bundle savedInstanceState)134 public void onCreate(Bundle savedInstanceState) { 135 super.onCreate(savedInstanceState); 136 137 if (savedInstanceState != null) { 138 mAddAccountCalled = savedInstanceState.getBoolean(KEY_ADD_CALLED); 139 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "restored"); 140 } 141 142 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 143 mUserHandle = Utils.getSecureTargetUser(getActivityToken(), um, null /* arguments */, 144 getIntent().getExtras()); 145 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle)) { 146 // We aren't allowed to add an account. 147 Toast.makeText(this, R.string.user_cannot_add_accounts_message, Toast.LENGTH_LONG) 148 .show(); 149 finish(); 150 return; 151 } 152 if (mAddAccountCalled) { 153 // We already called add account - maybe the callback was lost. 154 finish(); 155 return; 156 } 157 if (Utils.startQuietModeDialogIfNecessary(this, um, mUserHandle.getIdentifier())) { 158 finish(); 159 return; 160 } 161 if (um.isUserUnlocked(mUserHandle)) { 162 requestChooseAccount(); 163 } else { 164 // If the user is locked by fbe: we couldn't start the authenticator. So we must ask the 165 // user to unlock it first. 166 ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this); 167 if (!helper.launchConfirmationActivity(UNLOCK_WORK_PROFILE_REQUEST, 168 getString(R.string.unlock_set_unlock_launch_picker_title), 169 false, 170 mUserHandle.getIdentifier())) { 171 requestChooseAccount(); 172 } 173 } 174 } 175 176 @Override onActivityResult(int requestCode, int resultCode, Intent data)177 public void onActivityResult(int requestCode, int resultCode, Intent data) { 178 switch (requestCode) { 179 case UNLOCK_WORK_PROFILE_REQUEST: 180 if (resultCode == Activity.RESULT_OK) { 181 requestChooseAccount(); 182 } else { 183 finish(); 184 } 185 break; 186 case CHOOSE_ACCOUNT_REQUEST: 187 if (resultCode == RESULT_CANCELED) { 188 if (data != null) { 189 startActivityAsUser(data, mUserHandle); 190 } 191 setResult(resultCode); 192 finish(); 193 return; 194 } 195 // Go to account setup screen. finish() is called inside mCallback. 196 addAccount(data.getStringExtra(EXTRA_SELECTED_ACCOUNT)); 197 break; 198 case ADD_ACCOUNT_REQUEST: 199 setResult(resultCode); 200 if (mPendingIntent != null) { 201 mPendingIntent.cancel(); 202 mPendingIntent = null; 203 } 204 finish(); 205 break; 206 } 207 } 208 209 @Override onSaveInstanceState(Bundle outState)210 protected void onSaveInstanceState(Bundle outState) { 211 super.onSaveInstanceState(outState); 212 outState.putBoolean(KEY_ADD_CALLED, mAddAccountCalled); 213 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "saved"); 214 } 215 requestChooseAccount()216 private void requestChooseAccount() { 217 final String[] authorities = 218 getIntent().getStringArrayExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY); 219 final String[] accountTypes = 220 getIntent().getStringArrayExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY); 221 final Intent intent = new Intent(this, Settings.ChooseAccountActivity.class); 222 if (authorities != null) { 223 intent.putExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY, authorities); 224 } 225 if (accountTypes != null) { 226 intent.putExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY, accountTypes); 227 } 228 intent.putExtra(EXTRA_USER, mUserHandle); 229 startActivityForResult(intent, CHOOSE_ACCOUNT_REQUEST); 230 } 231 addAccount(String accountType)232 private void addAccount(String accountType) { 233 Bundle addAccountOptions = new Bundle(); 234 /* 235 * The identityIntent is for the purposes of establishing the identity 236 * of the caller and isn't intended for launching activities, services 237 * or broadcasts. 238 * 239 * Unfortunately for legacy reasons we still need to support this. But 240 * we can cripple the intent so that 3rd party authenticators can't 241 * fill in addressing information and launch arbitrary actions. 242 */ 243 Intent identityIntent = new Intent(); 244 identityIntent.setComponent(new ComponentName(SHOULD_NOT_RESOLVE, SHOULD_NOT_RESOLVE)); 245 identityIntent.setAction(SHOULD_NOT_RESOLVE); 246 identityIntent.addCategory(SHOULD_NOT_RESOLVE); 247 248 mPendingIntent = PendingIntent.getBroadcast(this, 0, identityIntent, 0); 249 addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent); 250 addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, Utils.hasMultipleUsers(this)); 251 AccountManager.get(this).addAccountAsUser( 252 accountType, 253 null, /* authTokenType */ 254 null, /* requiredFeatures */ 255 addAccountOptions, 256 null, 257 mCallback, 258 null /* handler */, 259 mUserHandle); 260 mAddAccountCalled = true; 261 } 262 } 263