AbstractAccountAuthenticator.java revision f7ae77cd67f1a3993b8e56c1af4720a7adf4e69d
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 17package android.accounts; 18 19import android.os.Bundle; 20import android.os.RemoteException; 21import android.os.Binder; 22import android.os.IBinder; 23import android.content.pm.PackageManager; 24import android.content.Context; 25import android.content.Intent; 26import android.Manifest; 27 28/** 29 * Base class for creating AccountAuthenticators. This implements the IAccountAuthenticator 30 * binder interface and also provides helper libraries to simplify the creation of 31 * AccountAuthenticators. 32 */ 33public abstract class AbstractAccountAuthenticator { 34 private final Context mContext; 35 36 public AbstractAccountAuthenticator(Context context) { 37 mContext = context; 38 } 39 40 private class Transport extends IAccountAuthenticator.Stub { 41 public void addAccount(IAccountAuthenticatorResponse response, String accountType, 42 String authTokenType, String[] requiredFeatures, Bundle options) 43 throws RemoteException { 44 checkBinderPermission(); 45 final Bundle result; 46 try { 47 result = AbstractAccountAuthenticator.this.addAccount( 48 new AccountAuthenticatorResponse(response), 49 accountType, authTokenType, requiredFeatures, options); 50 } catch (NetworkErrorException e) { 51 response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage()); 52 return; 53 } catch (UnsupportedOperationException e) { 54 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, 55 "addAccount not supported"); 56 return; 57 } 58 if (result != null) { 59 response.onResult(result); 60 } else { 61 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 62 "no response from the authenticator"); 63 } 64 } 65 66 public void confirmCredentials(IAccountAuthenticatorResponse response, 67 Account account, Bundle options) throws RemoteException { 68 checkBinderPermission(); 69 final Bundle result; 70 try { 71 result = AbstractAccountAuthenticator.this.confirmCredentials( 72 new AccountAuthenticatorResponse(response), account, options); 73 } catch (UnsupportedOperationException e) { 74 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, 75 "confirmCredentials not supported"); 76 return; 77 } 78 if (result != null) { 79 response.onResult(result); 80 } else { 81 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 82 "no response from the authenticator"); 83 } 84 } 85 86 public void getAuthTokenLabel(IAccountAuthenticatorResponse response, 87 String authTokenType) 88 throws RemoteException { 89 checkBinderPermission(); 90 try { 91 Bundle result = new Bundle(); 92 result.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, 93 AbstractAccountAuthenticator.this.getAuthTokenLabel(authTokenType)); 94 response.onResult(result); 95 } catch (IllegalArgumentException e) { 96 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, 97 "unknown authTokenType"); 98 } catch (UnsupportedOperationException e) { 99 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, 100 "getAuthTokenTypeLabel not supported"); 101 } 102 } 103 104 public void getAuthToken(IAccountAuthenticatorResponse response, 105 Account account, String authTokenType, Bundle loginOptions) 106 throws RemoteException { 107 checkBinderPermission(); 108 try { 109 final Bundle result = AbstractAccountAuthenticator.this.getAuthToken( 110 new AccountAuthenticatorResponse(response), account, 111 authTokenType, loginOptions); 112 if (result != null) { 113 response.onResult(result); 114 } else { 115 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 116 "no response from the authenticator"); 117 } 118 } catch (UnsupportedOperationException e) { 119 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, 120 "getAuthToken not supported"); 121 } catch (NetworkErrorException e) { 122 response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage()); 123 } 124 } 125 126 public void updateCredentials(IAccountAuthenticatorResponse response, Account account, 127 String authTokenType, Bundle loginOptions) throws RemoteException { 128 checkBinderPermission(); 129 final Bundle result; 130 try { 131 result = AbstractAccountAuthenticator.this.updateCredentials( 132 new AccountAuthenticatorResponse(response), account, 133 authTokenType, loginOptions); 134 } catch (UnsupportedOperationException e) { 135 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, 136 "updateCredentials not supported"); 137 return; 138 } 139 if (result != null) { 140 response.onResult(result); 141 } else { 142 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 143 "no response from the authenticator"); 144 } 145 } 146 147 public void editProperties(IAccountAuthenticatorResponse response, 148 String accountType) throws RemoteException { 149 checkBinderPermission(); 150 final Bundle result; 151 try { 152 result = AbstractAccountAuthenticator.this.editProperties( 153 new AccountAuthenticatorResponse(response), accountType); 154 } catch (UnsupportedOperationException e) { 155 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, 156 "editProperties not supported"); 157 return; 158 } 159 if (result != null) { 160 response.onResult(result); 161 } else { 162 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 163 "no response from the authenticator"); 164 } 165 } 166 167 public void hasFeatures(IAccountAuthenticatorResponse response, 168 Account account, String[] features) throws RemoteException { 169 checkBinderPermission(); 170 final Bundle result; 171 try { 172 result = AbstractAccountAuthenticator.this.hasFeatures( 173 new AccountAuthenticatorResponse(response), account, features); 174 } catch (UnsupportedOperationException e) { 175 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, 176 "hasFeatures not supported"); 177 return; 178 } catch (NetworkErrorException e) { 179 response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage()); 180 return; 181 } 182 if (result != null) { 183 response.onResult(result); 184 } else { 185 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 186 "no response from the authenticator"); 187 } 188 } 189 190 public void getAccountRemovalAllowed(IAccountAuthenticatorResponse response, 191 Account account) throws RemoteException { 192 checkBinderPermission(); 193 try { 194 final Bundle result = AbstractAccountAuthenticator.this.getAccountRemovalAllowed( 195 new AccountAuthenticatorResponse(response), account); 196 if (result != null) { 197 response.onResult(result); 198 } else { 199 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 200 "no response from the authenticator"); 201 } 202 } catch (UnsupportedOperationException e) { 203 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, 204 "getAccountRemovalAllowed not supported"); 205 } catch (NetworkErrorException e) { 206 response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage()); 207 } 208 } 209 } 210 211 private void checkBinderPermission() { 212 final int uid = Binder.getCallingUid(); 213 final String perm = Manifest.permission.ACCOUNT_MANAGER; 214 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) { 215 throw new SecurityException("caller uid " + uid + " lacks " + perm); 216 } 217 } 218 219 private Transport mTransport = new Transport(); 220 221 /** 222 * @return the IBinder for the AccountAuthenticator 223 */ 224 public final IBinder getIBinder() { 225 return mTransport.asBinder(); 226 } 227 228 /** 229 * Returns a Bundle that contains the Intent of the activity that can be used to edit the 230 * properties. In order to indicate success the activity should call response.setResult() 231 * with a non-null Bundle. 232 * @param response used to set the result for the request. If the Constants.INTENT_KEY 233 * is set in the bundle then this response field is to be used for sending future 234 * results if and when the Intent is started. 235 * @param accountType the AccountType whose properties are to be edited. 236 * @return a Bundle containing the result or the Intent to start to continue the request. 237 * If this is null then the request is considered to still be active and the result should 238 * sent later using response. 239 */ 240 public abstract Bundle editProperties(AccountAuthenticatorResponse response, 241 String accountType); 242 public abstract Bundle addAccount(AccountAuthenticatorResponse response, String accountType, 243 String authTokenType, String[] requiredFeatures, Bundle options) 244 throws NetworkErrorException; 245 public abstract Bundle confirmCredentials(AccountAuthenticatorResponse response, 246 Account account, Bundle options); 247 public abstract Bundle getAuthToken(AccountAuthenticatorResponse response, 248 Account account, String authTokenType, Bundle loginOptions) 249 throws NetworkErrorException; 250 public abstract String getAuthTokenLabel(String authTokenType); 251 public abstract Bundle updateCredentials(AccountAuthenticatorResponse response, 252 Account account, String authTokenType, Bundle loginOptions); 253 public abstract Bundle hasFeatures(AccountAuthenticatorResponse response, 254 Account account, String[] features) throws NetworkErrorException; 255 public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, 256 Account account) throws NetworkErrorException { 257 final Bundle result = new Bundle(); 258 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 259 return result; 260 } 261} 262