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
17b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownpackage android.support.v7.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;
31b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.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;
3711417b1cfde8f1749905f2d735623af9214148afJeff Brownimport java.util.List;
38fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
393efa63d3b896244713e84acbb5945562dce41d77Jeff Brownimport static android.support.v7.media.MediaRouteProviderProtocol.*;
403efa63d3b896244713e84acbb5945562dce41d77Jeff Brown
41fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown/**
42fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Maintains a connection to a particular media route provider service.
43fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */
44fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownfinal class RegisteredMediaRouteProvider extends MediaRouteProvider
45fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        implements ServiceConnection {
46f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private static final String TAG = "MediaRouteProviderProxy";  // max. 23 chars
47f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
48fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
49fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final ComponentName mComponentName;
50fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final PrivateHandler mPrivateHandler;
51fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final ArrayList<Controller> mControllers = new ArrayList<Controller>();
52fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
53f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private boolean mStarted;
54fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private boolean mBound;
55fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private Connection mActiveConnection;
56fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private boolean mConnectionReady;
57fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
58fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public RegisteredMediaRouteProvider(Context context, ComponentName componentName) {
59adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown        super(context, new ProviderMetadata(componentName));
60fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
61fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mComponentName = componentName;
62fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mPrivateHandler = new PrivateHandler();
63fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
64fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
65fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    @Override
66fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public RouteController onCreateRouteController(String routeId) {
6711417b1cfde8f1749905f2d735623af9214148afJeff Brown        MediaRouteProviderDescriptor descriptor = getDescriptor();
68fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (descriptor != null) {
6911417b1cfde8f1749905f2d735623af9214148afJeff Brown            List<MediaRouteDescriptor> routes = descriptor.getRoutes();
7011417b1cfde8f1749905f2d735623af9214148afJeff Brown            final int count = routes.size();
7111417b1cfde8f1749905f2d735623af9214148afJeff Brown            for (int i = 0; i < count; i++) {
7211417b1cfde8f1749905f2d735623af9214148afJeff Brown                final MediaRouteDescriptor route = routes.get(i);
7311417b1cfde8f1749905f2d735623af9214148afJeff Brown                if (route.getId().equals(routeId)) {
74fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Controller controller = new Controller(routeId);
75fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mControllers.add(controller);
76fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (mConnectionReady) {
77fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        controller.attachConnection(mActiveConnection);
78fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
79f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    updateBinding();
80fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    return controller;
81fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
82fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
83fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
84fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return null;
85fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
86fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
8728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    @Override
8811417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
8928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        if (mConnectionReady) {
9011417b1cfde8f1749905f2d735623af9214148afJeff Brown            mActiveConnection.setDiscoveryRequest(request);
9128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
92f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        updateBinding();
9328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown    }
9428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
95fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public boolean hasComponentName(String packageName, String className) {
96fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return mComponentName.getPackageName().equals(packageName)
97fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                && mComponentName.getClassName().equals(className);
98fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
99fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
100f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    public void start() {
101f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (!mStarted) {
102fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
103f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                Log.d(TAG, this + ": Starting");
104fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
105f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
106f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            mStarted = true;
107f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            updateBinding();
108fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
109fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
110fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
111f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    public void stop() {
112f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (mStarted) {
113f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (DEBUG) {
114f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                Log.d(TAG, this + ": Stopping");
115f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
116fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
117f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            mStarted = false;
118f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            updateBinding();
119fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
120fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
121fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
122fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void rebindIfDisconnected() {
123f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (mActiveConnection == null && shouldBind()) {
124fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            unbind();
125fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            bind();
126fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
127fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
128fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
129f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private void updateBinding() {
130f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (shouldBind()) {
131f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            bind();
132f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        } else {
133f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            unbind();
134f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        }
135f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    }
136f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
137f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private boolean shouldBind() {
138f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (mStarted) {
139f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            // Bind whenever there is a discovery request.
140f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (getDiscoveryRequest() != null) {
141f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                return true;
142f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
143f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
144f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            // Bind whenever the application has an active route controller.
145f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            // This means that one of this provider's routes is selected.
146f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (!mControllers.isEmpty()) {
147f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                return true;
148f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
149f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        }
150f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        return false;
151f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    }
152f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
153f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private void bind() {
154f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (!mBound) {
155f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (DEBUG) {
156f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                Log.d(TAG, this + ": Binding");
157f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
158f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
1593efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            Intent service = new Intent(MediaRouteProviderProtocol.SERVICE_INTERFACE);
160f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            service.setComponent(mComponentName);
161f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            try {
162f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                mBound = getContext().bindService(service, this, Context.BIND_AUTO_CREATE);
163f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                if (!mBound && DEBUG) {
164f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    Log.d(TAG, this + ": Bind failed");
165f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                }
166f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            } catch (SecurityException ex) {
167f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                if (DEBUG) {
168f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    Log.d(TAG, this + ": Bind failed", ex);
169f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                }
170f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
171f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        }
172f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    }
173f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
174f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    private void unbind() {
175f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        if (mBound) {
176f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (DEBUG) {
177f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                Log.d(TAG, this + ": Unbinding");
178f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            }
179f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
180f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            mBound = false;
181f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            disconnect();
182f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            getContext().unbindService(this);
183f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        }
184f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown    }
185f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
186fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    @Override
187fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void onServiceConnected(ComponentName name, IBinder service) {
188fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (DEBUG) {
189fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Log.d(TAG, this + ": Connected");
190fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
191fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
192fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mBound) {
193fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            disconnect();
194fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
195fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Messenger messenger = (service != null ? new Messenger(service) : null);
1963efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            if (isValidRemoteMessenger(messenger)) {
197fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Connection connection = new Connection(messenger);
198fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (connection.register()) {
199fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mActiveConnection = connection;
200fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                } else {
201fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (DEBUG) {
202fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        Log.d(TAG, this + ": Registration failed");
203fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
204fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
205fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } else {
206fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.e(TAG, this + ": Service returned invalid messenger binder");
207fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
208fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
209fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
210fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
211fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    @Override
212fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void onServiceDisconnected(ComponentName name) {
213fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (DEBUG) {
214fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Log.d(TAG, this + ": Service disconnected");
215fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
216fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        disconnect();
217fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
218fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
219fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void onConnectionReady(Connection connection) {
220fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
221fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionReady = true;
222fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            attachControllersToConnection();
22311417b1cfde8f1749905f2d735623af9214148afJeff Brown
22411417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteDiscoveryRequest request = getDiscoveryRequest();
22511417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (request != null) {
22611417b1cfde8f1749905f2d735623af9214148afJeff Brown                mActiveConnection.setDiscoveryRequest(request);
22728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown            }
228fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
229fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
230fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
231fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void onConnectionDied(Connection connection) {
232fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
233fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
234fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Service connection died");
235fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
236fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            disconnect();
237fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
238fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
239fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
240fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void onConnectionError(Connection connection, String error) {
241fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
242fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
243fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Service connection error - " + error);
244fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            unbind();
246fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
248fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
249fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void onConnectionDescriptorChanged(Connection connection,
25011417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteProviderDescriptor descriptor) {
251fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection == connection) {
252fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (DEBUG) {
253fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Log.d(TAG, this + ": Descriptor changed, descriptor=" + descriptor);
254fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
255fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            setDescriptor(descriptor);
256fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
257fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
258fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
259fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void disconnect() {
260fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (mActiveConnection != null) {
261fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            setDescriptor(null);
262fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionReady = false;
263fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            detachControllersFromConnection();
264fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mActiveConnection.dispose();
265fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mActiveConnection = null;
266fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void onControllerReleased(Controller controller) {
270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mControllers.remove(controller);
271fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        controller.detachConnection();
272f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        updateBinding();
273fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
274fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
275fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void attachControllersToConnection() {
276fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int count = mControllers.size();
277fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        for (int i = 0; i < count; i++) {
278fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mControllers.get(i).attachConnection(mActiveConnection);
279fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
280fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
281fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
282fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void detachControllersFromConnection() {
283fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int count = mControllers.size();
284fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        for (int i = 0; i < count; i++) {
285fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mControllers.get(i).detachConnection();
286fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
287fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
288fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
289fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    @Override
290fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public String toString() {
291fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return "Service connection " + mComponentName.flattenToShortString();
292fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
293fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
294fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final class Controller extends RouteController {
295fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final String mRouteId;
296fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
297fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean mSelected;
298fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mPendingSetVolume = -1;
299fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mPendingUpdateVolumeDelta;
300fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
301fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private Connection mConnection;
302fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mControllerId;
303fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
304fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public Controller(String routeId) {
305fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mRouteId = routeId;
306fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
307fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
308fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void attachConnection(Connection connection) {
309fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnection = connection;
310fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mControllerId = connection.createRouteController(mRouteId);
311fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mSelected) {
312fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                connection.selectRoute(mControllerId);
313fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mPendingSetVolume >= 0) {
314fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    connection.setVolume(mControllerId, mPendingSetVolume);
315fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingSetVolume = -1;
316fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
317fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (mPendingUpdateVolumeDelta != 0) {
318fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    connection.updateVolume(mControllerId, mPendingUpdateVolumeDelta);
319fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingUpdateVolumeDelta = 0;
320fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
321fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
322fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
323fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
324fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void detachConnection() {
325fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
326fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.releaseRouteController(mControllerId);
327fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection = null;
328fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mControllerId = 0;
329fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
330fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
331fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
332fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
333129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onRelease() {
334fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            onControllerReleased(this);
335fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
336fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
337fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
338129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onSelect() {
339fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mSelected = true;
340fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
341fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.selectRoute(mControllerId);
342fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
343fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
344fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
345fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
346129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onUnselect() {
34794be6100218126ce6a08bf1f56209578500b361fRoboErik            onUnselect(MediaRouter.UNSELECT_REASON_UNKNOWN);
34894be6100218126ce6a08bf1f56209578500b361fRoboErik        }
34994be6100218126ce6a08bf1f56209578500b361fRoboErik
35094be6100218126ce6a08bf1f56209578500b361fRoboErik        @Override
35194be6100218126ce6a08bf1f56209578500b361fRoboErik        public void onUnselect(int reason) {
352fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mSelected = false;
353fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
35494be6100218126ce6a08bf1f56209578500b361fRoboErik                mConnection.unselectRoute(mControllerId, reason);
355fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
356fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
357fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
358fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
359129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onSetVolume(int volume) {
360fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
361fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.setVolume(mControllerId, volume);
362fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } else {
363fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingSetVolume = volume;
364fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingUpdateVolumeDelta = 0;
365fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
366fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
367fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
368fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
369129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onUpdateVolume(int delta) {
370fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
371fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mConnection.updateVolume(mControllerId, delta);
372fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } else {
373fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingUpdateVolumeDelta += delta;
374fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
375fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
376fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
377fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
378129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
379fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mConnection != null) {
380fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return mConnection.sendControlRequest(mControllerId, intent, callback);
381fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
382fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
383fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
384fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
385fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
386fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final class Connection implements DeathRecipient {
387fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final Messenger mServiceMessenger;
388fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final ReceiveHandler mReceiveHandler;
389fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final Messenger mReceiveMessenger;
390fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
391fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mNextRequestId = 1;
392fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mNextControllerId = 1;
393fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mServiceVersion; // non-zero when registration complete
394fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
395fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private int mPendingRegisterRequestId;
396fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final SparseArray<ControlRequestCallback> mPendingCallbacks =
397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                new SparseArray<ControlRequestCallback>();
398fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
399fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public Connection(Messenger serviceMessenger) {
400fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mServiceMessenger = serviceMessenger;
401fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mReceiveHandler = new ReceiveHandler(this);
402fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mReceiveMessenger = new Messenger(mReceiveHandler);
403fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
404fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
405fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean register() {
406fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPendingRegisterRequestId = mNextRequestId++;
4073efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            if (!sendRequest(CLIENT_MSG_REGISTER,
408fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingRegisterRequestId,
4093efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                    CLIENT_VERSION_CURRENT, null, null)) {
410fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return false;
411fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
412fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
413fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
414fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mServiceMessenger.getBinder().linkToDeath(this, 0);
415fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
416fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (RemoteException ex) {
417fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                binderDied();
418fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
419fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
420fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
421fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
422fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void dispose() {
4233efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_UNREGISTER, 0, 0, null, null);
424fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mReceiveHandler.dispose();
425fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mServiceMessenger.getBinder().unlinkToDeath(this, 0);
426fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
427fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPrivateHandler.post(new Runnable() {
428fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                @Override
429fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                public void run() {
430fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    failPendingCallbacks();
431fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
432fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            });
433fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
434fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private void failPendingCallbacks() {
436fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int count = 0;
437fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = 0; i < mPendingCallbacks.size(); i++) {
43837da34e71d13376b3542b28d19c7356c622d6747Jeff Brown                mPendingCallbacks.valueAt(i).onError(null, null);
439fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
440fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPendingCallbacks.clear();
441fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
442fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
443fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onGenericFailure(int requestId) {
444fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (requestId == mPendingRegisterRequestId) {
445fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingRegisterRequestId = 0;
446fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionError(this, "Registation failed");
447fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
448fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
449fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
450fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingCallbacks.remove(requestId);
4513d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                callback.onError(null, null);
452fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
453fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return true;
454fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
455fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
456fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onGenericSuccess(int requestId) {
457fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return true;
458fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
459fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
460fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onRegistered(int requestId, int serviceVersion,
461fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                Bundle descriptorBundle) {
462fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mServiceVersion == 0
463fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    && requestId == mPendingRegisterRequestId
4643efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                    && serviceVersion >= SERVICE_VERSION_1) {
465fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingRegisterRequestId = 0;
466fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mServiceVersion = serviceVersion;
467fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionDescriptorChanged(this,
46811417b1cfde8f1749905f2d735623af9214148afJeff Brown                        MediaRouteProviderDescriptor.fromBundle(descriptorBundle));
469fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionReady(this);
470fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
471fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
472fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
473fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
474fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
475fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean onDescriptorChanged(Bundle descriptorBundle) {
476fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (mServiceVersion != 0) {
477fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                onConnectionDescriptorChanged(this,
47811417b1cfde8f1749905f2d735623af9214148afJeff Brown                        MediaRouteProviderDescriptor.fromBundle(descriptorBundle));
479fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
480fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
481fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
482fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
483fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
4843d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        public boolean onControlRequestSucceeded(int requestId, Bundle data) {
485fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
486fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (callback != null) {
487fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mPendingCallbacks.remove(requestId);
4883d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                callback.onResult(data);
4893d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                return true;
4903d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown            }
4913d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown            return false;
4923d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        }
4933d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown
4943d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown        public boolean onControlRequestFailed(int requestId, String error, Bundle data) {
4953d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
4963d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown            if (callback != null) {
4973d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                mPendingCallbacks.remove(requestId);
4983d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                callback.onError(error, data);
499fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
500fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
501fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
502fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
503fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
504fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
505fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void binderDied() {
506fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPrivateHandler.post(new Runnable() {
507fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                @Override
508fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                public void run() {
509fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    onConnectionDied(Connection.this);
510fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
511fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            });
512fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
513fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
514fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public int createRouteController(String routeId) {
515fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int controllerId = mNextControllerId++;
516fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Bundle data = new Bundle();
5173efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            data.putString(CLIENT_DATA_ROUTE_ID, routeId);
5183efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_CREATE_ROUTE_CONTROLLER,
519fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, data);
520fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return controllerId;
521fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
522fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
523fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void releaseRouteController(int controllerId) {
5243efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_RELEASE_ROUTE_CONTROLLER,
525fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, null);
526fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
527fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
528fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void selectRoute(int controllerId) {
5293efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_SELECT_ROUTE,
530fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, null);
531fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
532fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
53394be6100218126ce6a08bf1f56209578500b361fRoboErik        public void unselectRoute(int controllerId, int reason) {
53494be6100218126ce6a08bf1f56209578500b361fRoboErik            Bundle extras = new Bundle();
53594be6100218126ce6a08bf1f56209578500b361fRoboErik            extras.putInt(CLIENT_DATA_UNSELECT_REASON, reason);
5363efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_UNSELECT_ROUTE,
53794be6100218126ce6a08bf1f56209578500b361fRoboErik                    mNextRequestId++, controllerId, null, extras);
538fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
539fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
540fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void setVolume(int controllerId, int volume) {
541fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Bundle data = new Bundle();
5423efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            data.putInt(CLIENT_DATA_VOLUME, volume);
5433efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_SET_ROUTE_VOLUME,
544fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, data);
545fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
546fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
547fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void updateVolume(int controllerId, int delta) {
548fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Bundle data = new Bundle();
5493efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            data.putInt(CLIENT_DATA_VOLUME, delta);
5503efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_UPDATE_ROUTE_VOLUME,
551fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mNextRequestId++, controllerId, null, data);
552fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
553fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
554fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public boolean sendControlRequest(int controllerId, Intent intent,
555fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                ControlRequestCallback callback) {
556fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            int requestId = mNextRequestId++;
5573efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            if (sendRequest(CLIENT_MSG_ROUTE_CONTROL_REQUEST,
558fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    requestId, controllerId, intent, null)) {
559fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (callback != null) {
560fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mPendingCallbacks.put(requestId, callback);
561fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
562fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
563fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
564fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
565fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
566fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
56711417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
5683efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            sendRequest(CLIENT_MSG_SET_DISCOVERY_REQUEST,
56911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    mNextRequestId++, 0, request != null ? request.asBundle() : null, null);
57028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown        }
57128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown
572fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private boolean sendRequest(int what, int requestId, int arg, Object obj, Bundle data) {
573fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Message msg = Message.obtain();
574fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.what = what;
575fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.arg1 = requestId;
576fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.arg2 = arg;
577fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.obj = obj;
578fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.setData(data);
579fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            msg.replyTo = mReceiveMessenger;
580fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            try {
581fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mServiceMessenger.send(msg);
582fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return true;
583fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (DeadObjectException ex) {
584fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // The service died.
585fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            } catch (RemoteException ex) {
5863efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                if (what != CLIENT_MSG_UNREGISTER) {
587fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Log.e(TAG, "Could not send message to service.", ex);
588fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
589fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
590fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
591fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
592fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
593fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
594fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final class PrivateHandler extends Handler {
595fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
596fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
597fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    /**
598fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Handler that receives messages from the server.
599fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * <p>
600fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This inner class is static and only retains a weak reference to the connection
601fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * to prevent the client from being leaked in case the service is holding an
602fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * active reference to the client's messenger.
603fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p><p>
604fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * This handler should not be used to handle any messages other than those
605fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * that come from the service.
606fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p>
607fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     */
608fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private static final class ReceiveHandler extends Handler {
609fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final WeakReference<Connection> mConnectionRef;
610fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
611fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public ReceiveHandler(Connection connection) {
612fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionRef = new WeakReference<Connection>(connection);
613fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
614fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
615fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void dispose() {
616fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mConnectionRef.clear();
617fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
618fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
619fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        @Override
620fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public void handleMessage(Message msg) {
621f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            Connection connection = mConnectionRef.get();
622f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            if (connection != null) {
623f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                final int what = msg.what;
624f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                final int requestId = msg.arg1;
625f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                final int arg = msg.arg2;
626f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                final Object obj = msg.obj;
627f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                final Bundle data = msg.peekData();
628f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                if (!processMessage(connection, what, requestId, arg, obj, data)) {
629f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    if (DEBUG) {
630f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        Log.d(TAG, "Unhandled message from server: " + msg);
631f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    }
632fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
633fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
634fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
635fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
636f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown        private boolean processMessage(Connection connection,
637f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                int what, int requestId, int arg, Object obj, Bundle data) {
638f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown            switch (what) {
6393efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_GENERIC_FAILURE:
640f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    connection.onGenericFailure(requestId);
641f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    return true;
642f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
6433efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_GENERIC_SUCCESS:
644f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    connection.onGenericSuccess(requestId);
645f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    return true;
646f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
6473efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_REGISTERED:
648f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    if (obj == null || obj instanceof Bundle) {
649f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        return connection.onRegistered(requestId, arg, (Bundle)obj);
650f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    }
651f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    break;
652f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
6533efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_DESCRIPTOR_CHANGED:
654f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    if (obj == null || obj instanceof Bundle) {
655f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        return connection.onDescriptorChanged((Bundle)obj);
656f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    }
657f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    break;
658f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
6593efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED:
660f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    if (obj == null || obj instanceof Bundle) {
661f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        return connection.onControlRequestSucceeded(
662f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                                requestId, (Bundle)obj);
663f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    }
664f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    break;
665f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown
6663efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                case SERVICE_MSG_CONTROL_REQUEST_FAILED:
667f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    if (obj == null || obj instanceof Bundle) {
6687c5516660ad65c3f2a1a63ead6ffd2ae1879dd45Jeff Brown                        String error = (data == null ? null :
6693efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                                data.getString(SERVICE_DATA_ERROR));
670f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                        return connection.onControlRequestFailed(
671f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                                requestId, error, (Bundle)obj);
672f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    }
673f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    break;
674fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
675fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return false;
676fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
677fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
678fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown}
679