Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 70d71fdc authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change Ib839afc3 into eclair-mr2

* changes:
  remove the bind helper and bind directly
parents d753f070 b839afc3
Loading
Loading
Loading
Loading
+45 −17
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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);
@@ -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;
@@ -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");
            }
@@ -1163,7 +1160,7 @@ public class AccountManagerService
        private void unbind() {
            if (mAuthenticator != null) {
                mAuthenticator = null;
                mBindHelper.unbind(this);
                mContext.unbindService(this);
            }
        }

@@ -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();
@@ -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) {
@@ -1197,6 +1192,8 @@ public class AccountManagerService
            }
        }

        public abstract void run() throws RemoteException;

        public void onTimedOut() {
            IAccountManagerResponse response = getResponseAndClose();
            if (response != null) {
@@ -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 {
@@ -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;
@@ -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)) {
+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;
        }
    }
}