1 /* 2 * Copyright (C) 2009 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 android.accounts.cts; 18 19 import android.accounts.AbstractAccountAuthenticator; 20 import android.accounts.Account; 21 import android.accounts.AccountAuthenticatorResponse; 22 import android.accounts.AccountManager; 23 import android.accounts.NetworkErrorException; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.os.Bundle; 27 import android.util.Log; 28 29 import java.util.ArrayList; 30 import java.util.concurrent.atomic.AtomicBoolean; 31 import java.util.concurrent.atomic.AtomicInteger; 32 33 /** 34 * A simple Mock Account Authenticator 35 */ 36 public class MockAccountAuthenticator extends AbstractAccountAuthenticator { 37 private static String TAG = "AccountManagerTest"; 38 39 public static String KEY_ACCOUNT_INFO = "key_account_info"; 40 public static String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "key_account_authenticator_response"; 41 public static String ACCOUNT_NAME_FOR_NEW_REMOVE_API = "call new removeAccount api"; 42 public static String ACCOUNT_NAME_FOR_DEFAULT_IMPL = "call super api"; 43 // Key for triggering return intent flow 44 public static String KEY_RETURN_INTENT = "return an intent"; 45 public static String ACCOUNT_NAME_FOR_NEW_REMOVE_API1 = "call new removeAccount api"; 46 47 private final Context mContext; 48 private final AtomicInteger mTokenCounter = new AtomicInteger(0); 49 private final AtomicBoolean mIsRecentlyCalled = new AtomicBoolean(false); 50 51 AccountAuthenticatorResponse mResponse; 52 String mAccountType; 53 String mAuthTokenType; 54 String[] mRequiredFeatures; 55 public Bundle mOptionsUpdateCredentials; 56 public Bundle mOptionsConfirmCredentials; 57 public Bundle mOptionsAddAccount; 58 public Bundle mOptionsGetAuthToken; 59 Account mAccount; 60 String[] mFeatures; 61 62 final ArrayList<String> mockFeatureList = new ArrayList<String>(); 63 private final long mTokenDurationMillis = 1000; // 1 second 64 MockAccountAuthenticator(Context context)65 public MockAccountAuthenticator(Context context) { 66 super(context); 67 mContext = context; 68 69 // Create some mock features 70 mockFeatureList.add(AccountManagerTest.FEATURE_1); 71 mockFeatureList.add(AccountManagerTest.FEATURE_2); 72 } 73 getTokenDurationMillis()74 public long getTokenDurationMillis() { 75 return mTokenDurationMillis; 76 } 77 isRecentlyCalled()78 public boolean isRecentlyCalled() { 79 return mIsRecentlyCalled.getAndSet(false); 80 } 81 getLastTokenServed()82 public String getLastTokenServed() { 83 return Integer.toString(mTokenCounter.get()); 84 } 85 getResponse()86 public AccountAuthenticatorResponse getResponse() { 87 return mResponse; 88 } 89 getAccountType()90 public String getAccountType() { 91 return mAccountType; 92 } 93 getAuthTokenType()94 public String getAuthTokenType() { 95 return mAuthTokenType; 96 } 97 getRequiredFeatures()98 public String[] getRequiredFeatures() { 99 return mRequiredFeatures; 100 } 101 getAccount()102 public Account getAccount() { 103 return mAccount; 104 } 105 getFeatures()106 public String[] getFeatures() { 107 return mFeatures; 108 } 109 clearData()110 public void clearData() { 111 mResponse = null; 112 mAccountType = null; 113 mAuthTokenType = null; 114 mRequiredFeatures = null; 115 mOptionsUpdateCredentials = null; 116 mOptionsAddAccount = null; 117 mOptionsGetAuthToken = null; 118 mOptionsConfirmCredentials = null; 119 mAccount = null; 120 mFeatures = null; 121 } 122 callAccountAuthenticated()123 public void callAccountAuthenticated() { 124 AccountManager am = AccountManager.get(mContext); 125 am.notifyAccountAuthenticated(mAccount); 126 } 127 callSetPassword()128 public void callSetPassword() { 129 AccountManager am = AccountManager.get(mContext); 130 am.setPassword(mAccount, "password"); 131 } 132 createResultBundle()133 private Bundle createResultBundle() { 134 Bundle result = new Bundle(); 135 result.putString(AccountManager.KEY_ACCOUNT_NAME, AccountManagerTest.ACCOUNT_NAME); 136 result.putString(AccountManager.KEY_ACCOUNT_TYPE, AccountManagerTest.ACCOUNT_TYPE); 137 result.putString( 138 AccountManager.KEY_AUTHTOKEN, 139 Integer.toString(mTokenCounter.incrementAndGet())); 140 return result; 141 } 142 143 /** 144 * Adds an account of the specified accountType. 145 */ 146 @Override addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options)147 public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, 148 String authTokenType, String[] requiredFeatures, Bundle options) 149 throws NetworkErrorException { 150 this.mResponse = response; 151 this.mAccountType = accountType; 152 this.mAuthTokenType = authTokenType; 153 this.mRequiredFeatures = requiredFeatures; 154 this.mOptionsAddAccount = options; 155 AccountManager am = AccountManager.get(mContext); 156 am.addAccountExplicitly(AccountManagerTest.ACCOUNT, "fakePassword", null); 157 return createResultBundle(); 158 } 159 160 /** 161 * Update the locally stored credentials for an account. 162 */ 163 @Override updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)164 public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, 165 String authTokenType, Bundle options) throws NetworkErrorException { 166 this.mResponse = response; 167 this.mAccount = account; 168 this.mAuthTokenType = authTokenType; 169 this.mOptionsUpdateCredentials = options; 170 return createResultBundle(); 171 } 172 173 /** 174 * Returns a Bundle that contains the Intent of the activity that can be used to edit the 175 * properties. In order to indicate success the activity should call response.setResult() 176 * with a non-null Bundle. 177 */ 178 @Override editProperties(AccountAuthenticatorResponse response, String accountType)179 public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { 180 this.mResponse = response; 181 this.mAccountType = accountType; 182 return createResultBundle(); 183 } 184 185 /** 186 * Checks that the user knows the credentials of an account. 187 */ 188 @Override confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options)189 public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, 190 Bundle options) throws NetworkErrorException { 191 this.mResponse = response; 192 this.mAccount = account; 193 this.mOptionsConfirmCredentials = options; 194 Bundle result = new Bundle(); 195 if (options.containsKey(KEY_RETURN_INTENT)) { 196 Intent intent = new Intent(); 197 intent.setClassName("android.accounts.cts", "android.accounts.cts.AccountDummyActivity"); 198 result.putParcelable(AccountManager.KEY_INTENT, intent); 199 } else { 200 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 201 } 202 203 return result; 204 } 205 206 /** 207 * Gets the authtoken for an account. 208 */ 209 @Override getAuthToken( AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)210 public Bundle getAuthToken( 211 AccountAuthenticatorResponse response, 212 Account account, 213 String authTokenType, 214 Bundle options) throws NetworkErrorException { 215 Log.w(TAG, "MockAuth - getAuthToken@" + System.currentTimeMillis()); 216 mIsRecentlyCalled.set(true); 217 this.mResponse = response; 218 this.mAccount = account; 219 this.mAuthTokenType = authTokenType; 220 this.mOptionsGetAuthToken = options; 221 Bundle result = new Bundle(); 222 223 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 224 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 225 String token; 226 if (AccountManagerTest.AUTH_EXPIRING_TOKEN_TYPE.equals(authTokenType)) { 227 /* 228 * The resultant token should simply be the expiration timestamp. E.g. the time after 229 * which getting a new AUTH_EXPIRING_TOKEN_TYPE typed token will return a different 230 * value. 231 */ 232 long expiry = System.currentTimeMillis() + mTokenDurationMillis; 233 result.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, expiry); 234 } 235 result.putString( 236 AccountManager.KEY_AUTHTOKEN, 237 Integer.toString(mTokenCounter.incrementAndGet())); 238 return result; 239 } 240 241 /** 242 * Ask the authenticator for a localized label for the given authTokenType. 243 */ 244 @Override getAuthTokenLabel(String authTokenType)245 public String getAuthTokenLabel(String authTokenType) { 246 this.mAuthTokenType = authTokenType; 247 return AccountManagerTest.AUTH_TOKEN_LABEL; 248 } 249 250 /** 251 * Checks if the account supports all the specified authenticator specific features. 252 */ 253 @Override hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features)254 public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, 255 String[] features) throws NetworkErrorException { 256 257 this.mResponse = response; 258 this.mAccount = account; 259 this.mFeatures = features; 260 261 Bundle result = new Bundle(); 262 if (null == features) { 263 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 264 } 265 else { 266 boolean booleanResult = true; 267 for (String feature: features) { 268 if (!mockFeatureList.contains(feature)) { 269 booleanResult = false; 270 break; 271 } 272 } 273 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, booleanResult); 274 } 275 return result; 276 } 277 278 @Override getAccountRemovalAllowed(AccountAuthenticatorResponse response, Account account)279 public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, 280 Account account) throws NetworkErrorException { 281 final Bundle result = new Bundle(); 282 if (ACCOUNT_NAME_FOR_NEW_REMOVE_API.equals(account.name)) { 283 Intent intent = AccountRemovalDummyActivity.createIntent(mContext); 284 // Pass in the authenticator response, so that account removal can 285 // be 286 // completed 287 intent.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); 288 intent.putExtra(KEY_ACCOUNT_INFO, account); 289 result.putParcelable(AccountManager.KEY_INTENT, intent); 290 // Adding this following line to reject account installation 291 // requests 292 // coming from old removeAccount API. 293 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false); 294 } else if (ACCOUNT_NAME_FOR_DEFAULT_IMPL.equals(account.name)) { 295 return super.getAccountRemovalAllowed(response, account); 296 } else { 297 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 298 } 299 return result; 300 } 301 302 @Override addAccountFromCredentials(final AccountAuthenticatorResponse response, Account account, Bundle accountCredentials)303 public Bundle addAccountFromCredentials(final AccountAuthenticatorResponse response, 304 Account account, 305 Bundle accountCredentials) throws NetworkErrorException { 306 return super.addAccountFromCredentials(response, account, accountCredentials); 307 } 308 309 @Override getAccountCredentialsForCloning(final AccountAuthenticatorResponse response, final Account account)310 public Bundle getAccountCredentialsForCloning(final AccountAuthenticatorResponse response, 311 final Account account) throws NetworkErrorException { 312 return super.getAccountCredentialsForCloning(response, account); 313 } 314 315 } 316