blob: 6c28485c8ad99c5c8e6b63eb082be7f0bb3b1200 [file] [log] [blame]
Fred Quintana60307342009-03-24 22:48:12 -07001/*
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
Fred Quintanaa698f422009-04-08 19:14:54 -070019import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
Fred Quintana60307342009-03-24 22:48:12 -070023import android.os.Handler;
24import android.os.IBinder;
25import android.os.Message;
Fred Quintanaa698f422009-04-08 19:14:54 -070026import android.util.Log;
Fred Quintana60307342009-03-24 22:48:12 -070027
Fred Quintana60307342009-03-24 22:48:12 -070028import java.util.ArrayList;
Fred Quintanaa698f422009-04-08 19:14:54 -070029import java.util.Map;
Fred Quintana60307342009-03-24 22:48:12 -070030
Fred Quintana60307342009-03-24 22:48:12 -070031import com.google.android.collect.Lists;
Fred Quintanaa698f422009-04-08 19:14:54 -070032import com.google.android.collect.Maps;
Fred Quintana60307342009-03-24 22:48:12 -070033
34/**
35 * A helper object that simplifies binding to Account Authenticators. It uses the
36 * {@link AccountAuthenticatorCache} to find the component name of the authenticators,
37 * allowing the user to bind by account name. It also allows multiple, simultaneous binds
38 * to the same authenticator, with each bind call guaranteed to return either
39 * {@link Callback#onConnected} or {@link Callback#onDisconnected} if the bind() call
40 * itself succeeds, even if the authenticator is already bound internally.
41 */
42public class AuthenticatorBindHelper {
Fred Quintanaa698f422009-04-08 19:14:54 -070043 private static final String TAG = "Accounts";
44 private final Handler mHandler;
45 private final Context mContext;
46 private final int mMessageWhatConnected;
47 private final int mMessageWhatDisconnected;
48 private final Map<String, MyServiceConnection> mServiceConnections = Maps.newHashMap();
49 private final Map<String, ArrayList<Callback>> mServiceUsers = Maps.newHashMap();
50 private final AccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -070051
52 public AuthenticatorBindHelper(Context context,
53 AccountAuthenticatorCache authenticatorCache, Handler handler,
54 int messageWhatConnected, int messageWhatDisconnected) {
55 mContext = context;
56 mHandler = handler;
57 mAuthenticatorCache = authenticatorCache;
58 mMessageWhatConnected = messageWhatConnected;
59 mMessageWhatDisconnected = messageWhatDisconnected;
60 }
61
62 public interface Callback {
63 void onConnected(IBinder service);
64 void onDisconnected();
65 }
66
67 public boolean bind(String authenticatorType, Callback callback) {
68 // if the authenticator is connecting or connected then return true
69 synchronized (mServiceConnections) {
70 if (mServiceConnections.containsKey(authenticatorType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -070071 MyServiceConnection connection = mServiceConnections.get(authenticatorType);
72 if (Log.isLoggable(TAG, Log.VERBOSE)) {
73 Log.v(TAG, "service connection already exists for " + authenticatorType);
74 }
Fred Quintana60307342009-03-24 22:48:12 -070075 mServiceUsers.get(authenticatorType).add(callback);
Fred Quintanaa698f422009-04-08 19:14:54 -070076 if (connection.mService != null) {
77 if (Log.isLoggable(TAG, Log.VERBOSE)) {
78 Log.v(TAG, "the service is connected, scheduling a connected message for "
79 + authenticatorType);
80 }
81 connection.scheduleCallbackConnectedMessage(callback);
82 } else {
83 if (Log.isLoggable(TAG, Log.VERBOSE)) {
84 Log.v(TAG, "the service is *not* connected, waiting for for "
85 + authenticatorType);
86 }
87 }
Fred Quintana60307342009-03-24 22:48:12 -070088 return true;
89 }
90
Fred Quintanaa698f422009-04-08 19:14:54 -070091 if (Log.isLoggable(TAG, Log.VERBOSE)) {
92 Log.v(TAG, "there is no service connection for " + authenticatorType);
93 }
94
Fred Quintana60307342009-03-24 22:48:12 -070095 // otherwise find the component name for the authenticator and initiate a bind
96 // if no authenticator or the bind fails then return false, otherwise return true
97 AccountAuthenticatorCache.AuthenticatorInfo authenticatorInfo =
98 mAuthenticatorCache.getAuthenticatorInfo(authenticatorType);
99 if (authenticatorInfo == null) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700100 if (Log.isLoggable(TAG, Log.VERBOSE)) {
101 Log.v(TAG, "there is no authenticator for " + authenticatorType
102 + ", bailing out");
103 }
Fred Quintana60307342009-03-24 22:48:12 -0700104 return false;
105 }
106
107 MyServiceConnection connection = new MyServiceConnection(authenticatorType);
108
109 Intent intent = new Intent();
110 intent.setAction("android.accounts.AccountAuthenticator");
111 intent.setComponent(authenticatorInfo.mComponentName);
Fred Quintanaa698f422009-04-08 19:14:54 -0700112 if (Log.isLoggable(TAG, Log.VERBOSE)) {
113 Log.v(TAG, "performing bindService to " + authenticatorInfo.mComponentName);
114 }
Fred Quintana60307342009-03-24 22:48:12 -0700115 if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700116 if (Log.isLoggable(TAG, Log.VERBOSE)) {
117 Log.v(TAG, "bindService to " + authenticatorInfo.mComponentName + " failed");
118 }
Fred Quintana60307342009-03-24 22:48:12 -0700119 return false;
120 }
121
122 mServiceConnections.put(authenticatorType, connection);
123 mServiceUsers.put(authenticatorType, Lists.newArrayList(callback));
124 return true;
125 }
126 }
127
128 public void unbind(Callback callbackToUnbind) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700129 if (Log.isLoggable(TAG, Log.VERBOSE)) {
130 Log.v(TAG, "unbinding callback " + callbackToUnbind);
131 }
Fred Quintana60307342009-03-24 22:48:12 -0700132 synchronized (mServiceConnections) {
133 for (Map.Entry<String, ArrayList<Callback>> entry : mServiceUsers.entrySet()) {
134 final String authenticatorType = entry.getKey();
135 final ArrayList<Callback> serviceUsers = entry.getValue();
136 for (Callback callback : serviceUsers) {
137 if (callback == callbackToUnbind) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700138 if (Log.isLoggable(TAG, Log.VERBOSE)) {
139 Log.v(TAG, "found callback in service" + authenticatorType);
140 }
Fred Quintana60307342009-03-24 22:48:12 -0700141 serviceUsers.remove(callbackToUnbind);
142 if (serviceUsers.isEmpty()) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700143 if (Log.isLoggable(TAG, Log.VERBOSE)) {
144 Log.v(TAG, "there are no more callbacks for service "
145 + authenticatorType + ", unbinding service");
146 }
Fred Quintana60307342009-03-24 22:48:12 -0700147 unbindFromService(authenticatorType);
Fred Quintanaa698f422009-04-08 19:14:54 -0700148 } else {
149 if (Log.isLoggable(TAG, Log.VERBOSE)) {
150 Log.v(TAG, "leaving service " + authenticatorType
151 + " around since there are still callbacks using it");
152 }
Fred Quintana60307342009-03-24 22:48:12 -0700153 }
154 return;
155 }
156 }
157 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700158 Log.e(TAG, "did not find callback " + callbackToUnbind + " in any of the services");
Fred Quintana60307342009-03-24 22:48:12 -0700159 }
160 }
161
162 private void unbindFromService(String authenticatorType) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700163 if (Log.isLoggable(TAG, Log.VERBOSE)) {
164 Log.v(TAG, "unbindService from " + authenticatorType);
165 }
Fred Quintana60307342009-03-24 22:48:12 -0700166 mContext.unbindService(mServiceConnections.get(authenticatorType));
167 mServiceUsers.remove(authenticatorType);
168 mServiceConnections.remove(authenticatorType);
169 }
170
171 private class ConnectedMessagePayload {
172 public final IBinder mService;
173 public final Callback mCallback;
174 public ConnectedMessagePayload(IBinder service, Callback callback) {
175 mService = service;
176 mCallback = callback;
177 }
178 }
179
180 private class MyServiceConnection implements ServiceConnection {
Fred Quintanaa698f422009-04-08 19:14:54 -0700181 private final String mAuthenticatorType;
182 private IBinder mService = null;
Fred Quintana60307342009-03-24 22:48:12 -0700183
184 public MyServiceConnection(String authenticatorType) {
185 mAuthenticatorType = authenticatorType;
186 }
187
188 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700189 if (Log.isLoggable(TAG, Log.VERBOSE)) {
190 Log.v(TAG, "onServiceConnected for account type " + mAuthenticatorType);
191 }
Fred Quintana60307342009-03-24 22:48:12 -0700192 // post a message for each service user to tell them that the service is connected
193 synchronized (mServiceConnections) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700194 mService = service;
Fred Quintana60307342009-03-24 22:48:12 -0700195 for (Callback callback : mServiceUsers.get(mAuthenticatorType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700196 if (Log.isLoggable(TAG, Log.VERBOSE)) {
197 Log.v(TAG, "the service became connected, scheduling a connected "
198 + "message for " + mAuthenticatorType);
199 }
200 scheduleCallbackConnectedMessage(callback);
Fred Quintana60307342009-03-24 22:48:12 -0700201 }
202 }
203 }
204
Fred Quintanaa698f422009-04-08 19:14:54 -0700205 private void scheduleCallbackConnectedMessage(Callback callback) {
206 final ConnectedMessagePayload payload =
207 new ConnectedMessagePayload(mService, callback);
208 mHandler.obtainMessage(mMessageWhatConnected, payload).sendToTarget();
209 }
210
Fred Quintana60307342009-03-24 22:48:12 -0700211 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700212 if (Log.isLoggable(TAG, Log.VERBOSE)) {
213 Log.v(TAG, "onServiceDisconnected for account type " + mAuthenticatorType);
214 }
Fred Quintana60307342009-03-24 22:48:12 -0700215 // post a message for each service user to tell them that the service is disconnected,
216 // and unbind from the service.
217 synchronized (mServiceConnections) {
218 for (Callback callback : mServiceUsers.get(mAuthenticatorType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700219 if (Log.isLoggable(TAG, Log.VERBOSE)) {
220 Log.v(TAG, "the service became disconnected, scheduling a "
221 + "disconnected message for "
222 + mAuthenticatorType);
223 }
Fred Quintana60307342009-03-24 22:48:12 -0700224 mHandler.obtainMessage(mMessageWhatDisconnected, callback).sendToTarget();
225 }
226 unbindFromService(mAuthenticatorType);
227 }
228 }
229 }
230
231 boolean handleMessage(Message message) {
232 if (message.what == mMessageWhatConnected) {
233 ConnectedMessagePayload payload = (ConnectedMessagePayload)message.obj;
Fred Quintanaa698f422009-04-08 19:14:54 -0700234 if (Log.isLoggable(TAG, Log.VERBOSE)) {
235 Log.v(TAG, "notifying callback " + payload.mCallback + " that it is connected");
236 }
Fred Quintana60307342009-03-24 22:48:12 -0700237 payload.mCallback.onConnected(payload.mService);
238 return true;
239 } else if (message.what == mMessageWhatDisconnected) {
240 Callback callback = (Callback)message.obj;
Fred Quintanaa698f422009-04-08 19:14:54 -0700241 if (Log.isLoggable(TAG, Log.VERBOSE)) {
242 Log.v(TAG, "notifying callback " + callback + " that it is disconnected");
243 }
Fred Quintana60307342009-03-24 22:48:12 -0700244 callback.onDisconnected();
245 return true;
246 } else {
247 return false;
248 }
249 }
250}