163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon/*
263aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * Copyright (C) 2014 The Android Open Source Project
363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon *
463aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * Licensed under the Apache License, Version 2.0 (the "License");
563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * you may not use this file except in compliance with the License.
663aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * You may obtain a copy of the License at
763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon *
863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon *      http://www.apache.org/licenses/LICENSE-2.0
963aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon *
1063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * Unless required by applicable law or agreed to in writing, software
1163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * distributed under the License is distributed on an "AS IS" BASIS,
1263aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * See the License for the specific language governing permissions and
1463aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * limitations under the License.
1563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon */
1663aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
177cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnpackage com.android.server.telecom;
1863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
1963aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordonimport android.content.ComponentName;
2063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordonimport android.content.Context;
2163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordonimport android.content.Intent;
2263aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordonimport android.content.ServiceConnection;
2363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordonimport android.os.IBinder;
24105d977687d1d0de7cd9420fc140b01404261df1Evan Charltonimport android.os.UserHandle;
254bc0245eb5c09ab48dcbb673eefdf7175ae8ac9cSantos Cordonimport android.text.TextUtils;
264bc0245eb5c09ab48dcbb673eefdf7175ae8ac9cSantos Cordonimport android.util.ArraySet;
2763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
2891d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunnimport com.android.internal.util.Preconditions;
295c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon
30a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shraunerimport java.util.Collections;
315c12c6e00101d16d2db776839a027c62c109dea8Santos Cordonimport java.util.Set;
32a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shraunerimport java.util.concurrent.ConcurrentHashMap;
3363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
3463aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon/**
3563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * Abstract class to perform the work of binding and unbinding to the specified service interface.
3663aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * Subclasses supply the service intent and component name and this class will invoke protected
3763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon * methods when the class is bound, unbound, or upon failure.
3863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon */
3978a5e6b9c1595c81f72d7a822617cb78db224e48Ihab Awadabstract class ServiceBinder {
4063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
415c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    /**
425c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon     * Callback to notify after a binding succeeds or fails.
435c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon     */
445c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    interface BindCallback {
45c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon        void onSuccess();
46c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon        void onFailure();
47c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon    }
48c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon
49c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon    /**
50c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     * Listener for bind events on ServiceBinder.
51c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     */
5278a5e6b9c1595c81f72d7a822617cb78db224e48Ihab Awad    interface Listener<ServiceBinderClass extends ServiceBinder> {
5374d420be72fa30735fe9b7a25715f6db046c0398Santos Cordon        void onUnbind(ServiceBinderClass serviceBinder);
545c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    }
555c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon
566192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad    /**
576192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad     * Helper class to perform on-demand binding.
586192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad     */
598731faf3a1dcf888b894d4ad0faab97effab10bcIhab Awad    final class Binder2 {
606192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad        /**
616192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad         * Performs an asynchronous bind to the service (only if not already bound) and executes the
626192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad         * specified callback.
636192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad         *
646192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad         * @param callback The callback to notify of the binding's success or failure.
65165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon         * @param call The call for which we are being bound.
666192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad         */
67165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon        void bind(BindCallback callback, Call call) {
686192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            Log.d(ServiceBinder.this, "bind()");
696192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad
706192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            // Reset any abort request if we're asked to bind again.
716192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            clearAbort();
726192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad
736192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            if (!mCallbacks.isEmpty()) {
746192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad                // Binding already in progress, append to the list of callbacks and bail out.
756192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad                mCallbacks.add(callback);
766192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad                return;
776192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            }
786192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad
796192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            mCallbacks.add(callback);
806192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            if (mServiceConnection == null) {
816192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad                Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);
82165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon                ServiceConnection connection = new ServiceBinderConnection(call);
836192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad
84165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon                Log.event(call, Log.Events.BIND_CS, mComponentName);
858e6edbf6eede9221ad4ef30ffb7d6ff26553a329Santos Cordon                final int bindingFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
868e6edbf6eede9221ad4ef30ffb7d6ff26553a329Santos Cordon                final boolean isBound;
87105d977687d1d0de7cd9420fc140b01404261df1Evan Charlton                if (mUserHandle != null) {
888e6edbf6eede9221ad4ef30ffb7d6ff26553a329Santos Cordon                    isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags,
898e6edbf6eede9221ad4ef30ffb7d6ff26553a329Santos Cordon                            mUserHandle);
90105d977687d1d0de7cd9420fc140b01404261df1Evan Charlton                } else {
918e6edbf6eede9221ad4ef30ffb7d6ff26553a329Santos Cordon                    isBound = mContext.bindService(serviceIntent, connection, bindingFlags);
92105d977687d1d0de7cd9420fc140b01404261df1Evan Charlton                }
938e6edbf6eede9221ad4ef30ffb7d6ff26553a329Santos Cordon                if (!isBound) {
946192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad                    handleFailedConnection();
956192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad                    return;
966192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad                }
976192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            } else {
986192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad                Log.d(ServiceBinder.this, "Service is already bound.");
996192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad                Preconditions.checkNotNull(mBinder);
1006192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad                handleSuccessfulConnection();
1016192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            }
1026192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad        }
1036192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad    }
1046192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad
10563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    private final class ServiceBinderConnection implements ServiceConnection {
106165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon        /**
107165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon         * The initial call for which the service was bound.
108165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon         */
109165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon        private Call mCall;
110165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon
111165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon        ServiceBinderConnection(Call call) {
112165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon            mCall = call;
113165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon        }
114165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon
11563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        @Override
11663aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        public void onServiceConnected(ComponentName componentName, IBinder binder) {
1178d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad            synchronized (mLock) {
1188d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                Log.i(this, "Service bound %s", componentName);
1198d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad
120165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon                Log.event(mCall, Log.Events.CS_BOUND, componentName);
121165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon                mCall = null;
122165c1ced107a1c3b9b359757d41438f9ca585e78Santos Cordon
1238d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                // Unbind request was queued so unbind immediately.
1248d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                if (mIsBindingAborted) {
1258d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                    clearAbort();
1268d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                    logServiceDisconnected("onServiceConnected");
1278d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                    mContext.unbindService(this);
1288d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                    handleFailedConnection();
1298d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                    return;
1308d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                }
13163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
1328d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                mServiceConnection = this;
1338d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                setBinder(binder);
1348d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                handleSuccessfulConnection();
13563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon            }
13663aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        }
13763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
13863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        @Override
13963aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        public void onServiceDisconnected(ComponentName componentName) {
1408d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad            synchronized (mLock) {
1418d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                logServiceDisconnected("onServiceDisconnected");
142c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon
1438d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                mServiceConnection = null;
1448d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                clearAbort();
14563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
1468d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                handleServiceDisconnected();
1478d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad            }
14863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        }
14963aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    }
15063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
15163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /** The application context. */
15263aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    private final Context mContext;
15363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
1548d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad    /** The Telecom lock object. */
1558d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad    protected final TelecomSystem.SyncRoot mLock;
1568d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad
15763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /** The intent action to use when binding through {@link Context#bindService}. */
15863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    private final String mServiceAction;
15963aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
16063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /** The component name of the service to bind to. */
16163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    private final ComponentName mComponentName;
16263aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
1635c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    /** The set of callbacks waiting for notification of the binding's success or failure. */
1644bc0245eb5c09ab48dcbb673eefdf7175ae8ac9cSantos Cordon    private final Set<BindCallback> mCallbacks = new ArraySet<>();
1655c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon
16663aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /** Used to bind and unbind from the service. */
16763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    private ServiceConnection mServiceConnection;
16863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
169105d977687d1d0de7cd9420fc140b01404261df1Evan Charlton    /** {@link UserHandle} to use for binding, to support work profiles and multi-user. */
170105d977687d1d0de7cd9420fc140b01404261df1Evan Charlton    private UserHandle mUserHandle;
171105d977687d1d0de7cd9420fc140b01404261df1Evan Charlton
17263aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /** The binder provided by {@link ServiceConnection#onServiceConnected} */
17363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    private IBinder mBinder;
17463aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
1758e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad    private int mAssociatedCallCount = 0;
1768e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad
17763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /**
17863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     * Indicates that an unbind request was made when the service was not yet bound. If the service
17963aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     * successfully connects when this is true, it should be unbound immediately.
18063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     */
18163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    private boolean mIsBindingAborted;
18263aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
18363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /**
184c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     * Set of currently registered listeners.
185a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
186a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner     * load factor before resizing, 1 means we only expect a single thread to
187a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner     * access the map so make only a single shard
188c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     */
189a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner    private final Set<Listener> mListeners = Collections.newSetFromMap(
190a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner            new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
191c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon
192c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon    /**
19363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     * Persists the specified parameters and initializes the new instance.
19463aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     *
19563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     * @param serviceAction The intent-action used with {@link Context#bindService}.
19663aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     * @param componentName The component name of the service with which to bind.
19791d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn     * @param context The context.
198105d977687d1d0de7cd9420fc140b01404261df1Evan Charlton     * @param userHandle The {@link UserHandle} to use for binding.
19963aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     */
200105d977687d1d0de7cd9420fc140b01404261df1Evan Charlton    protected ServiceBinder(String serviceAction, ComponentName componentName, Context context,
2018d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad            TelecomSystem.SyncRoot lock, UserHandle userHandle) {
2024bc0245eb5c09ab48dcbb673eefdf7175ae8ac9cSantos Cordon        Preconditions.checkState(!TextUtils.isEmpty(serviceAction));
20363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        Preconditions.checkNotNull(componentName);
20463aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
20591d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn        mContext = context;
2068d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad        mLock = lock;
20763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        mServiceAction = serviceAction;
20863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        mComponentName = componentName;
209105d977687d1d0de7cd9420fc140b01404261df1Evan Charlton        mUserHandle = userHandle;
21063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    }
21163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
2128e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad    final void incrementAssociatedCallCount() {
2138e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad        mAssociatedCallCount++;
214c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon        Log.v(this, "Call count increment %d, %s", mAssociatedCallCount,
215c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon                mComponentName.flattenToShortString());
2168e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad    }
2178e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad
2188e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad    final void decrementAssociatedCallCount() {
2198e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad        if (mAssociatedCallCount > 0) {
2208e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad            mAssociatedCallCount--;
221c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon            Log.v(this, "Call count decrement %d, %s", mAssociatedCallCount,
222c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon                    mComponentName.flattenToShortString());
223c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon
224c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon            if (mAssociatedCallCount == 0) {
225c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon                unbind();
226c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon            }
2278e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad        } else {
228f1c191d3974fed3f57680c63571ae0212c4622e7Sailesh Nepal            Log.wtf(this, "%s: ignoring a request to decrement mAssociatedCallCount below zero",
229f1c191d3974fed3f57680c63571ae0212c4622e7Sailesh Nepal                    mComponentName.getClassName());
2308e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad        }
2318e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad    }
2328e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad
2338e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad    final int getAssociatedCallCount() {
2348e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad        return mAssociatedCallCount;
2358e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad    }
2368e55d1d7ea86f768acb90f88dc9e5b5368d3398aBen Gilad
23763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /**
23863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     * Unbinds from the service if already bound, no-op otherwise.
23963aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     */
24063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    final void unbind() {
24163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        if (mServiceConnection == null) {
24263aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon            // We're not yet bound, so queue up an abort request.
24363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon            mIsBindingAborted = true;
24463aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        } else {
245c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon            logServiceDisconnected("unbind");
24663aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon            mContext.unbindService(mServiceConnection);
24763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon            mServiceConnection = null;
2486192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            setBinder(null);
24963aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        }
25063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    }
25163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
252bb167cdd0dffa8103ba051b0e115dcaecf2b649dBen Gilad    final ComponentName getComponentName() {
25363aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        return mComponentName;
25463aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    }
25563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
2566192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad    final boolean isServiceValid(String actionName) {
2576192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad        if (mBinder == null) {
258682fe6ba2fe99e209d72a051539697a755b994c0Santos Cordon            Log.w(this, "%s invoked while service is unbound", actionName);
2596192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad            return false;
2606192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad        }
2616192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad
2626192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad        return true;
2636192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad    }
2646192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad
265c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon    final void addListener(Listener listener) {
266c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon        mListeners.add(listener);
267c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon    }
268c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon
269c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon    final void removeListener(Listener listener) {
270a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner        if (listener != null) {
271a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner            mListeners.remove(listener);
272a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner        }
273c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon    }
274c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon
275c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon    /**
276c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     * Logs a standard message upon service disconnection. This method exists because there is no
277c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     * single method called whenever the service unbinds and we want to log the same string in all
278c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     * instances where that occurs.  (Context.unbindService() does not cause onServiceDisconnected
279c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     * to execute).
280c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     *
281c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     * @param sourceTag Tag to disambiguate
282c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon     */
283c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon    private void logServiceDisconnected(String sourceTag) {
284c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon        Log.i(this, "Service unbound %s, from %s.", mComponentName, sourceTag);
285c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon    }
286c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon
28763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /**
2885c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon     * Notifies all the outstanding callbacks that the service is successfully bound. The list of
2895c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon     * outstanding callbacks is cleared afterwards.
29063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     */
291b6141aec99bcf399faea1bfc9f48503492c4fb2dSailesh Nepal    private void handleSuccessfulConnection() {
2925c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon        for (BindCallback callback : mCallbacks) {
2935c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon            callback.onSuccess();
2945c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon        }
2955c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon        mCallbacks.clear();
2965c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    }
29763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
29863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /**
2995c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon     * Notifies all the outstanding callbacks that the service failed to bind. The list of
3005c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon     * outstanding callbacks is cleared afterwards.
30163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     */
3025c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    private void handleFailedConnection() {
3035c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon        for (BindCallback callback : mCallbacks) {
3045c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon            callback.onFailure();
3055c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon        }
3065c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon        mCallbacks.clear();
3075c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    }
30863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
30963aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    /**
31063aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     * Handles a service disconnection.
31163aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon     */
3125c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    private void handleServiceDisconnected() {
3136192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad        setBinder(null);
3145c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    }
31563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon
31663aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    private void clearAbort() {
31763aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon        mIsBindingAborted = false;
31863aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon    }
3195c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon
3205c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    /**
3216192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad     * Sets the (private) binder and updates the child class.
3226192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad     *
3236192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad     * @param binder The new binder value.
3246192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad     */
3256192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad    private void setBinder(IBinder binder) {
326c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon        if (mBinder != binder) {
327c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon            mBinder = binder;
328c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon
329c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon            setServiceInterface(binder);
330c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon
331c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon            if (binder == null) {
332a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner                for (Listener l : mListeners) {
333c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon                    l.onUnbind(this);
334c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon                }
335c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon            }
336c499c1c0758dcb4b02048df96e7405994660ab3fSantos Cordon        }
3376192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad    }
3386192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad
3396192561b1f56d7c4e6c650e178e07ba61ad02667Ben Gilad    /**
3405c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon     * Sets the service interface after the service is bound or unbound.
3415c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon     *
3425c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon     * @param binder The actual bound service implementation.
3435c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon     */
3445c12c6e00101d16d2db776839a027c62c109dea8Santos Cordon    protected abstract void setServiceInterface(IBinder binder);
34563aeb16a14a94dd44345f6200c7c002e780a15ffSantos Cordon}
346