Loading core/java/android/accounts/AccountManagerService.java +45 −17 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.RegisteredServicesCache; import android.content.pm.PackageInfo; Loading Loading @@ -90,11 +92,8 @@ public class AccountManagerService // Messages that can be sent on mHandler private static final int MESSAGE_TIMED_OUT = 3; private static final int MESSAGE_CONNECTED = 7; private static final int MESSAGE_DISCONNECTED = 8; private final AccountAuthenticatorCache mAuthenticatorCache; private final AuthenticatorBindHelper mBindHelper; private final DatabaseHelper mOpenHelper; private final SimWatcher mSimWatcher; Loading Loading @@ -220,8 +219,6 @@ public class AccountManagerService mAuthenticatorCache = new AccountAuthenticatorCache(mContext); mAuthenticatorCache.setListener(this); mBindHelper = new AuthenticatorBindHelper(mContext, mAuthenticatorCache, mMessageHandler, MESSAGE_CONNECTED, MESSAGE_DISCONNECTED); if (SystemProperties.getBoolean("ro.config.sim_password_clear", false)) { mSimWatcher = new SimWatcher(mContext); Loading Loading @@ -1072,7 +1069,7 @@ public class AccountManagerService } private abstract class Session extends IAccountAuthenticatorResponse.Stub implements AuthenticatorBindHelper.Callback, IBinder.DeathRecipient { implements IBinder.DeathRecipient, ServiceConnection { IAccountManagerResponse mResponse; final String mAccountType; final boolean mExpectActivityLaunch; Loading Loading @@ -1154,7 +1151,7 @@ public class AccountManagerService if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "initiating bind to authenticator type " + mAccountType); } if (!mBindHelper.bind(mAccountType, this)) { if (!bindToAuthenticator(mAccountType)) { Log.d(TAG, "bind attempt failed for " + toDebugString()); onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); } Loading @@ -1163,7 +1160,7 @@ public class AccountManagerService private void unbind() { if (mAuthenticator != null) { mAuthenticator = null; mBindHelper.unbind(this); mContext.unbindService(this); } } Loading @@ -1176,7 +1173,7 @@ public class AccountManagerService mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this); } public void onConnected(IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) { mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); try { run(); Loading @@ -1186,9 +1183,7 @@ public class AccountManagerService } } public abstract void run() throws RemoteException; public void onDisconnected() { public void onServiceDisconnected(ComponentName name) { mAuthenticator = null; IAccountManagerResponse response = getResponseAndClose(); if (response != null) { Loading @@ -1197,6 +1192,8 @@ public class AccountManagerService } } public abstract void run() throws RemoteException; public void onTimedOut() { IAccountManagerResponse response = getResponseAndClose(); if (response != null) { Loading Loading @@ -1266,6 +1263,39 @@ public class AccountManagerService } } } /** * find the component name for the authenticator and initiate a bind * if no authenticator or the bind fails then return false, otherwise return true */ private boolean bindToAuthenticator(String authenticatorType) { AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo = mAuthenticatorCache.getServiceInfo( AuthenticatorDescription.newKey(authenticatorType)); if (authenticatorInfo == null) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "there is no authenticator for " + authenticatorType + ", bailing out"); } return false; } Intent intent = new Intent(); intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); intent.setComponent(authenticatorInfo.componentName); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); } if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); } return false; } return true; } } private class MessageHandler extends Handler { Loading @@ -1274,9 +1304,6 @@ public class AccountManagerService } public void handleMessage(Message msg) { if (mBindHelper.handleMessage(msg)) { return; } switch (msg.what) { case MESSAGE_TIMED_OUT: Session session = (Session)msg.obj; Loading Loading @@ -1571,16 +1598,17 @@ public class AccountManagerService private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) { final boolean fromAuthenticator = hasAuthenticatorUid(account.type, callerUid); final boolean hasExplicitGrants = hasExplicitlyGrantedPermission(account, authTokenType); final boolean inSystemImage = inSystemImage(callerUid); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " + callerUid + ", account " + account + ": is authenticator? " + fromAuthenticator + ", has explicit permission? " + hasExplicitGrants); } return fromAuthenticator || hasExplicitGrants || inSystemImage(callerUid); return fromAuthenticator || hasExplicitGrants || inSystemImage; } private boolean hasAuthenticatorUid(String accountType, int callingUid) { private boolean hasAuthenticatorcontextUid(String accountType, int callingUid) { for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : mAuthenticatorCache.getAllServices()) { if (serviceInfo.type.type.equals(accountType)) { Loading core/java/android/accounts/AuthenticatorBindHelper.javadeleted 100644 → 0 +0 −258 Original line number Diff line number Diff line /* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.accounts; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.util.Log; import java.util.ArrayList; import java.util.Map; import com.google.android.collect.Lists; import com.google.android.collect.Maps; /** * A helper object that simplifies binding to Account Authenticators. It uses the * {@link AccountAuthenticatorCache} to find the component name of the authenticators, * allowing the user to bind by account name. It also allows multiple, simultaneous binds * to the same authenticator, with each bind call guaranteed to return either * {@link Callback#onConnected} or {@link Callback#onDisconnected} if the bind() call * itself succeeds, even if the authenticator is already bound internally. * @hide */ public class AuthenticatorBindHelper { private static final String TAG = "Accounts"; private final Handler mHandler; private final Context mContext; private final int mMessageWhatConnected; private final int mMessageWhatDisconnected; private final Map<String, MyServiceConnection> mServiceConnections = Maps.newHashMap(); private final Map<String, ArrayList<Callback>> mServiceUsers = Maps.newHashMap(); private final AccountAuthenticatorCache mAuthenticatorCache; public AuthenticatorBindHelper(Context context, AccountAuthenticatorCache authenticatorCache, Handler handler, int messageWhatConnected, int messageWhatDisconnected) { mContext = context; mHandler = handler; mAuthenticatorCache = authenticatorCache; mMessageWhatConnected = messageWhatConnected; mMessageWhatDisconnected = messageWhatDisconnected; } public interface Callback { void onConnected(IBinder service); void onDisconnected(); } public boolean bind(String authenticatorType, Callback callback) { // if the authenticator is connecting or connected then return true synchronized (mServiceConnections) { if (mServiceConnections.containsKey(authenticatorType)) { MyServiceConnection connection = mServiceConnections.get(authenticatorType); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "service connection already exists for " + authenticatorType); } mServiceUsers.get(authenticatorType).add(callback); if (connection.mService != null) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "the service is connected, scheduling a connected message for " + authenticatorType); } connection.scheduleCallbackConnectedMessage(callback); } else { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "the service is *not* connected, waiting for for " + authenticatorType); } } return true; } if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "there is no service connection for " + authenticatorType); } // otherwise find the component name for the authenticator and initiate a bind // if no authenticator or the bind fails then return false, otherwise return true AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo = mAuthenticatorCache.getServiceInfo( AuthenticatorDescription.newKey(authenticatorType)); if (authenticatorInfo == null) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "there is no authenticator for " + authenticatorType + ", bailing out"); } return false; } MyServiceConnection connection = new MyServiceConnection(authenticatorType); Intent intent = new Intent(); intent.setAction("android.accounts.AccountAuthenticator"); intent.setComponent(authenticatorInfo.componentName); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); } if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); } return false; } mServiceConnections.put(authenticatorType, connection); mServiceUsers.put(authenticatorType, Lists.newArrayList(callback)); return true; } } public void unbind(Callback callbackToUnbind) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "unbinding callback " + callbackToUnbind); } synchronized (mServiceConnections) { for (Map.Entry<String, ArrayList<Callback>> entry : mServiceUsers.entrySet()) { final String authenticatorType = entry.getKey(); final ArrayList<Callback> serviceUsers = entry.getValue(); for (Callback callback : serviceUsers) { if (callback == callbackToUnbind) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "found callback in service" + authenticatorType); } serviceUsers.remove(callbackToUnbind); if (serviceUsers.isEmpty()) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "there are no more callbacks for service " + authenticatorType + ", unbinding service"); } unbindFromServiceLocked(authenticatorType); } else { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "leaving service " + authenticatorType + " around since there are still callbacks using it"); } } return; } } } Log.e(TAG, "did not find callback " + callbackToUnbind + " in any of the services"); } } /** * You must synchronized on mServiceConnections before calling this */ private void unbindFromServiceLocked(String authenticatorType) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "unbindService from " + authenticatorType); } mContext.unbindService(mServiceConnections.get(authenticatorType)); mServiceUsers.remove(authenticatorType); mServiceConnections.remove(authenticatorType); } private class ConnectedMessagePayload { public final IBinder mService; public final Callback mCallback; public ConnectedMessagePayload(IBinder service, Callback callback) { mService = service; mCallback = callback; } } private class MyServiceConnection implements ServiceConnection { private final String mAuthenticatorType; private IBinder mService = null; public MyServiceConnection(String authenticatorType) { mAuthenticatorType = authenticatorType; } public void onServiceConnected(ComponentName name, IBinder service) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "onServiceConnected for account type " + mAuthenticatorType); } // post a message for each service user to tell them that the service is connected synchronized (mServiceConnections) { mService = service; for (Callback callback : mServiceUsers.get(mAuthenticatorType)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "the service became connected, scheduling a connected " + "message for " + mAuthenticatorType); } scheduleCallbackConnectedMessage(callback); } } } private void scheduleCallbackConnectedMessage(Callback callback) { final ConnectedMessagePayload payload = new ConnectedMessagePayload(mService, callback); mHandler.obtainMessage(mMessageWhatConnected, payload).sendToTarget(); } public void onServiceDisconnected(ComponentName name) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "onServiceDisconnected for account type " + mAuthenticatorType); } // post a message for each service user to tell them that the service is disconnected, // and unbind from the service. synchronized (mServiceConnections) { final ArrayList<Callback> callbackList = mServiceUsers.get(mAuthenticatorType); if (callbackList != null) { for (Callback callback : callbackList) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "the service became disconnected, scheduling a " + "disconnected message for " + mAuthenticatorType); } mHandler.obtainMessage(mMessageWhatDisconnected, callback).sendToTarget(); } unbindFromServiceLocked(mAuthenticatorType); } } } } boolean handleMessage(Message message) { if (message.what == mMessageWhatConnected) { ConnectedMessagePayload payload = (ConnectedMessagePayload)message.obj; if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "notifying callback " + payload.mCallback + " that it is connected"); } payload.mCallback.onConnected(payload.mService); return true; } else if (message.what == mMessageWhatDisconnected) { Callback callback = (Callback)message.obj; if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "notifying callback " + callback + " that it is disconnected"); } callback.onDisconnected(); return true; } else { return false; } } } Loading
core/java/android/accounts/AccountManagerService.java +45 −17 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.RegisteredServicesCache; import android.content.pm.PackageInfo; Loading Loading @@ -90,11 +92,8 @@ public class AccountManagerService // Messages that can be sent on mHandler private static final int MESSAGE_TIMED_OUT = 3; private static final int MESSAGE_CONNECTED = 7; private static final int MESSAGE_DISCONNECTED = 8; private final AccountAuthenticatorCache mAuthenticatorCache; private final AuthenticatorBindHelper mBindHelper; private final DatabaseHelper mOpenHelper; private final SimWatcher mSimWatcher; Loading Loading @@ -220,8 +219,6 @@ public class AccountManagerService mAuthenticatorCache = new AccountAuthenticatorCache(mContext); mAuthenticatorCache.setListener(this); mBindHelper = new AuthenticatorBindHelper(mContext, mAuthenticatorCache, mMessageHandler, MESSAGE_CONNECTED, MESSAGE_DISCONNECTED); if (SystemProperties.getBoolean("ro.config.sim_password_clear", false)) { mSimWatcher = new SimWatcher(mContext); Loading Loading @@ -1072,7 +1069,7 @@ public class AccountManagerService } private abstract class Session extends IAccountAuthenticatorResponse.Stub implements AuthenticatorBindHelper.Callback, IBinder.DeathRecipient { implements IBinder.DeathRecipient, ServiceConnection { IAccountManagerResponse mResponse; final String mAccountType; final boolean mExpectActivityLaunch; Loading Loading @@ -1154,7 +1151,7 @@ public class AccountManagerService if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "initiating bind to authenticator type " + mAccountType); } if (!mBindHelper.bind(mAccountType, this)) { if (!bindToAuthenticator(mAccountType)) { Log.d(TAG, "bind attempt failed for " + toDebugString()); onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); } Loading @@ -1163,7 +1160,7 @@ public class AccountManagerService private void unbind() { if (mAuthenticator != null) { mAuthenticator = null; mBindHelper.unbind(this); mContext.unbindService(this); } } Loading @@ -1176,7 +1173,7 @@ public class AccountManagerService mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this); } public void onConnected(IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) { mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); try { run(); Loading @@ -1186,9 +1183,7 @@ public class AccountManagerService } } public abstract void run() throws RemoteException; public void onDisconnected() { public void onServiceDisconnected(ComponentName name) { mAuthenticator = null; IAccountManagerResponse response = getResponseAndClose(); if (response != null) { Loading @@ -1197,6 +1192,8 @@ public class AccountManagerService } } public abstract void run() throws RemoteException; public void onTimedOut() { IAccountManagerResponse response = getResponseAndClose(); if (response != null) { Loading Loading @@ -1266,6 +1263,39 @@ public class AccountManagerService } } } /** * find the component name for the authenticator and initiate a bind * if no authenticator or the bind fails then return false, otherwise return true */ private boolean bindToAuthenticator(String authenticatorType) { AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo = mAuthenticatorCache.getServiceInfo( AuthenticatorDescription.newKey(authenticatorType)); if (authenticatorInfo == null) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "there is no authenticator for " + authenticatorType + ", bailing out"); } return false; } Intent intent = new Intent(); intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); intent.setComponent(authenticatorInfo.componentName); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); } if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); } return false; } return true; } } private class MessageHandler extends Handler { Loading @@ -1274,9 +1304,6 @@ public class AccountManagerService } public void handleMessage(Message msg) { if (mBindHelper.handleMessage(msg)) { return; } switch (msg.what) { case MESSAGE_TIMED_OUT: Session session = (Session)msg.obj; Loading Loading @@ -1571,16 +1598,17 @@ public class AccountManagerService private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) { final boolean fromAuthenticator = hasAuthenticatorUid(account.type, callerUid); final boolean hasExplicitGrants = hasExplicitlyGrantedPermission(account, authTokenType); final boolean inSystemImage = inSystemImage(callerUid); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " + callerUid + ", account " + account + ": is authenticator? " + fromAuthenticator + ", has explicit permission? " + hasExplicitGrants); } return fromAuthenticator || hasExplicitGrants || inSystemImage(callerUid); return fromAuthenticator || hasExplicitGrants || inSystemImage; } private boolean hasAuthenticatorUid(String accountType, int callingUid) { private boolean hasAuthenticatorcontextUid(String accountType, int callingUid) { for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : mAuthenticatorCache.getAllServices()) { if (serviceInfo.type.type.equals(accountType)) { Loading
core/java/android/accounts/AuthenticatorBindHelper.javadeleted 100644 → 0 +0 −258 Original line number Diff line number Diff line /* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.accounts; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.util.Log; import java.util.ArrayList; import java.util.Map; import com.google.android.collect.Lists; import com.google.android.collect.Maps; /** * A helper object that simplifies binding to Account Authenticators. It uses the * {@link AccountAuthenticatorCache} to find the component name of the authenticators, * allowing the user to bind by account name. It also allows multiple, simultaneous binds * to the same authenticator, with each bind call guaranteed to return either * {@link Callback#onConnected} or {@link Callback#onDisconnected} if the bind() call * itself succeeds, even if the authenticator is already bound internally. * @hide */ public class AuthenticatorBindHelper { private static final String TAG = "Accounts"; private final Handler mHandler; private final Context mContext; private final int mMessageWhatConnected; private final int mMessageWhatDisconnected; private final Map<String, MyServiceConnection> mServiceConnections = Maps.newHashMap(); private final Map<String, ArrayList<Callback>> mServiceUsers = Maps.newHashMap(); private final AccountAuthenticatorCache mAuthenticatorCache; public AuthenticatorBindHelper(Context context, AccountAuthenticatorCache authenticatorCache, Handler handler, int messageWhatConnected, int messageWhatDisconnected) { mContext = context; mHandler = handler; mAuthenticatorCache = authenticatorCache; mMessageWhatConnected = messageWhatConnected; mMessageWhatDisconnected = messageWhatDisconnected; } public interface Callback { void onConnected(IBinder service); void onDisconnected(); } public boolean bind(String authenticatorType, Callback callback) { // if the authenticator is connecting or connected then return true synchronized (mServiceConnections) { if (mServiceConnections.containsKey(authenticatorType)) { MyServiceConnection connection = mServiceConnections.get(authenticatorType); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "service connection already exists for " + authenticatorType); } mServiceUsers.get(authenticatorType).add(callback); if (connection.mService != null) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "the service is connected, scheduling a connected message for " + authenticatorType); } connection.scheduleCallbackConnectedMessage(callback); } else { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "the service is *not* connected, waiting for for " + authenticatorType); } } return true; } if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "there is no service connection for " + authenticatorType); } // otherwise find the component name for the authenticator and initiate a bind // if no authenticator or the bind fails then return false, otherwise return true AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo = mAuthenticatorCache.getServiceInfo( AuthenticatorDescription.newKey(authenticatorType)); if (authenticatorInfo == null) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "there is no authenticator for " + authenticatorType + ", bailing out"); } return false; } MyServiceConnection connection = new MyServiceConnection(authenticatorType); Intent intent = new Intent(); intent.setAction("android.accounts.AccountAuthenticator"); intent.setComponent(authenticatorInfo.componentName); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); } if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); } return false; } mServiceConnections.put(authenticatorType, connection); mServiceUsers.put(authenticatorType, Lists.newArrayList(callback)); return true; } } public void unbind(Callback callbackToUnbind) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "unbinding callback " + callbackToUnbind); } synchronized (mServiceConnections) { for (Map.Entry<String, ArrayList<Callback>> entry : mServiceUsers.entrySet()) { final String authenticatorType = entry.getKey(); final ArrayList<Callback> serviceUsers = entry.getValue(); for (Callback callback : serviceUsers) { if (callback == callbackToUnbind) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "found callback in service" + authenticatorType); } serviceUsers.remove(callbackToUnbind); if (serviceUsers.isEmpty()) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "there are no more callbacks for service " + authenticatorType + ", unbinding service"); } unbindFromServiceLocked(authenticatorType); } else { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "leaving service " + authenticatorType + " around since there are still callbacks using it"); } } return; } } } Log.e(TAG, "did not find callback " + callbackToUnbind + " in any of the services"); } } /** * You must synchronized on mServiceConnections before calling this */ private void unbindFromServiceLocked(String authenticatorType) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "unbindService from " + authenticatorType); } mContext.unbindService(mServiceConnections.get(authenticatorType)); mServiceUsers.remove(authenticatorType); mServiceConnections.remove(authenticatorType); } private class ConnectedMessagePayload { public final IBinder mService; public final Callback mCallback; public ConnectedMessagePayload(IBinder service, Callback callback) { mService = service; mCallback = callback; } } private class MyServiceConnection implements ServiceConnection { private final String mAuthenticatorType; private IBinder mService = null; public MyServiceConnection(String authenticatorType) { mAuthenticatorType = authenticatorType; } public void onServiceConnected(ComponentName name, IBinder service) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "onServiceConnected for account type " + mAuthenticatorType); } // post a message for each service user to tell them that the service is connected synchronized (mServiceConnections) { mService = service; for (Callback callback : mServiceUsers.get(mAuthenticatorType)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "the service became connected, scheduling a connected " + "message for " + mAuthenticatorType); } scheduleCallbackConnectedMessage(callback); } } } private void scheduleCallbackConnectedMessage(Callback callback) { final ConnectedMessagePayload payload = new ConnectedMessagePayload(mService, callback); mHandler.obtainMessage(mMessageWhatConnected, payload).sendToTarget(); } public void onServiceDisconnected(ComponentName name) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "onServiceDisconnected for account type " + mAuthenticatorType); } // post a message for each service user to tell them that the service is disconnected, // and unbind from the service. synchronized (mServiceConnections) { final ArrayList<Callback> callbackList = mServiceUsers.get(mAuthenticatorType); if (callbackList != null) { for (Callback callback : callbackList) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "the service became disconnected, scheduling a " + "disconnected message for " + mAuthenticatorType); } mHandler.obtainMessage(mMessageWhatDisconnected, callback).sendToTarget(); } unbindFromServiceLocked(mAuthenticatorType); } } } } boolean handleMessage(Message message) { if (message.what == mMessageWhatConnected) { ConnectedMessagePayload payload = (ConnectedMessagePayload)message.obj; if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "notifying callback " + payload.mCallback + " that it is connected"); } payload.mCallback.onConnected(payload.mService); return true; } else if (message.what == mMessageWhatDisconnected) { Callback callback = (Callback)message.obj; if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "notifying callback " + callback + " that it is disconnected"); } callback.onDisconnected(); return true; } else { return false; } } }