blob: 557e9c8c85577d764f033fd5bc42fd85e5dd14d9 [file] [log] [blame]
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17package com.android.server.tv;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.media.tv.ITvRemoteProvider;
24import android.media.tv.ITvRemoteServiceInput;
25import android.os.Binder;
26import android.os.Handler;
27import android.os.IBinder;
28import android.os.RemoteException;
29import android.os.UserHandle;
30import android.util.Log;
31import android.util.Slog;
32
33import java.io.PrintWriter;
34import java.lang.ref.WeakReference;
35
36/**
37 * Maintains a connection to a tv remote provider service.
38 */
39final class TvRemoteProviderProxy implements ServiceConnection {
40 private static final String TAG = "TvRemoteProvProxy"; // max. 23 chars
41 private static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
42 private static final boolean DEBUG_KEY = false;
43
44
45 // This should match TvRemoteProvider.ACTION_TV_REMOTE_PROVIDER
46 protected static final String SERVICE_INTERFACE =
47 "com.android.media.tv.remoteprovider.TvRemoteProvider";
48 private final Context mContext;
49 private final ComponentName mComponentName;
50 private final int mUserId;
51 private final int mUid;
52 private final Handler mHandler;
53
54 /**
55 * State guarded by mLock.
56 * This is the first lock in sequence for an incoming call.
57 * The second lock is always {@link TvRemoteService#mLock}
58 *
59 * There are currently no methods that break this sequence.
60 */
61 private final Object mLock = new Object();
62
63 private ProviderMethods mProviderMethods;
64 // Connection state
65 private boolean mRunning;
66 private boolean mBound;
67 private Connection mActiveConnection;
68 private boolean mConnectionReady;
69
70 public TvRemoteProviderProxy(Context context, ComponentName componentName, int userId,
71 int uid) {
72 mContext = context;
73 mComponentName = componentName;
74 mUserId = userId;
75 mUid = uid;
76 mHandler = new Handler();
77 }
78
79 public void dump(PrintWriter pw, String prefix) {
80 pw.println(prefix + "Proxy");
81 pw.println(prefix + " mUserId=" + mUserId);
82 pw.println(prefix + " mRunning=" + mRunning);
83 pw.println(prefix + " mBound=" + mBound);
84 pw.println(prefix + " mActiveConnection=" + mActiveConnection);
85 pw.println(prefix + " mConnectionReady=" + mConnectionReady);
86 }
87
88 public void setProviderSink(ProviderMethods provider) {
89 mProviderMethods = provider;
90 }
91
92 public boolean hasComponentName(String packageName, String className) {
93 return mComponentName.getPackageName().equals(packageName)
94 && mComponentName.getClassName().equals(className);
95 }
96
97 public void start() {
98 if (!mRunning) {
99 if (DEBUG) {
100 Slog.d(TAG, this + ": Starting");
101 }
102
103 mRunning = true;
104 updateBinding();
105 }
106 }
107
108 public void stop() {
109 if (mRunning) {
110 if (DEBUG) {
111 Slog.d(TAG, this + ": Stopping");
112 }
113
114 mRunning = false;
115 updateBinding();
116 }
117 }
118
119 public void rebindIfDisconnected() {
120 synchronized (mLock) {
121 if (mActiveConnection == null && shouldBind()) {
122 unbind();
123 bind();
124 }
125 }
126 }
127
128 private void updateBinding() {
129 if (shouldBind()) {
130 bind();
131 } else {
132 unbind();
133 }
134 }
135
136 private boolean shouldBind() {
137 return mRunning;
138 }
139
140 private void bind() {
141 if (!mBound) {
142 if (DEBUG) {
143 Slog.d(TAG, this + ": Binding");
144 }
145
146 Intent service = new Intent(SERVICE_INTERFACE);
147 service.setComponent(mComponentName);
148 try {
149 mBound = mContext.bindServiceAsUser(service, this,
150 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
151 new UserHandle(mUserId));
152 if (!mBound && DEBUG) {
153 Slog.d(TAG, this + ": Bind failed");
154 }
155 } catch (SecurityException ex) {
156 if (DEBUG) {
157 Slog.d(TAG, this + ": Bind failed", ex);
158 }
159 }
160 }
161 }
162
163 private void unbind() {
164 if (mBound) {
165 if (DEBUG) {
166 Slog.d(TAG, this + ": Unbinding");
167 }
168
169 mBound = false;
170 disconnect();
171 mContext.unbindService(this);
172 }
173 }
174
175 @Override
176 public void onServiceConnected(ComponentName name, IBinder service) {
177 if (DEBUG) {
178 Slog.d(TAG, this + ": onServiceConnected()");
179 }
180
181 if (mBound) {
182 disconnect();
183
184 ITvRemoteProvider provider = ITvRemoteProvider.Stub.asInterface(service);
185 if (provider != null) {
186 Connection connection = new Connection(provider);
187 if (connection.register()) {
188 synchronized (mLock) {
189 mActiveConnection = connection;
190 }
191 if (DEBUG) {
192 Slog.d(TAG, this + ": Connected successfully.");
193 }
194 } else {
195 if (DEBUG) {
196 Slog.d(TAG, this + ": Registration failed");
197 }
198 }
199 } else {
200 Slog.e(TAG, this + ": Service returned invalid remote-control provider binder");
201 }
202 }
203 }
204
205 @Override
206 public void onServiceDisconnected(ComponentName name) {
207 if (DEBUG) Slog.d(TAG, this + ": Service disconnected");
208 disconnect();
209 }
210
211
212 private void onConnectionReady(Connection connection) {
213 synchronized (mLock) {
214 if (DEBUG) Slog.d(TAG, "onConnectionReady");
215 if (mActiveConnection == connection) {
216 if (DEBUG) Slog.d(TAG, "mConnectionReady = true");
217 mConnectionReady = true;
218 }
219 }
220 }
221
222 private void onConnectionDied(Connection connection) {
223 if (mActiveConnection == connection) {
224 if (DEBUG) Slog.d(TAG, this + ": Service connection died");
225 disconnect();
226 }
227 }
228
229 private void disconnect() {
230 synchronized (mLock) {
231 if (mActiveConnection != null) {
232 mConnectionReady = false;
233 mActiveConnection.dispose();
234 mActiveConnection = null;
235 }
236 }
237 }
238
239 // Provider helpers
240 public void inputBridgeConnected(IBinder token) {
241 synchronized (mLock) {
242 if (DEBUG) Slog.d(TAG, this + ": inputBridgeConnected token: " + token);
243 if (mConnectionReady) {
244 mActiveConnection.onInputBridgeConnected(token);
245 }
246 }
247 }
248
249 public interface ProviderMethods {
250 // InputBridge
251 void openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
252 int width, int height, int maxPointers);
253
254 void closeInputBridge(TvRemoteProviderProxy provider, IBinder token);
255
256 void clearInputBridge(TvRemoteProviderProxy provider, IBinder token);
257
258 void sendTimeStamp(TvRemoteProviderProxy provider, IBinder token, long timestamp);
259
260 void sendKeyDown(TvRemoteProviderProxy provider, IBinder token, int keyCode);
261
262 void sendKeyUp(TvRemoteProviderProxy provider, IBinder token, int keyCode);
263
264 void sendPointerDown(TvRemoteProviderProxy provider, IBinder token, int pointerId, int x,
265 int y);
266
267 void sendPointerUp(TvRemoteProviderProxy provider, IBinder token, int pointerId);
268
269 void sendPointerSync(TvRemoteProviderProxy provider, IBinder token);
270 }
271
272 private final class Connection implements IBinder.DeathRecipient {
273 private final ITvRemoteProvider mTvRemoteProvider;
274 private final RemoteServiceInputProvider mServiceInputProvider;
275
276 public Connection(ITvRemoteProvider provider) {
277 mTvRemoteProvider = provider;
278 mServiceInputProvider = new RemoteServiceInputProvider(this);
279 }
280
281 public boolean register() {
282 if (DEBUG) Slog.d(TAG, "Connection::register()");
283 try {
284 mTvRemoteProvider.asBinder().linkToDeath(this, 0);
285 mTvRemoteProvider.setRemoteServiceInputSink(mServiceInputProvider);
286 mHandler.post(new Runnable() {
287 @Override
288 public void run() {
289 onConnectionReady(Connection.this);
290 }
291 });
292 return true;
293 } catch (RemoteException ex) {
294 binderDied();
295 }
296 return false;
297 }
298
299 public void dispose() {
300 if (DEBUG) Slog.d(TAG, "Connection::dispose()");
301 mTvRemoteProvider.asBinder().unlinkToDeath(this, 0);
302 mServiceInputProvider.dispose();
303 }
304
305
306 public void onInputBridgeConnected(IBinder token) {
307 if (DEBUG) Slog.d(TAG, this + ": onInputBridgeConnected");
308 try {
309 mTvRemoteProvider.onInputBridgeConnected(token);
310 } catch (RemoteException ex) {
311 Slog.e(TAG, "Failed to deliver onInputBridgeConnected. ", ex);
312 }
313 }
314
315 @Override
316 public void binderDied() {
317 mHandler.post(new Runnable() {
318 @Override
319 public void run() {
320 onConnectionDied(Connection.this);
321 }
322 });
323 }
324
325 void openInputBridge(final IBinder token, final String name, final int width,
326 final int height, final int maxPointers) {
327 synchronized (mLock) {
328 if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
329 if (DEBUG) {
330 Slog.d(TAG, this + ": openInputBridge," +
331 " token=" + token + ", name=" + name);
332 }
333 final long idToken = Binder.clearCallingIdentity();
334 try {
335 if (mProviderMethods != null) {
336 mProviderMethods.openInputBridge(TvRemoteProviderProxy.this, token,
337 name, width, height, maxPointers);
338 }
339 } finally {
340 Binder.restoreCallingIdentity(idToken);
341 }
342 } else {
343 if (DEBUG) {
344 Slog.w(TAG,
345 "openInputBridge, Invalid connection or incorrect uid: " + Binder
346 .getCallingUid());
347 }
348 }
349 }
350 }
351
352 void closeInputBridge(final IBinder token) {
353 synchronized (mLock) {
354 if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
355 if (DEBUG) {
356 Slog.d(TAG, this + ": closeInputBridge," +
357 " token=" + token);
358 }
359 final long idToken = Binder.clearCallingIdentity();
360 try {
361 if (mProviderMethods != null) {
362 mProviderMethods.closeInputBridge(TvRemoteProviderProxy.this, token);
363 }
364 } finally {
365 Binder.restoreCallingIdentity(idToken);
366 }
367 } else {
368 if (DEBUG) {
369 Slog.w(TAG,
370 "closeInputBridge, Invalid connection or incorrect uid: " +
371 Binder.getCallingUid());
372 }
373 }
374 }
375 }
376
377 void clearInputBridge(final IBinder token) {
378 synchronized (mLock) {
379 if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
380 if (DEBUG) {
381 Slog.d(TAG, this + ": clearInputBridge," +
382 " token=" + token);
383 }
384 final long idToken = Binder.clearCallingIdentity();
385 try {
386 if (mProviderMethods != null) {
387 mProviderMethods.clearInputBridge(TvRemoteProviderProxy.this, token);
388 }
389 } finally {
390 Binder.restoreCallingIdentity(idToken);
391 }
392 } else {
393 if (DEBUG) {
394 Slog.w(TAG,
395 "clearInputBridge, Invalid connection or incorrect uid: " +
396 Binder.getCallingUid());
397 }
398 }
399 }
400 }
401
402 void sendTimestamp(final IBinder token, final long timestamp) {
403 synchronized (mLock) {
404 if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
405 final long idToken = Binder.clearCallingIdentity();
406 try {
407 if (mProviderMethods != null) {
408 mProviderMethods.sendTimeStamp(TvRemoteProviderProxy.this, token,
409 timestamp);
410 }
411 } finally {
412 Binder.restoreCallingIdentity(idToken);
413 }
414 } else {
415 if (DEBUG) {
416 Slog.w(TAG,
417 "sendTimeStamp, Invalid connection or incorrect uid: " + Binder
418 .getCallingUid());
419 }
420 }
421 }
422 }
423
424 void sendKeyDown(final IBinder token, final int keyCode) {
425 synchronized (mLock) {
426 if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
427 if (DEBUG_KEY) {
428 Slog.d(TAG, this + ": sendKeyDown," +
429 " token=" + token + ", keyCode=" + keyCode);
430 }
431 final long idToken = Binder.clearCallingIdentity();
432 try {
433 if (mProviderMethods != null) {
434 mProviderMethods.sendKeyDown(TvRemoteProviderProxy.this, token,
435 keyCode);
436 }
437 } finally {
438 Binder.restoreCallingIdentity(idToken);
439 }
440 } else {
441 if (DEBUG) {
442 Slog.w(TAG,
443 "sendKeyDown, Invalid connection or incorrect uid: " + Binder
444 .getCallingUid());
445 }
446 }
447 }
448 }
449
450 void sendKeyUp(final IBinder token, final int keyCode) {
451 synchronized (mLock) {
452 if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
453 if (DEBUG_KEY) {
454 Slog.d(TAG, this + ": sendKeyUp," +
455 " token=" + token + ", keyCode=" + keyCode);
456 }
457 final long idToken = Binder.clearCallingIdentity();
458 try {
459 if (mProviderMethods != null) {
460 mProviderMethods.sendKeyUp(TvRemoteProviderProxy.this, token, keyCode);
461 }
462 } finally {
463 Binder.restoreCallingIdentity(idToken);
464 }
465 } else {
466 if (DEBUG) {
467 Slog.w(TAG,
468 "sendKeyUp, Invalid connection or incorrect uid: " + Binder
469 .getCallingUid());
470 }
471 }
472 }
473 }
474
475 void sendPointerDown(final IBinder token, final int pointerId, final int x, final int y) {
476 synchronized (mLock) {
477 if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
478 if (DEBUG_KEY) {
479 Slog.d(TAG, this + ": sendPointerDown," +
480 " token=" + token + ", pointerId=" + pointerId);
481 }
482 final long idToken = Binder.clearCallingIdentity();
483 try {
484 if (mProviderMethods != null) {
485 mProviderMethods.sendPointerDown(TvRemoteProviderProxy.this, token,
486 pointerId, x, y);
487 }
488 } finally {
489 Binder.restoreCallingIdentity(idToken);
490 }
491 } else {
492 if (DEBUG) {
493 Slog.w(TAG,
494 "sendPointerDown, Invalid connection or incorrect uid: " + Binder
495 .getCallingUid());
496 }
497 }
498 }
499 }
500
501 void sendPointerUp(final IBinder token, final int pointerId) {
502 synchronized (mLock) {
503 if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
504 if (DEBUG_KEY) {
505 Slog.d(TAG, this + ": sendPointerUp," +
506 " token=" + token + ", pointerId=" + pointerId);
507 }
508 final long idToken = Binder.clearCallingIdentity();
509 try {
510 if (mProviderMethods != null) {
511 mProviderMethods.sendPointerUp(TvRemoteProviderProxy.this, token,
512 pointerId);
513 }
514 } finally {
515 Binder.restoreCallingIdentity(idToken);
516 }
517 } else {
518 if (DEBUG) {
519 Slog.w(TAG,
520 "sendPointerUp, Invalid connection or incorrect uid: " + Binder
521 .getCallingUid());
522 }
523 }
524 }
525 }
526
527 void sendPointerSync(final IBinder token) {
528 synchronized (mLock) {
529 if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
530 if (DEBUG_KEY) {
531 Slog.d(TAG, this + ": sendPointerSync," +
532 " token=" + token);
533 }
534 final long idToken = Binder.clearCallingIdentity();
535 try {
536 if (mProviderMethods != null) {
537 mProviderMethods.sendPointerSync(TvRemoteProviderProxy.this, token);
538 }
539 } finally {
540 Binder.restoreCallingIdentity(idToken);
541 }
542 } else {
543 if (DEBUG) {
544 Slog.w(TAG,
545 "sendPointerSync, Invalid connection or incorrect uid: " + Binder
546 .getCallingUid());
547 }
548 }
549 }
550 }
551 }
552
553 /**
554 * Receives events from the connected provider.
555 * <p>
556 * This inner class is static and only retains a weak reference to the connection
557 * to prevent the client from being leaked in case the service is holding an
558 * active reference to the client's callback.
559 * </p>
560 */
561 private static final class RemoteServiceInputProvider extends ITvRemoteServiceInput.Stub {
562 private final WeakReference<Connection> mConnectionRef;
563
564 public RemoteServiceInputProvider(Connection connection) {
565 mConnectionRef = new WeakReference<Connection>(connection);
566 }
567
568 public void dispose() {
569 // Terminate the connection.
570 mConnectionRef.clear();
571 }
572
573 @Override
574 public void openInputBridge(IBinder token, String name, int width,
575 int height, int maxPointers) throws RemoteException {
576 Connection connection = mConnectionRef.get();
577 if (connection != null) {
578 connection.openInputBridge(token, name, width, height, maxPointers);
579 }
580 }
581
582 @Override
583 public void closeInputBridge(IBinder token) throws RemoteException {
584 Connection connection = mConnectionRef.get();
585 if (connection != null) {
586 connection.closeInputBridge(token);
587 }
588 }
589
590 @Override
591 public void clearInputBridge(IBinder token) throws RemoteException {
592 Connection connection = mConnectionRef.get();
593 if (connection != null) {
594 connection.clearInputBridge(token);
595 }
596 }
597
598 @Override
599 public void sendTimestamp(IBinder token, long timestamp) throws RemoteException {
600 Connection connection = mConnectionRef.get();
601 if (connection != null) {
602 connection.sendTimestamp(token, timestamp);
603 }
604 }
605
606 @Override
607 public void sendKeyDown(IBinder token, int keyCode) throws RemoteException {
608 Connection connection = mConnectionRef.get();
609 if (connection != null) {
610 connection.sendKeyDown(token, keyCode);
611 }
612 }
613
614 @Override
615 public void sendKeyUp(IBinder token, int keyCode) throws RemoteException {
616 Connection connection = mConnectionRef.get();
617 if (connection != null) {
618 connection.sendKeyUp(token, keyCode);
619 }
620 }
621
622 @Override
623 public void sendPointerDown(IBinder token, int pointerId, int x, int y)
624 throws RemoteException {
625 Connection connection = mConnectionRef.get();
626 if (connection != null) {
627 connection.sendPointerDown(token, pointerId, x, y);
628 }
629 }
630
631 @Override
632 public void sendPointerUp(IBinder token, int pointerId) throws RemoteException {
633 Connection connection = mConnectionRef.get();
634 if (connection != null) {
635 connection.sendPointerUp(token, pointerId);
636 }
637 }
638
639 @Override
640 public void sendPointerSync(IBinder token) throws RemoteException {
641 Connection connection = mConnectionRef.get();
642 if (connection != null) {
643 connection.sendPointerSync(token);
644 }
645 }
646 }
647}