2 * Copyright (C) 2013 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com
.android
.server
.media
;
19 import android
.content
.ComponentName
;
20 import android
.content
.Context
;
21 import android
.content
.Intent
;
22 import android
.content
.ServiceConnection
;
23 import android
.media
.IRemoteDisplayCallback
;
24 import android
.media
.IRemoteDisplayProvider
;
25 import android
.media
.RemoteDisplayState
;
26 import android
.os
.Handler
;
27 import android
.os
.IBinder
;
28 import android
.os
.RemoteException
;
29 import android
.os
.IBinder
.DeathRecipient
;
30 import android
.os
.UserHandle
;
31 import android
.util
.Log
;
32 import android
.util
.Slog
;
34 import java
.io
.PrintWriter
;
35 import java
.lang
.ref
.WeakReference
;
36 import java
.util
.Objects
;
39 * Maintains a connection to a particular remote display provider service.
41 final class RemoteDisplayProviderProxy
implements ServiceConnection
{
42 private static final String TAG
= "RemoteDisplayProvider"; // max. 23 chars
43 private static final boolean DEBUG
= Log
.isLoggable(TAG
, Log
.DEBUG
);
45 private final Context mContext
;
46 private final ComponentName mComponentName
;
47 private final int mUserId
;
48 private final Handler mHandler
;
50 private Callback mDisplayStateCallback
;
53 private boolean mRunning
;
54 private boolean mBound
;
55 private Connection mActiveConnection
;
56 private boolean mConnectionReady
;
59 private int mDiscoveryMode
;
60 private String mSelectedDisplayId
;
61 private RemoteDisplayState mDisplayState
;
62 private boolean mScheduledDisplayStateChangedCallback
;
64 public RemoteDisplayProviderProxy(Context context
, ComponentName componentName
,
67 mComponentName
= componentName
;
69 mHandler
= new Handler();
72 public void dump(PrintWriter pw
, String prefix
) {
73 pw
.println(prefix
+ "Proxy");
74 pw
.println(prefix
+ " mUserId=" + mUserId
);
75 pw
.println(prefix
+ " mRunning=" + mRunning
);
76 pw
.println(prefix
+ " mBound=" + mBound
);
77 pw
.println(prefix
+ " mActiveConnection=" + mActiveConnection
);
78 pw
.println(prefix
+ " mConnectionReady=" + mConnectionReady
);
79 pw
.println(prefix
+ " mDiscoveryMode=" + mDiscoveryMode
);
80 pw
.println(prefix
+ " mSelectedDisplayId=" + mSelectedDisplayId
);
81 pw
.println(prefix
+ " mDisplayState=" + mDisplayState
);
84 public void setCallback(Callback callback
) {
85 mDisplayStateCallback
= callback
;
88 public RemoteDisplayState
getDisplayState() {
92 public void setDiscoveryMode(int mode
) {
93 if (mDiscoveryMode
!= mode
) {
94 mDiscoveryMode
= mode
;
95 if (mConnectionReady
) {
96 mActiveConnection
.setDiscoveryMode(mode
);
102 public void setSelectedDisplay(String id
) {
103 if (!Objects
.equals(mSelectedDisplayId
, id
)) {
104 if (mConnectionReady
&& mSelectedDisplayId
!= null) {
105 mActiveConnection
.disconnect(mSelectedDisplayId
);
107 mSelectedDisplayId
= id
;
108 if (mConnectionReady
&& id
!= null) {
109 mActiveConnection
.connect(id
);
115 public void setDisplayVolume(int volume
) {
116 if (mConnectionReady
&& mSelectedDisplayId
!= null) {
117 mActiveConnection
.setVolume(mSelectedDisplayId
, volume
);
121 public void adjustDisplayVolume(int delta
) {
122 if (mConnectionReady
&& mSelectedDisplayId
!= null) {
123 mActiveConnection
.adjustVolume(mSelectedDisplayId
, delta
);
127 public boolean hasComponentName(String packageName
, String className
) {
128 return mComponentName
.getPackageName().equals(packageName
)
129 && mComponentName
.getClassName().equals(className
);
132 public String
getFlattenedComponentName() {
133 return mComponentName
.flattenToShortString();
136 public void start() {
139 Slog
.d(TAG
, this + ": Starting");
150 Slog
.d(TAG
, this + ": Stopping");
158 public void rebindIfDisconnected() {
159 if (mActiveConnection
== null && shouldBind()) {
165 private void updateBinding() {
173 private boolean shouldBind() {
175 // Bind whenever there is a discovery request or selected display.
176 if (mDiscoveryMode
!= RemoteDisplayState
.DISCOVERY_MODE_NONE
177 || mSelectedDisplayId
!= null) {
184 private void bind() {
187 Slog
.d(TAG
, this + ": Binding");
190 Intent service
= new Intent(RemoteDisplayState
.SERVICE_INTERFACE
);
191 service
.setComponent(mComponentName
);
193 mBound
= mContext
.bindServiceAsUser(service
, this,
194 Context
.BIND_AUTO_CREATE
| Context
.BIND_FOREGROUND_SERVICE
,
195 new UserHandle(mUserId
));
196 if (!mBound
&& DEBUG
) {
197 Slog
.d(TAG
, this + ": Bind failed");
199 } catch (SecurityException ex
) {
201 Slog
.d(TAG
, this + ": Bind failed", ex
);
207 private void unbind() {
210 Slog
.d(TAG
, this + ": Unbinding");
215 mContext
.unbindService(this);
220 public void onServiceConnected(ComponentName name
, IBinder service
) {
222 Slog
.d(TAG
, this + ": Connected");
228 IRemoteDisplayProvider provider
= IRemoteDisplayProvider
.Stub
.asInterface(service
);
229 if (provider
!= null) {
230 Connection connection
= new Connection(provider
);
231 if (connection
.register()) {
232 mActiveConnection
= connection
;
235 Slog
.d(TAG
, this + ": Registration failed");
239 Slog
.e(TAG
, this + ": Service returned invalid remote display provider binder");
245 public void onServiceDisconnected(ComponentName name
) {
247 Slog
.d(TAG
, this + ": Service disconnected");
252 private void onConnectionReady(Connection connection
) {
253 if (mActiveConnection
== connection
) {
254 mConnectionReady
= true;
256 if (mDiscoveryMode
!= RemoteDisplayState
.DISCOVERY_MODE_NONE
) {
257 mActiveConnection
.setDiscoveryMode(mDiscoveryMode
);
259 if (mSelectedDisplayId
!= null) {
260 mActiveConnection
.connect(mSelectedDisplayId
);
265 private void onConnectionDied(Connection connection
) {
266 if (mActiveConnection
== connection
) {
268 Slog
.d(TAG
, this + ": Service connection died");
274 private void onDisplayStateChanged(Connection connection
, RemoteDisplayState state
) {
275 if (mActiveConnection
== connection
) {
277 Slog
.d(TAG
, this + ": State changed, state=" + state
);
279 setDisplayState(state
);
283 private void disconnect() {
284 if (mActiveConnection
!= null) {
285 if (mSelectedDisplayId
!= null) {
286 mActiveConnection
.disconnect(mSelectedDisplayId
);
288 mConnectionReady
= false;
289 mActiveConnection
.dispose();
290 mActiveConnection
= null;
291 setDisplayState(null);
295 private void setDisplayState(RemoteDisplayState state
) {
296 if (!Objects
.equals(mDisplayState
, state
)) {
297 mDisplayState
= state
;
298 if (!mScheduledDisplayStateChangedCallback
) {
299 mScheduledDisplayStateChangedCallback
= true;
300 mHandler
.post(mDisplayStateChanged
);
306 public String
toString() {
307 return "Service connection " + mComponentName
.flattenToShortString();
310 private final Runnable mDisplayStateChanged
= new Runnable() {
313 mScheduledDisplayStateChangedCallback
= false;
314 if (mDisplayStateCallback
!= null) {
315 mDisplayStateCallback
.onDisplayStateChanged(
316 RemoteDisplayProviderProxy
.this, mDisplayState
);
321 public interface Callback
{
322 void onDisplayStateChanged(RemoteDisplayProviderProxy provider
, RemoteDisplayState state
);
325 private final class Connection
implements DeathRecipient
{
326 private final IRemoteDisplayProvider mProvider
;
327 private final ProviderCallback mCallback
;
329 public Connection(IRemoteDisplayProvider provider
) {
330 mProvider
= provider
;
331 mCallback
= new ProviderCallback(this);
334 public boolean register() {
336 mProvider
.asBinder().linkToDeath(this, 0);
337 mProvider
.setCallback(mCallback
);
338 mHandler
.post(new Runnable() {
341 onConnectionReady(Connection
.this);
345 } catch (RemoteException ex
) {
351 public void dispose() {
352 mProvider
.asBinder().unlinkToDeath(this, 0);
356 public void setDiscoveryMode(int mode
) {
358 mProvider
.setDiscoveryMode(mode
);
359 } catch (RemoteException ex
) {
360 Slog
.e(TAG
, "Failed to deliver request to set discovery mode.", ex
);
364 public void connect(String id
) {
366 mProvider
.connect(id
);
367 } catch (RemoteException ex
) {
368 Slog
.e(TAG
, "Failed to deliver request to connect to display.", ex
);
372 public void disconnect(String id
) {
374 mProvider
.disconnect(id
);
375 } catch (RemoteException ex
) {
376 Slog
.e(TAG
, "Failed to deliver request to disconnect from display.", ex
);
380 public void setVolume(String id
, int volume
) {
382 mProvider
.setVolume(id
, volume
);
383 } catch (RemoteException ex
) {
384 Slog
.e(TAG
, "Failed to deliver request to set display volume.", ex
);
388 public void adjustVolume(String id
, int volume
) {
390 mProvider
.adjustVolume(id
, volume
);
391 } catch (RemoteException ex
) {
392 Slog
.e(TAG
, "Failed to deliver request to adjust display volume.", ex
);
397 public void binderDied() {
398 mHandler
.post(new Runnable() {
401 onConnectionDied(Connection
.this);
406 void postStateChanged(final RemoteDisplayState state
) {
407 mHandler
.post(new Runnable() {
410 onDisplayStateChanged(Connection
.this, state
);
417 * Receives callbacks from the service.
419 * This inner class is static and only retains a weak reference to the connection
420 * to prevent the client from being leaked in case the service is holding an
421 * active reference to the client's callback.
424 private static final class ProviderCallback
extends IRemoteDisplayCallback
.Stub
{
425 private final WeakReference
<Connection
> mConnectionRef
;
427 public ProviderCallback(Connection connection
) {
428 mConnectionRef
= new WeakReference
<Connection
>(connection
);
431 public void dispose() {
432 mConnectionRef
.clear();
436 public void onStateChanged(RemoteDisplayState state
) throws RemoteException
{
437 Connection connection
= mConnectionRef
.get();
438 if (connection
!= null) {
439 connection
.postStateChanged(state
);