1fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown/*
2fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Copyright (C) 2013 The Android Open Source Project
3fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown *
4fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
5fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * you may not use this file except in compliance with the License.
6fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * You may obtain a copy of the License at
7fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown *
8fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
9fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown *
10fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Unless required by applicable law or agreed to in writing, software
11fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
12fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * See the License for the specific language governing permissions and
14fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * limitations under the License.
15fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */
16fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.mediarouter.media;
18fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_ID;
20ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_LIBRARY_GROUP;
21ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_UNSELECT_REASON;
22ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_VOLUME;
23ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol
248e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas        .CLIENT_MSG_CREATE_ROUTE_CONTROLLER;
25ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_REGISTER;
26ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol
278e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas        .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER;
28f2d1695bf23114f2a5d6bfc9e092a4bfcb5b2701Aurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol
29f2d1695bf23114f2a5d6bfc9e092a4bfcb5b2701Aurimas Liutikas        .CLIENT_MSG_ROUTE_CONTROL_REQUEST;
30ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_SELECT_ROUTE;
31f2d1695bf23114f2a5d6bfc9e092a4bfcb5b2701Aurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol
32f2d1695bf23114f2a5d6bfc9e092a4bfcb5b2701Aurimas Liutikas        .CLIENT_MSG_SET_DISCOVERY_REQUEST;
33ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_SET_ROUTE_VOLUME;
34ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_UNREGISTER;
35ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_UNSELECT_ROUTE;
36ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_UPDATE_ROUTE_VOLUME;
37ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_VERSION_CURRENT;
38ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.SERVICE_DATA_ERROR;
39ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol
408e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas        .SERVICE_MSG_CONTROL_REQUEST_FAILED;
41ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol
428e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas        .SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED;
43ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.SERVICE_MSG_DESCRIPTOR_CHANGED;
44ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.SERVICE_MSG_GENERIC_FAILURE;
45ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.SERVICE_MSG_GENERIC_SUCCESS;
46ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.SERVICE_MSG_REGISTERED;
47ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.SERVICE_VERSION_1;
48ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.mediarouter.media.MediaRouteProviderProtocol.isValidRemoteMessenger;
498e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas
50fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.ComponentName;
51fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.Context;
52fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.Intent;
53fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.ServiceConnection;
54fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.Bundle;
55fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.DeadObjectException;
56fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.Handler;
57fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.IBinder;
58fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.IBinder.DeathRecipient;
59fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.Message;
60fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.Messenger;
618e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport android.os.RemoteException;
62fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.util.Log;
63fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.util.SparseArray;
64fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
65f2d1695bf23114f2a5d6bfc9e092a4bfcb5b2701Aurimas Liutikasimport androidx.annotation.NonNull;
66f2d1695bf23114f2a5d6bfc9e092a4bfcb5b2701Aurimas Liutikasimport androidx.mediarouter.media.MediaRouter.ControlRequestCallback;
67f2d1695bf23114f2a5d6bfc9e092a4bfcb5b2701Aurimas Liutikas
68fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport java.lang.ref.WeakReference;
69fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport java.util.ArrayList;
7011417b1cfde8f1749905f2d735623af9214148afJeff Brownimport java.util.List;
71fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
72fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown/**
73fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Maintains a connection to a particular media route provider service.
74fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */
75fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownfinal class RegisteredMediaRouteProvider extends MediaRouteProvider
76fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        implements ServiceConnection {
77e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas    static final String TAG = "MediaRouteProviderProxy";  // max. 23 chars
78e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
79fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
80fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final ComponentName mComponentName;
81e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas    final PrivateHandler mPrivateHandler;
82fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final ArrayList<Controller> mControllers = new ArrayList<Controller>();
83fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
84f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private boolean mStarted;
85fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private boolean mBound;
86fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private Connection mActiveConnection;
87fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private boolean mConnectionReady;
88fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
89fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public RegisteredMediaRouteProvider(Context context, ComponentName componentName) {
90adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown        super(context, new ProviderMetadata(componentName));
91fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
92fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mComponentName = componentName;
93fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mPrivateHandler = new PrivateHandler();
94fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
95fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
96fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    @Override
972f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim    public RouteController onCreateRouteController(@NonNull String routeId) {
982f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        if (routeId == null) {
992f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim            throw new IllegalArgumentException("routeId cannot be null");
100fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
1012f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        return createRouteController(routeId, null);
1022f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim    }
1032f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim
1042f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim    @Override
1052f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim    public RouteController onCreateRouteController(
1062f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim            @NonNull String routeId, @NonNull String routeGroupId) {
1072f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        if (routeId == null) {
1082f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim            throw new IllegalArgumentException("routeId cannot be null");
1092f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        }
1102f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        if (routeGroupId == null) {
1112f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim            throw new IllegalArgumentException("routeGroupId cannot be null");
1122f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        }
1132f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        return createRouteController(routeId, routeGroupId);
114fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
115fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
11628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    @Override
11711417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
11828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        if (mConnectionReady) {
11911417b1cfde8f1749905f2d735623af9214148afJeff Brown            mActiveConnection.setDiscoveryRequest(request);
12028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
121f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        updateBinding();
12228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
12328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
1242a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim    @Override
1252a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim    public void onServiceConnected(ComponentName name, IBinder service) {
1262a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim        if (DEBUG) {
1272a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim            Log.d(TAG, this + ": Connected");
1282a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim        }
1292a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim
1302a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim        if (mBound) {
1312a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim            disconnect();
1322a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim
1332a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim            Messenger messenger = (service != null ? new Messenger(service) : null);
1342a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim            if (isValidRemoteMessenger(messenger)) {
1352a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim                Connection connection = new Connection(messenger);
1362a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim                if (connection.register()) {
1372a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim                    mActiveConnection = connection;
1382a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim                } else {
1392a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim                    if (DEBUG) {
1402a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim                        Log.d(TAG, this + ": Registration failed");
1412a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim                    }
1422a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim                }
1432a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim            } else {
1442a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim                Log.e(TAG, this + ": Service returned invalid messenger binder");
1452a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim            }
1462a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim        }
1472a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim    }
1482a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim
1492a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim    @Override
1502a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim    public void onServiceDisconnected(ComponentName name) {
1512a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim        if (DEBUG) {
1522a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim            Log.d(TAG, this + ": Service disconnected");
1532a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim        }
1542a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim        disconnect();
1552a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim    }
1562a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim
1572a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim    @Override
1582a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim    public String toString() {
1592a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim        return "Service connection " + mComponentName.flattenToShortString();
1602a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim    }
1612a67dc6d2e1c788bd14e4a9bd685ee4d924de8bbSungsoo Lim
162fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public boolean hasComponentName(String packageName, String className) {
163fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return mComponentName.getPackageName().equals(packageName)
164fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                && mComponentName.getClassName().equals(className);
165fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
166fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
167f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    public void start() {
168f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (!mStarted) {
169fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
170f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                Log.d(TAG, this + ": Starting");
171fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
172f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
173f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            mStarted = true;
174f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            updateBinding();
175fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
176fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
177fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
178f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    public void stop() {
179f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (mStarted) {
180f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (DEBUG) {
181f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                Log.d(TAG, this + ": Stopping");
182f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
183fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
184f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            mStarted = false;
185f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            updateBinding();
186fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
187fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
188fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
189fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void rebindIfDisconnected() {
190f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (mActiveConnection == null && shouldBind()) {
191fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            unbind();
192fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            bind();
193fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
194fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
195fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
196f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private void updateBinding() {
197f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (shouldBind()) {
198f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            bind();
199f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        } else {
200f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            unbind();
201f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        }
202f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    }
203f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
204f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private boolean shouldBind() {
205f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (mStarted) {
206f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            // Bind whenever there is a discovery request.
207f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (getDiscoveryRequest() != null) {
208f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                return true;
209f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
210f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
211f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            // Bind whenever the application has an active route controller.
212f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            // This means that one of this provider's routes is selected.
213f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (!mControllers.isEmpty()) {
214f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                return true;
215f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
216f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        }
217f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        return false;
218f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    }
219f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
220f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private void bind() {
221f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (!mBound) {
222f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (DEBUG) {
223f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                Log.d(TAG, this + ": Binding");
224f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
225f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
2263efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            Intent service = new Intent(MediaRouteProviderProtocol.SERVICE_INTERFACE);
227f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            service.setComponent(mComponentName);
228f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            try {
229f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                mBound = getContext().bindService(service, this, Context.BIND_AUTO_CREATE);
230f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                if (!mBound && DEBUG) {
231f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    Log.d(TAG, this + ": Bind failed");
232f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                }
233f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            } catch (SecurityException ex) {
234f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                if (DEBUG) {
235f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    Log.d(TAG, this + ": Bind failed", ex);
236f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                }
237f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
238f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        }
239f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    }
240f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
241f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private void unbind() {
242f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (mBound) {
243f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (DEBUG) {
244f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                Log.d(TAG, this + ": Unbinding");
245f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
246f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
247f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            mBound = false;
248f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            disconnect();
249f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            getContext().unbindService(this);
250f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        }
251f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    }
252f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
2532f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim    private RouteController createRouteController(String routeId, String routeGroupId) {
2542f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        MediaRouteProviderDescriptor descriptor = getDescriptor();
2552f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        if (descriptor != null) {
2562f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim            List<MediaRouteDescriptor> routes = descriptor.getRoutes();
2572f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim            final int count = routes.size();
2582f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim            for (int i = 0; i < count; i++) {
2592f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim                final MediaRouteDescriptor route = routes.get(i);
2602f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim                if (route.getId().equals(routeId)) {
2612f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim                    Controller controller = new Controller(routeId, routeGroupId);
2622f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim                    mControllers.add(controller);
2632f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim                    if (mConnectionReady) {
2642f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim                        controller.attachConnection(mActiveConnection);
2652f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim                    }
2662f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim                    updateBinding();
2672f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim                    return controller;
2682f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim                }
2692f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim            }
2702f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        }
2712f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        return null;
2722f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim    }
2732f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim
274e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas    void onConnectionReady(Connection connection) {
275fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
276fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionReady = true;
277fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            attachControllersToConnection();
27811417b1cfde8f1749905f2d735623af9214148afJeff Brown
27911417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteDiscoveryRequest request = getDiscoveryRequest();
28011417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (request != null) {
28111417b1cfde8f1749905f2d735623af9214148afJeff Brown                mActiveConnection.setDiscoveryRequest(request);
28228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            }
283fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
284fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
285fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
286e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas    void onConnectionDied(Connection connection) {
287fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
288fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
289fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Service connection died");
290fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
291fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            disconnect();
292fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
293fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
294fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
295e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas    void onConnectionError(Connection connection, String error) {
296fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
297fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
298fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Service connection error - " + error);
299fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
300fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            unbind();
301fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
302fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
303fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
304e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas    void onConnectionDescriptorChanged(Connection connection,
30511417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteProviderDescriptor descriptor) {
306fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
307fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
308fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Descriptor changed, descriptor=" + descriptor);
309fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
310fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            setDescriptor(descriptor);
311fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
312fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
313fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
314fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void disconnect() {
315fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection != null) {
316fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            setDescriptor(null);
317fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionReady = false;
318fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            detachControllersFromConnection();
319fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mActiveConnection.dispose();
320fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mActiveConnection = null;
321fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
322fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
323fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
324e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas    void onControllerReleased(Controller controller) {
325fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mControllers.remove(controller);
326fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        controller.detachConnection();
327f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        updateBinding();
328fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
329fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
330fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void attachControllersToConnection() {
331fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int count = mControllers.size();
332fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        for (int i = 0; i < count; i++) {
333fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mControllers.get(i).attachConnection(mActiveConnection);
334fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
335fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
336fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
337fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void detachControllersFromConnection() {
338fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int count = mControllers.size();
339fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        for (int i = 0; i < count; i++) {
340fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mControllers.get(i).detachConnection();
341fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
342fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
343fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
344fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final class Controller extends RouteController {
345fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final String mRouteId;
3462f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        private final String mRouteGroupId;
347fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
348fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean mSelected;
349fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mPendingSetVolume = -1;
350fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mPendingUpdateVolumeDelta;
351fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
352fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private Connection mConnection;
353fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mControllerId;
354fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
3552f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        public Controller(String routeId, String routeGroupId) {
356fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRouteId = routeId;
3572f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim            mRouteGroupId = routeGroupId;
358fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
359fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
360fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void attachConnection(Connection connection) {
361fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnection = connection;
3622f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim            mControllerId = connection.createRouteController(mRouteId, mRouteGroupId);
363fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mSelected) {
364fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                connection.selectRoute(mControllerId);
365fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mPendingSetVolume >= 0) {
366fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    connection.setVolume(mControllerId, mPendingSetVolume);
367fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingSetVolume = -1;
368fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
369fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mPendingUpdateVolumeDelta != 0) {
370fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    connection.updateVolume(mControllerId, mPendingUpdateVolumeDelta);
371fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingUpdateVolumeDelta = 0;
372fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
373fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
374fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
375fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
376fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void detachConnection() {
377fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
378fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.releaseRouteController(mControllerId);
379fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection = null;
380fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mControllerId = 0;
381fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
382fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
383fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
384fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
385129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onRelease() {
386fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            onControllerReleased(this);
387fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
388fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
389fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
390129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onSelect() {
391fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mSelected = true;
392fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
393fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.selectRoute(mControllerId);
394fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
395fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
396fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
398129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onUnselect() {
39994be6100218126ce6a08bf1f56209578500b361fRoboErik            onUnselect(MediaRouter.UNSELECT_REASON_UNKNOWN);
40094be6100218126ce6a08bf1f56209578500b361fRoboErik        }
40194be6100218126ce6a08bf1f56209578500b361fRoboErik
40294be6100218126ce6a08bf1f56209578500b361fRoboErik        @Override
40394be6100218126ce6a08bf1f56209578500b361fRoboErik        public void onUnselect(int reason) {
404fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mSelected = false;
405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
40694be6100218126ce6a08bf1f56209578500b361fRoboErik                mConnection.unselectRoute(mControllerId, reason);
407fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
408fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
409fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
410fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
411129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onSetVolume(int volume) {
412fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
413fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.setVolume(mControllerId, volume);
414fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } else {
415fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingSetVolume = volume;
416fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingUpdateVolumeDelta = 0;
417fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
418fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
419fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
420fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
421129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onUpdateVolume(int delta) {
422fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
423fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.updateVolume(mControllerId, delta);
424fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } else {
425fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingUpdateVolumeDelta += delta;
426fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
427fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
428fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
429fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
430129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
431fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
432fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mConnection.sendControlRequest(mControllerId, intent, callback);
433fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
434fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
436fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
437fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
438fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final class Connection implements DeathRecipient {
439fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final Messenger mServiceMessenger;
440fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ReceiveHandler mReceiveHandler;
441fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final Messenger mReceiveMessenger;
442fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
443fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mNextRequestId = 1;
444fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mNextControllerId = 1;
445fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mServiceVersion; // non-zero when registration complete
446fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
447fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mPendingRegisterRequestId;
448fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final SparseArray<ControlRequestCallback> mPendingCallbacks =
449fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                new SparseArray<ControlRequestCallback>();
450fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
451fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public Connection(Messenger serviceMessenger) {
452fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mServiceMessenger = serviceMessenger;
453fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mReceiveHandler = new ReceiveHandler(this);
454fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mReceiveMessenger = new Messenger(mReceiveHandler);
455fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
456fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
457fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean register() {
458fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPendingRegisterRequestId = mNextRequestId++;
4593efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            if (!sendRequest(CLIENT_MSG_REGISTER,
460fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingRegisterRequestId,
4613efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                    CLIENT_VERSION_CURRENT, null, null)) {
462fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return false;
463fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
464fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
465fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
466fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mServiceMessenger.getBinder().linkToDeath(this, 0);
467fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
468fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (RemoteException ex) {
469fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                binderDied();
470fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
471fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
472fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
473fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
474fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void dispose() {
4753efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_UNREGISTER, 0, 0, null, null);
476fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mReceiveHandler.dispose();
477fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mServiceMessenger.getBinder().unlinkToDeath(this, 0);
478fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
479fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPrivateHandler.post(new Runnable() {
480fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                @Override
481fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                public void run() {
482fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    failPendingCallbacks();
483fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
484fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            });
485fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
486fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
487e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas        void failPendingCallbacks() {
488fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = 0;
489fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < mPendingCallbacks.size(); i++) {
49037da34e71d13376b3542b28d19c7356c622d6747Jeff Brown                mPendingCallbacks.valueAt(i).onError(null, null);
491fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
492fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPendingCallbacks.clear();
493fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
494fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
495fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onGenericFailure(int requestId) {
496fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (requestId == mPendingRegisterRequestId) {
497fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingRegisterRequestId = 0;
498c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas                onConnectionError(this, "Registration failed");
499fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
500fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
501fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
502fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingCallbacks.remove(requestId);
5033d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                callback.onError(null, null);
504fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
505fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return true;
506fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
507fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
508fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onGenericSuccess(int requestId) {
509fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return true;
510fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
511fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
512fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onRegistered(int requestId, int serviceVersion,
513fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Bundle descriptorBundle) {
514fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mServiceVersion == 0
515fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    && requestId == mPendingRegisterRequestId
5163efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                    && serviceVersion >= SERVICE_VERSION_1) {
517fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingRegisterRequestId = 0;
518fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mServiceVersion = serviceVersion;
519fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionDescriptorChanged(this,
52011417b1cfde8f1749905f2d735623af9214148afJeff Brown                        MediaRouteProviderDescriptor.fromBundle(descriptorBundle));
521fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionReady(this);
522fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
523fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
524fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
525fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
526fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
527fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onDescriptorChanged(Bundle descriptorBundle) {
528fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mServiceVersion != 0) {
529fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionDescriptorChanged(this,
53011417b1cfde8f1749905f2d735623af9214148afJeff Brown                        MediaRouteProviderDescriptor.fromBundle(descriptorBundle));
531fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
532fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
533fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
534fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
535fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
5363d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        public boolean onControlRequestSucceeded(int requestId, Bundle data) {
537fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
538fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
539fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingCallbacks.remove(requestId);
5403d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                callback.onResult(data);
5413d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                return true;
5423d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown            }
5433d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown            return false;
5443d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        }
5453d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown
5463d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        public boolean onControlRequestFailed(int requestId, String error, Bundle data) {
5473d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
5483d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown            if (callback != null) {
5493d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                mPendingCallbacks.remove(requestId);
5503d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                callback.onError(error, data);
551fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
552fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
553fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
554fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
555fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
556fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
557fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void binderDied() {
558fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPrivateHandler.post(new Runnable() {
559fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                @Override
560fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                public void run() {
561fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    onConnectionDied(Connection.this);
562fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
563fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            });
564fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
565fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
5662f829125aef3796ca674d0ca5fccf9bd37b8417bSungsoo Lim        public int createRouteController(String routeId, String routeGroupId) {
567fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int controllerId = mNextControllerId++;
568fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Bundle data = new Bundle();
5693efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            data.putString(CLIENT_DATA_ROUTE_ID, routeId);
5708e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas            data.putString(CLIENT_DATA_ROUTE_LIBRARY_GROUP, routeGroupId);
5713efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_CREATE_ROUTE_CONTROLLER,
572fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, data);
573fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return controllerId;
574fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
575fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
576fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void releaseRouteController(int controllerId) {
5773efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_RELEASE_ROUTE_CONTROLLER,
578fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, null);
579fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
580fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
581fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void selectRoute(int controllerId) {
5823efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_SELECT_ROUTE,
583fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, null);
584fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
585fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
58694be6100218126ce6a08bf1f56209578500b361fRoboErik        public void unselectRoute(int controllerId, int reason) {
58794be6100218126ce6a08bf1f56209578500b361fRoboErik            Bundle extras = new Bundle();
58894be6100218126ce6a08bf1f56209578500b361fRoboErik            extras.putInt(CLIENT_DATA_UNSELECT_REASON, reason);
5893efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_UNSELECT_ROUTE,
59094be6100218126ce6a08bf1f56209578500b361fRoboErik                    mNextRequestId++, controllerId, null, extras);
591fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
592fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
593fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void setVolume(int controllerId, int volume) {
594fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Bundle data = new Bundle();
5953efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            data.putInt(CLIENT_DATA_VOLUME, volume);
5963efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_SET_ROUTE_VOLUME,
597fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, data);
598fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
599fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
600fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void updateVolume(int controllerId, int delta) {
601fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Bundle data = new Bundle();
6023efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            data.putInt(CLIENT_DATA_VOLUME, delta);
6033efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_UPDATE_ROUTE_VOLUME,
604fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, data);
605fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
606fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
607fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean sendControlRequest(int controllerId, Intent intent,
608fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ControlRequestCallback callback) {
609fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int requestId = mNextRequestId++;
6103efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            if (sendRequest(CLIENT_MSG_ROUTE_CONTROL_REQUEST,
611fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    requestId, controllerId, intent, null)) {
612fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (callback != null) {
613fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingCallbacks.put(requestId, callback);
614fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
615fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
616fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
617fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
618fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
619fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
62011417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
6213efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_SET_DISCOVERY_REQUEST,
62211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    mNextRequestId++, 0, request != null ? request.asBundle() : null, null);
62328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
62428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
625fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean sendRequest(int what, int requestId, int arg, Object obj, Bundle data) {
626fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Message msg = Message.obtain();
627fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.what = what;
628fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.arg1 = requestId;
629fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.arg2 = arg;
630fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.obj = obj;
631fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.setData(data);
632fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.replyTo = mReceiveMessenger;
633fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
634fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mServiceMessenger.send(msg);
635fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
636fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (DeadObjectException ex) {
637fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // The service died.
638fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (RemoteException ex) {
6393efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                if (what != CLIENT_MSG_UNREGISTER) {
640fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Log.e(TAG, "Could not send message to service.", ex);
641fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
642fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
643fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
644fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
645fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
646fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
64798ec0d5b378c8417156037af6389e90f0074a26eAurimas Liutikas    private static final class PrivateHandler extends Handler {
648e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas        PrivateHandler() {
649e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas        }
650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
651fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
652fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
653fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Handler that receives messages from the server.
654fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * <p>
655fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This inner class is static and only retains a weak reference to the connection
656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * to prevent the client from being leaked in case the service is holding an
657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * active reference to the client's messenger.
658fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p><p>
659fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This handler should not be used to handle any messages other than those
660fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * that come from the service.
661fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p>
662fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
663fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private static final class ReceiveHandler extends Handler {
664fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final WeakReference<Connection> mConnectionRef;
665fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
666fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public ReceiveHandler(Connection connection) {
667fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionRef = new WeakReference<Connection>(connection);
668fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
669fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
670fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void dispose() {
671fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionRef.clear();
672fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
673fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
674fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
675fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void handleMessage(Message msg) {
676f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            Connection connection = mConnectionRef.get();
677f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (connection != null) {
678f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                final int what = msg.what;
679f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                final int requestId = msg.arg1;
680f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                final int arg = msg.arg2;
681f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                final Object obj = msg.obj;
682f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                final Bundle data = msg.peekData();
683f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                if (!processMessage(connection, what, requestId, arg, obj, data)) {
684f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    if (DEBUG) {
685f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        Log.d(TAG, "Unhandled message from server: " + msg);
686f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    }
687fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
688fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
689fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
690fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
691f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        private boolean processMessage(Connection connection,
692f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                int what, int requestId, int arg, Object obj, Bundle data) {
693f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            switch (what) {
6943efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_GENERIC_FAILURE:
695f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    connection.onGenericFailure(requestId);
696f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    return true;
697f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
6983efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_GENERIC_SUCCESS:
699f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    connection.onGenericSuccess(requestId);
700f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    return true;
701f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
7023efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_REGISTERED:
703f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    if (obj == null || obj instanceof Bundle) {
704f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        return connection.onRegistered(requestId, arg, (Bundle)obj);
705f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    }
706f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    break;
707f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
7083efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_DESCRIPTOR_CHANGED:
709f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    if (obj == null || obj instanceof Bundle) {
710f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        return connection.onDescriptorChanged((Bundle)obj);
711f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    }
712f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    break;
713f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
7143efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED:
715f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    if (obj == null || obj instanceof Bundle) {
716f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        return connection.onControlRequestSucceeded(
717f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                                requestId, (Bundle)obj);
718f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    }
719f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    break;
720f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
7213efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_CONTROL_REQUEST_FAILED:
722f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    if (obj == null || obj instanceof Bundle) {
7237c5516660ad65c3f2a1a63ead6ffd2ae1879dd45Jeff Brown                        String error = (data == null ? null :
7243efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                                data.getString(SERVICE_DATA_ERROR));
725f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        return connection.onControlRequestFailed(
726f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                                requestId, error, (Bundle)obj);
727f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    }
728f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    break;
729fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
730fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
731fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
732fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
733fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown}
734