RegisteredMediaRouteProvider.java revision fa326a4649d9d0e8113e315f6c8251fe686abce4
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
17fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownpackage android.support.v4.media;
18fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
19fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.ComponentName;
20fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.Context;
21fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.Intent;
22fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.ServiceConnection;
23fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.Bundle;
24fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.DeadObjectException;
25fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.Handler;
26fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.IBinder;
27fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.RemoteException;
28fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.IBinder.DeathRecipient;
29fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.Message;
30fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.os.Messenger;
31fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.support.v4.media.MediaRouter.ControlRequestCallback;
32fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.util.Log;
33fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.util.SparseArray;
34fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
35fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport java.lang.ref.WeakReference;
36fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport java.util.ArrayList;
37fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
38fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown/**
39fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Maintains a connection to a particular media route provider service.
40fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */
41fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownfinal class RegisteredMediaRouteProvider extends MediaRouteProvider
42fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        implements ServiceConnection {
43fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private static final String TAG = "RegisteredMediaRouteProvider";
44fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private static final boolean DEBUG = true;
45fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
46fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final ComponentName mComponentName;
47fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final PrivateHandler mPrivateHandler;
48fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final ArrayList<Controller> mControllers = new ArrayList<Controller>();
49fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
50fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private boolean mBound;
51fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private Connection mActiveConnection;
52fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private boolean mConnectionReady;
53fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
54fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public RegisteredMediaRouteProvider(Context context, ComponentName componentName) {
55fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        super(context, new ProviderMetadata(componentName.getPackageName()));
56fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
57fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mComponentName = componentName;
58fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mPrivateHandler = new PrivateHandler();
59fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
60fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
61fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    @Override
62fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public RouteController onCreateRouteController(String routeId) {
63fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        ProviderDescriptor descriptor = getDescriptor();
64fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (descriptor != null) {
65fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            RouteDescriptor[] routes = descriptor.getRoutes();
66fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < routes.length; i++) {
67fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (routes[i].getId().equals(routeId)) {
68fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Controller controller = new Controller(routeId);
69fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mControllers.add(controller);
70fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mConnectionReady) {
71fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        controller.attachConnection(mActiveConnection);
72fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
73fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return controller;
74fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
75fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
76fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
77fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return null;
78fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
79fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
80fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public boolean hasComponentName(String packageName, String className) {
81fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return mComponentName.getPackageName().equals(packageName)
82fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                && mComponentName.getClassName().equals(className);
83fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
84fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
85fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void bind() {
86fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (DEBUG) {
87fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Log.d(TAG, this + ": Binding");
88fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
89fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
90fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        Intent service = new Intent(MediaRouteProviderService.SERVICE_INTERFACE);
91fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        service.setComponent(mComponentName);
92fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        try {
93fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // TODO: Should this use BIND_ALLOW_OOM_MANAGEMENT?
94fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mBound = getContext().bindService(service, this, Context.BIND_AUTO_CREATE);
95fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (!mBound && DEBUG) {
96fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Bind failed");
97fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
98fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        } catch (SecurityException ex) {
99fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
100fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Bind failed", ex);
101fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
102fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
103fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
104fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
105fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void unbind() {
106fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (DEBUG) {
107fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Log.d(TAG, this + ": Unbinding");
108fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
109fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
110fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        disconnect();
111fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mBound) {
112fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mBound = false;
113fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            getContext().unbindService(this);
114fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
115fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
116fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
117fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void rebindIfDisconnected() {
118fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == null) {
119fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            unbind();
120fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            bind();
121fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
122fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
123fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
124fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    @Override
125fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void onServiceConnected(ComponentName name, IBinder service) {
126fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (DEBUG) {
127fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Log.d(TAG, this + ": Connected");
128fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
129fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
130fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mBound) {
131fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            disconnect();
132fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
133fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Messenger messenger = (service != null ? new Messenger(service) : null);
134fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (MediaRouteProviderService.isValidRemoteMessenger(messenger)) {
135fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Connection connection = new Connection(messenger);
136fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (connection.register()) {
137fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mActiveConnection = connection;
138fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                } else {
139fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (DEBUG) {
140fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        Log.d(TAG, this + ": Registration failed");
141fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
142fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
143fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } else {
144fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.e(TAG, this + ": Service returned invalid messenger binder");
145fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
146fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
147fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
148fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
149fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    @Override
150fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void onServiceDisconnected(ComponentName name) {
151fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (DEBUG) {
152fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Log.d(TAG, this + ": Service disconnected");
153fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
154fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        disconnect();
155fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
156fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
157fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void onConnectionReady(Connection connection) {
158fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
159fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionReady = true;
160fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            attachControllersToConnection();
161fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
162fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
163fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
164fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void onConnectionDied(Connection connection) {
165fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
166fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
167fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Service connection died");
168fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
169fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            disconnect();
170fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
171fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
172fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
173fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void onConnectionError(Connection connection, String error) {
174fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
175fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
176fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Service connection error - " + error);
177fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
178fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            unbind();
179fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
180fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
181fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
182fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void onConnectionDescriptorChanged(Connection connection,
183fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            ProviderDescriptor descriptor) {
184fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
185fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
186fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Descriptor changed, descriptor=" + descriptor);
187fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
188fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            setDescriptor(descriptor);
189fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
190fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
191fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
192fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void disconnect() {
193fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection != null) {
194fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            setDescriptor(null);
195fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionReady = false;
196fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            detachControllersFromConnection();
197fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mActiveConnection.dispose();
198fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mActiveConnection = null;
199fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
200fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
201fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
202fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void onControllerReleased(Controller controller) {
203fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mControllers.remove(controller);
204fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        controller.detachConnection();
205fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
206fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
207fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void attachControllersToConnection() {
208fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int count = mControllers.size();
209fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        for (int i = 0; i < count; i++) {
210fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mControllers.get(i).attachConnection(mActiveConnection);
211fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
212fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
213fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
214fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void detachControllersFromConnection() {
215fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int count = mControllers.size();
216fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        for (int i = 0; i < count; i++) {
217fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mControllers.get(i).detachConnection();
218fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
219fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
220fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
221fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    @Override
222fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public String toString() {
223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return "Service connection " + mComponentName.flattenToShortString();
224fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
225fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
226fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final class Controller extends RouteController {
227fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final String mRouteId;
228fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
229fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean mSelected;
230fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mPendingSetVolume = -1;
231fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mPendingUpdateVolumeDelta;
232fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
233fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private Connection mConnection;
234fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mControllerId;
235fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
236fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public Controller(String routeId) {
237fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRouteId = routeId;
238fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
239fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
240fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void attachConnection(Connection connection) {
241fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnection = connection;
242fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mControllerId = connection.createRouteController(mRouteId);
243fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mSelected) {
244fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                connection.selectRoute(mControllerId);
245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mPendingSetVolume >= 0) {
246fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    connection.setVolume(mControllerId, mPendingSetVolume);
247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingSetVolume = -1;
248fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
249fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mPendingUpdateVolumeDelta != 0) {
250fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    connection.updateVolume(mControllerId, mPendingUpdateVolumeDelta);
251fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingUpdateVolumeDelta = 0;
252fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
253fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
254fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
255fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
256fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void detachConnection() {
257fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
258fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.releaseRouteController(mControllerId);
259fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection = null;
260fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mControllerId = 0;
261fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
262fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
263fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
264fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
265fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void release() {
266fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            onControllerReleased(this);
267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void select() {
271fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mSelected = true;
272fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
273fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.selectRoute(mControllerId);
274fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
275fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
276fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
277fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
278fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void unselect() {
279fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mSelected = false;
280fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
281fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.unselectRoute(mControllerId);
282fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
283fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
284fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
285fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
286fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void setVolume(int volume) {
287fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
288fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.setVolume(mControllerId, volume);
289fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } else {
290fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingSetVolume = volume;
291fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingUpdateVolumeDelta = 0;
292fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
293fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
294fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
295fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
296fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void updateVolume(int delta) {
297fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
298fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.updateVolume(mControllerId, delta);
299fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } else {
300fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingUpdateVolumeDelta += delta;
301fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
302fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
303fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
304fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
305fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean sendControlRequest(Intent intent, ControlRequestCallback callback) {
306fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
307fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mConnection.sendControlRequest(mControllerId, intent, callback);
308fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
309fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
310fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
311fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
312fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
313fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final class Connection implements DeathRecipient {
314fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final Messenger mServiceMessenger;
315fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ReceiveHandler mReceiveHandler;
316fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final Messenger mReceiveMessenger;
317fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
318fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mNextRequestId = 1;
319fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mNextControllerId = 1;
320fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mServiceVersion; // non-zero when registration complete
321fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
322fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mPendingRegisterRequestId;
323fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final SparseArray<ControlRequestCallback> mPendingCallbacks =
324fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                new SparseArray<ControlRequestCallback>();
325fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
326fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public Connection(Messenger serviceMessenger) {
327fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mServiceMessenger = serviceMessenger;
328fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mReceiveHandler = new ReceiveHandler(this);
329fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mReceiveMessenger = new Messenger(mReceiveHandler);
330fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
331fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
332fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean register() {
333fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPendingRegisterRequestId = mNextRequestId++;
334fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (!sendRequest(MediaRouteProviderService.CLIENT_MSG_REGISTER,
335fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingRegisterRequestId,
336fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    MediaRouteProviderService.CLIENT_VERSION_CURRENT, null, null)) {
337fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return false;
338fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
339fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
340fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
341fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mServiceMessenger.getBinder().linkToDeath(this, 0);
342fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
343fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (RemoteException ex) {
344fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                binderDied();
345fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
346fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
347fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
348fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
349fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void dispose() {
350fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sendRequest(MediaRouteProviderService.CLIENT_MSG_UNREGISTER, 0, 0, null, null);
351fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mReceiveHandler.dispose();
352fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mServiceMessenger.getBinder().unlinkToDeath(this, 0);
353fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
354fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPrivateHandler.post(new Runnable() {
355fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                @Override
356fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                public void run() {
357fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    failPendingCallbacks();
358fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
359fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            });
360fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
361fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
362fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void failPendingCallbacks() {
363fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = 0;
364fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < mPendingCallbacks.size(); i++) {
365fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingCallbacks.get(i).onResult(ControlRequestCallback.REQUEST_FAILED, null);
366fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
367fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPendingCallbacks.clear();
368fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
369fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
370fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onGenericFailure(int requestId) {
371fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (requestId == mPendingRegisterRequestId) {
372fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingRegisterRequestId = 0;
373fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionError(this, "Registation failed");
374fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
375fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
376fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
377fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingCallbacks.remove(requestId);
378fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                callback.onResult(ControlRequestCallback.REQUEST_FAILED, null);
379fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
380fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return true;
381fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
382fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
383fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onGenericSuccess(int requestId) {
384fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return true;
385fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
386fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
387fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onRegistered(int requestId, int serviceVersion,
388fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Bundle descriptorBundle) {
389fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mServiceVersion == 0
390fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    && requestId == mPendingRegisterRequestId
391fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    && serviceVersion >= MediaRouteProviderService.SERVICE_VERSION_1) {
392fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingRegisterRequestId = 0;
393fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mServiceVersion = serviceVersion;
394fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionDescriptorChanged(this,
395fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        ProviderDescriptor.fromBundle(descriptorBundle));
396fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionReady(this);
397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
398fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
399fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
400fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
401fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
402fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onDescriptorChanged(Bundle descriptorBundle) {
403fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mServiceVersion != 0) {
404fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionDescriptorChanged(this,
405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        ProviderDescriptor.fromBundle(descriptorBundle));
406fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
407fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
408fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
409fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
410fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
411fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onControlRequestResult(int requestId, int resultCode,
412fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Bundle resultData) {
413fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
414fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
415fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingCallbacks.remove(requestId);
416fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                callback.onResult(resultCode, resultData);
417fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
418fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
419fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
420fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
421fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
422fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
423fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void binderDied() {
424fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPrivateHandler.post(new Runnable() {
425fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                @Override
426fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                public void run() {
427fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    onConnectionDied(Connection.this);
428fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
429fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            });
430fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
431fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
432fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public int createRouteController(String routeId) {
433fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int controllerId = mNextControllerId++;
434fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Bundle data = new Bundle();
435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            data.putString(MediaRouteProviderService.CLIENT_DATA_ROUTE_ID, routeId);
436fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sendRequest(MediaRouteProviderService.CLIENT_MSG_CREATE_ROUTE_CONTROLLER,
437fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, data);
438fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return controllerId;
439fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
440fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
441fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void releaseRouteController(int controllerId) {
442fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sendRequest(MediaRouteProviderService.CLIENT_MSG_RELEASE_ROUTE_CONTROLLER,
443fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, null);
444fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
445fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
446fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void selectRoute(int controllerId) {
447fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sendRequest(MediaRouteProviderService.CLIENT_MSG_SELECT_ROUTE,
448fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, null);
449fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
450fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
451fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void unselectRoute(int controllerId) {
452fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sendRequest(MediaRouteProviderService.CLIENT_MSG_UNSELECT_ROUTE,
453fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, null);
454fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
455fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
456fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void setVolume(int controllerId, int volume) {
457fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Bundle data = new Bundle();
458fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            data.putInt(MediaRouteProviderService.CLIENT_DATA_VOLUME, volume);
459fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sendRequest(MediaRouteProviderService.CLIENT_MSG_SET_ROUTE_VOLUME,
460fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, data);
461fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
462fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
463fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void updateVolume(int controllerId, int delta) {
464fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Bundle data = new Bundle();
465fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            data.putInt(MediaRouteProviderService.CLIENT_DATA_VOLUME, delta);
466fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            sendRequest(MediaRouteProviderService.CLIENT_MSG_UPDATE_ROUTE_VOLUME,
467fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, data);
468fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
469fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
470fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean sendControlRequest(int controllerId, Intent intent,
471fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ControlRequestCallback callback) {
472fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int requestId = mNextRequestId++;
473fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (sendRequest(MediaRouteProviderService.CLIENT_MSG_ROUTE_CONTROL_REQUEST,
474fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    requestId, controllerId, intent, null)) {
475fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (callback != null) {
476fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingCallbacks.put(requestId, callback);
477fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
478fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
479fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
480fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
481fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
482fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
483fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean sendRequest(int what, int requestId, int arg, Object obj, Bundle data) {
484fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Message msg = Message.obtain();
485fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.what = what;
486fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.arg1 = requestId;
487fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.arg2 = arg;
488fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.obj = obj;
489fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.setData(data);
490fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.replyTo = mReceiveMessenger;
491fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
492fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mServiceMessenger.send(msg);
493fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
494fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (DeadObjectException ex) {
495fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // The service died.
496fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (RemoteException ex) {
497fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (what != MediaRouteProviderService.CLIENT_MSG_UNREGISTER) {
498fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Log.e(TAG, "Could not send message to service.", ex);
499fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
500fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
501fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
502fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
503fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
504fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
505fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final class PrivateHandler extends Handler {
506fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
507fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
508fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
509fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Handler that receives messages from the server.
510fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * <p>
511fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This inner class is static and only retains a weak reference to the connection
512fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * to prevent the client from being leaked in case the service is holding an
513fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * active reference to the client's messenger.
514fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p><p>
515fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This handler should not be used to handle any messages other than those
516fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * that come from the service.
517fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p>
518fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
519fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private static final class ReceiveHandler extends Handler {
520fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final WeakReference<Connection> mConnectionRef;
521fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
522fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public ReceiveHandler(Connection connection) {
523fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionRef = new WeakReference<Connection>(connection);
524fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
525fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
526fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void dispose() {
527fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionRef.clear();
528fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
529fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
530fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
531fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void handleMessage(Message msg) {
532fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int what = msg.what;
533fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int requestId = msg.arg1;
534fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final int arg = msg.arg2;
535fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            final Object obj = msg.obj;
536fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (!processMessage(what, requestId, arg, obj)) {
537fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (DEBUG) {
538fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Log.d(TAG, "Unhandled message from server: " + msg);
539fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
540fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
541fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
542fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
543fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean processMessage(int what, int requestId, int arg, Object obj) {
544fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Connection connection = mConnectionRef.get();
545fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (connection != null) {
546fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                switch (what) {
547fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    case MediaRouteProviderService.SERVICE_MSG_GENERIC_FAILURE:
548fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        connection.onGenericFailure(requestId);
549fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        return true;
550fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
551fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    case MediaRouteProviderService.SERVICE_MSG_GENERIC_SUCCESS:
552fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        connection.onGenericSuccess(requestId);
553fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        return true;
554fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
555fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    case MediaRouteProviderService.SERVICE_MSG_REGISTERED:
556fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        if (obj instanceof Bundle) {
557fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            return connection.onRegistered(requestId, arg, (Bundle)obj);
558fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        }
559fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        break;
560fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
561fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    case MediaRouteProviderService.SERVICE_MSG_DESCRIPTOR_CHANGED:
562fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        if (obj instanceof Bundle) {
563fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            return connection.onDescriptorChanged((Bundle)obj);
564fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        }
565fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        break;
566fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
567fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    case MediaRouteProviderService.SERVICE_MSG_CONTROL_RESULT:
568fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        if (obj == null || obj instanceof Bundle) {
569fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            return connection.onControlRequestResult(
570fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                                    requestId, arg, (Bundle)obj);
571fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        }
572fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        break;
573fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
574fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
575fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
576fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
577fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
578fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown}
579