1 /* 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 17 package com.android.server.tv; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.media.tv.ITvRemoteProvider; 24 import android.media.tv.ITvRemoteServiceInput; 25 import android.os.Binder; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.RemoteException; 29 import android.os.UserHandle; 30 import android.util.Log; 31 import android.util.Slog; 32 33 import java.io.PrintWriter; 34 import java.lang.ref.WeakReference; 35 36 /** 37 * Maintains a connection to a tv remote provider service. 38 */ 39 final 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 } 648