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 final ChooseLockSettingsHelper.Builder builder = 167 new ChooseLockSettingsHelper.Builder(this); 168 final boolean launched = builder.setRequestCode(UNLOCK_WORK_PROFILE_REQUEST) 169 .setTitle(getString(R.string.unlock_set_unlock_launch_picker_title)) 170 .setUserId(mUserHandle.getIdentifier()) 171 .show(); 172 if (!launched) { 173 requestChooseAccount(); 174 } 175 } 176 } 177 178 @Override onActivityResult(int requestCode, int resultCode, Intent data)179 public void onActivityResult(int requestCode, int resultCode, Intent data) { 180 switch (requestCode) { 181 case UNLOCK_WORK_PROFILE_REQUEST: 182 if (resultCode == Activity.RESULT_OK) { 183 requestChooseAccount(); 184 } else { 185 finish(); 186 } 187 break; 188 case CHOOSE_ACCOUNT_REQUEST: 189 if (resultCode == RESULT_CANCELED) { 190 if (data != null) { 191 startActivityAsUser(data, mUserHandle); 192 } 193 setResult(resultCode); 194 finish(); 195 return; 196 } 197 // Go to account setup screen. finish() is called inside mCallback. 198 addAccount(data.getStringExtra(EXTRA_SELECTED_ACCOUNT)); 199 break; 200 case ADD_ACCOUNT_REQUEST: 201 setResult(resultCode); 202 if (mPendingIntent != null) { 203 mPendingIntent.cancel(); 204 mPendingIntent = null; 205 } 206 finish(); 207 break; 208 } 209 } 210 211 @Override onSaveInstanceState(Bundle outState)212 protected void onSaveInstanceState(Bundle outState) { 213 super.onSaveInstanceState(outState); 214 outState.putBoolean(KEY_ADD_CALLED, mAddAccountCalled); 215 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "saved"); 216 } 217 requestChooseAccount()218 private void requestChooseAccount() { 219 final String[] authorities = 220 getIntent().getStringArrayExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY); 221 final String[] accountTypes = 222 getIntent().getStringArrayExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY); 223 final Intent intent = new Intent(this, Settings.ChooseAccountActivity.class); 224 if (authorities != null) { 225 intent.putExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY, authorities); 226 } 227 if (accountTypes != null) { 228 intent.putExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY, accountTypes); 229 } 230 intent.putExtra(EXTRA_USER, mUserHandle); 231 startActivityForResult(intent, CHOOSE_ACCOUNT_REQUEST); 232 } 233 addAccount(String accountType)234 private void addAccount(String accountType) { 235 Bundle addAccountOptions = new Bundle(); 236 /* 237 * The identityIntent is for the purposes of establishing the identity 238 * of the caller and isn't intended for launching activities, services 239 * or broadcasts. 240 * 241 * Unfortunately for legacy reasons we still need to support this. But 242 * we can disable the intent so that 3rd party authenticators can't 243 * fill in addressing information and launch arbitrary actions. 244 */ 245 Intent identityIntent = new Intent(); 246 identityIntent.setComponent(new ComponentName(SHOULD_NOT_RESOLVE, SHOULD_NOT_RESOLVE)); 247 identityIntent.setAction(SHOULD_NOT_RESOLVE); 248 identityIntent.addCategory(SHOULD_NOT_RESOLVE); 249 250 mPendingIntent = PendingIntent.getBroadcast(this, 0, identityIntent, 251 PendingIntent.FLAG_IMMUTABLE); 252 addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent); 253 addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, Utils.hasMultipleUsers(this)); 254 AccountManager.get(this).addAccountAsUser( 255 accountType, 256 null, /* authTokenType */ 257 null, /* requiredFeatures */ 258 addAccountOptions, 259 null, 260 mCallback, 261 null /* handler */, 262 mUserHandle); 263 mAddAccountCalled = true; 264 } 265 } 266