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