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