1 /* 2 * Copyright 2019 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.media; 18 19 import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE; 20 21 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; 22 23 import android.annotation.NonNull; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.ServiceConnection; 28 import android.media.IMediaRoute2ProviderService; 29 import android.media.IMediaRoute2ProviderServiceCallback; 30 import android.media.MediaRoute2ProviderInfo; 31 import android.media.MediaRoute2ProviderService; 32 import android.media.RouteDiscoveryPreference; 33 import android.media.RoutingSessionInfo; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.IBinder; 37 import android.os.IBinder.DeathRecipient; 38 import android.os.Looper; 39 import android.os.RemoteException; 40 import android.os.UserHandle; 41 import android.text.TextUtils; 42 import android.util.Log; 43 import android.util.Slog; 44 45 import com.android.internal.annotations.GuardedBy; 46 47 import java.lang.ref.WeakReference; 48 import java.util.ArrayList; 49 import java.util.Collections; 50 import java.util.List; 51 import java.util.Objects; 52 import java.util.Set; 53 54 /** 55 * Maintains a connection to a particular {@link MediaRoute2ProviderService}. 56 */ 57 final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider 58 implements ServiceConnection { 59 private static final String TAG = "MR2ProviderSvcProxy"; 60 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 61 62 private final Context mContext; 63 private final int mUserId; 64 private final Handler mHandler; 65 private final boolean mIsSelfScanOnlyProvider; 66 67 // Connection state 68 private boolean mRunning; 69 private boolean mBound; 70 private Connection mActiveConnection; 71 private boolean mConnectionReady; 72 73 private boolean mIsManagerScanning; 74 private RouteDiscoveryPreference mLastDiscoveryPreference = null; 75 private boolean mLastDiscoveryPreferenceIncludesThisPackage = false; 76 77 @GuardedBy("mLock") 78 final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>(); 79 MediaRoute2ProviderServiceProxy( @onNull Context context, @NonNull ComponentName componentName, boolean isSelfScanOnlyProvider, int userId)80 MediaRoute2ProviderServiceProxy( 81 @NonNull Context context, 82 @NonNull ComponentName componentName, 83 boolean isSelfScanOnlyProvider, 84 int userId) { 85 super(componentName); 86 mContext = Objects.requireNonNull(context, "Context must not be null."); 87 mIsSelfScanOnlyProvider = isSelfScanOnlyProvider; 88 mUserId = userId; 89 mHandler = new Handler(Looper.myLooper()); 90 } 91 setManagerScanning(boolean managerScanning)92 public void setManagerScanning(boolean managerScanning) { 93 if (mIsManagerScanning != managerScanning) { 94 mIsManagerScanning = managerScanning; 95 updateBinding(); 96 } 97 } 98 99 @Override requestCreateSession(long requestId, String packageName, String routeId, Bundle sessionHints)100 public void requestCreateSession(long requestId, String packageName, String routeId, 101 Bundle sessionHints) { 102 if (mConnectionReady) { 103 mActiveConnection.requestCreateSession(requestId, packageName, routeId, sessionHints); 104 updateBinding(); 105 } 106 } 107 108 @Override releaseSession(long requestId, String sessionId)109 public void releaseSession(long requestId, String sessionId) { 110 if (mConnectionReady) { 111 mActiveConnection.releaseSession(requestId, sessionId); 112 updateBinding(); 113 } 114 } 115 116 @Override updateDiscoveryPreference( Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference)117 public void updateDiscoveryPreference( 118 Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference) { 119 mLastDiscoveryPreference = discoveryPreference; 120 mLastDiscoveryPreferenceIncludesThisPackage = 121 activelyScanningPackages.contains(mComponentName.getPackageName()); 122 if (mConnectionReady) { 123 mActiveConnection.updateDiscoveryPreference(discoveryPreference); 124 } 125 updateBinding(); 126 } 127 128 @Override selectRoute(long requestId, String sessionId, String routeId)129 public void selectRoute(long requestId, String sessionId, String routeId) { 130 if (mConnectionReady) { 131 mActiveConnection.selectRoute(requestId, sessionId, routeId); 132 } 133 } 134 135 @Override deselectRoute(long requestId, String sessionId, String routeId)136 public void deselectRoute(long requestId, String sessionId, String routeId) { 137 if (mConnectionReady) { 138 mActiveConnection.deselectRoute(requestId, sessionId, routeId); 139 } 140 } 141 142 @Override transferToRoute(long requestId, String sessionId, String routeId)143 public void transferToRoute(long requestId, String sessionId, String routeId) { 144 if (mConnectionReady) { 145 mActiveConnection.transferToRoute(requestId, sessionId, routeId); 146 } 147 } 148 149 @Override setRouteVolume(long requestId, String routeId, int volume)150 public void setRouteVolume(long requestId, String routeId, int volume) { 151 if (mConnectionReady) { 152 mActiveConnection.setRouteVolume(requestId, routeId, volume); 153 updateBinding(); 154 } 155 } 156 157 @Override setSessionVolume(long requestId, String sessionId, int volume)158 public void setSessionVolume(long requestId, String sessionId, int volume) { 159 if (mConnectionReady) { 160 mActiveConnection.setSessionVolume(requestId, sessionId, volume); 161 updateBinding(); 162 } 163 } 164 165 @Override prepareReleaseSession(@onNull String sessionId)166 public void prepareReleaseSession(@NonNull String sessionId) { 167 synchronized (mLock) { 168 for (RoutingSessionInfo session : mSessionInfos) { 169 if (TextUtils.equals(session.getId(), sessionId)) { 170 mSessionInfos.remove(session); 171 mReleasingSessions.add(session); 172 break; 173 } 174 } 175 } 176 } 177 hasComponentName(String packageName, String className)178 public boolean hasComponentName(String packageName, String className) { 179 return mComponentName.getPackageName().equals(packageName) 180 && mComponentName.getClassName().equals(className); 181 } 182 start()183 public void start() { 184 if (!mRunning) { 185 if (DEBUG) { 186 Slog.d(TAG, this + ": Starting"); 187 } 188 mRunning = true; 189 updateBinding(); 190 } 191 } 192 stop()193 public void stop() { 194 if (mRunning) { 195 if (DEBUG) { 196 Slog.d(TAG, this + ": Stopping"); 197 } 198 mRunning = false; 199 updateBinding(); 200 } 201 } 202 rebindIfDisconnected()203 public void rebindIfDisconnected() { 204 //TODO: When we are connecting to the service, calling this will unbind and bind again. 205 // We'd better not unbind if we are connecting. 206 if (mActiveConnection == null && shouldBind()) { 207 unbind(); 208 bind(); 209 } 210 } 211 updateBinding()212 private void updateBinding() { 213 if (shouldBind()) { 214 bind(); 215 } else { 216 unbind(); 217 } 218 } 219 shouldBind()220 private boolean shouldBind() { 221 if (mRunning) { 222 boolean shouldBind = 223 mLastDiscoveryPreference != null 224 && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty(); 225 if (mIsSelfScanOnlyProvider) { 226 shouldBind &= mLastDiscoveryPreferenceIncludesThisPackage; 227 } 228 shouldBind |= mIsManagerScanning; 229 shouldBind |= !getSessionInfos().isEmpty(); 230 return shouldBind; 231 } 232 return false; 233 } 234 bind()235 private void bind() { 236 if (!mBound) { 237 if (DEBUG) { 238 Slog.d(TAG, this + ": Binding"); 239 } 240 241 Intent service = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE); 242 service.setComponent(mComponentName); 243 try { 244 mBound = mContext.bindServiceAsUser(service, this, 245 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, 246 new UserHandle(mUserId)); 247 if (!mBound && DEBUG) { 248 Slog.d(TAG, this + ": Bind failed"); 249 } 250 } catch (SecurityException ex) { 251 if (DEBUG) { 252 Slog.d(TAG, this + ": Bind failed", ex); 253 } 254 } 255 } 256 } 257 unbind()258 private void unbind() { 259 if (mBound) { 260 if (DEBUG) { 261 Slog.d(TAG, this + ": Unbinding"); 262 } 263 264 mBound = false; 265 disconnect(); 266 mContext.unbindService(this); 267 } 268 } 269 270 @Override onServiceConnected(ComponentName name, IBinder service)271 public void onServiceConnected(ComponentName name, IBinder service) { 272 if (DEBUG) { 273 Slog.d(TAG, this + ": Connected"); 274 } 275 276 if (mBound) { 277 disconnect(); 278 IMediaRoute2ProviderService serviceBinder = 279 IMediaRoute2ProviderService.Stub.asInterface(service); 280 if (serviceBinder != null) { 281 Connection connection = new Connection(serviceBinder); 282 if (connection.register()) { 283 mActiveConnection = connection; 284 } else { 285 if (DEBUG) { 286 Slog.d(TAG, this + ": Registration failed"); 287 } 288 } 289 } else { 290 Slog.e(TAG, this + ": Service returned invalid binder"); 291 } 292 } 293 } 294 295 @Override onServiceDisconnected(ComponentName name)296 public void onServiceDisconnected(ComponentName name) { 297 if (DEBUG) { 298 Slog.d(TAG, this + ": Service disconnected"); 299 } 300 disconnect(); 301 } 302 303 @Override onBindingDied(ComponentName name)304 public void onBindingDied(ComponentName name) { 305 if (DEBUG) { 306 Slog.d(TAG, this + ": Service binding died"); 307 } 308 unbind(); 309 if (shouldBind()) { 310 bind(); 311 } 312 } 313 onConnectionReady(Connection connection)314 private void onConnectionReady(Connection connection) { 315 if (mActiveConnection == connection) { 316 mConnectionReady = true; 317 if (mLastDiscoveryPreference != null) { 318 updateDiscoveryPreference( 319 mLastDiscoveryPreferenceIncludesThisPackage 320 ? Set.of(mComponentName.getPackageName()) 321 : Set.of(), 322 mLastDiscoveryPreference); 323 } 324 } 325 } 326 onConnectionDied(Connection connection)327 private void onConnectionDied(Connection connection) { 328 if (mActiveConnection == connection) { 329 if (DEBUG) { 330 Slog.d(TAG, this + ": Service connection died"); 331 } 332 disconnect(); 333 } 334 } 335 onProviderUpdated(Connection connection, MediaRoute2ProviderInfo providerInfo)336 private void onProviderUpdated(Connection connection, MediaRoute2ProviderInfo providerInfo) { 337 if (mActiveConnection != connection) { 338 return; 339 } 340 if (DEBUG) { 341 Slog.d(TAG, this + ": updated"); 342 } 343 setAndNotifyProviderState(providerInfo); 344 } 345 onSessionCreated(Connection connection, long requestId, RoutingSessionInfo newSession)346 private void onSessionCreated(Connection connection, long requestId, 347 RoutingSessionInfo newSession) { 348 if (mActiveConnection != connection) { 349 return; 350 } 351 352 if (newSession == null) { 353 Slog.w(TAG, "onSessionCreated: Ignoring null session sent from " + mComponentName); 354 return; 355 } 356 357 newSession = assignProviderIdForSession(newSession); 358 String newSessionId = newSession.getId(); 359 360 synchronized (mLock) { 361 if (mSessionInfos.stream() 362 .anyMatch(session -> TextUtils.equals(session.getId(), newSessionId)) 363 || mReleasingSessions.stream() 364 .anyMatch(session -> TextUtils.equals(session.getId(), newSessionId))) { 365 Slog.w(TAG, "onSessionCreated: Duplicate session already exists. Ignoring."); 366 return; 367 } 368 mSessionInfos.add(newSession); 369 } 370 371 mCallback.onSessionCreated(this, requestId, newSession); 372 } 373 findSessionByIdLocked(RoutingSessionInfo session)374 private int findSessionByIdLocked(RoutingSessionInfo session) { 375 for (int i = 0; i < mSessionInfos.size(); i++) { 376 if (TextUtils.equals(mSessionInfos.get(i).getId(), session.getId())) { 377 return i; 378 } 379 } 380 return -1; 381 } 382 383 onSessionsUpdated(Connection connection, List<RoutingSessionInfo> sessions)384 private void onSessionsUpdated(Connection connection, List<RoutingSessionInfo> sessions) { 385 if (mActiveConnection != connection) { 386 return; 387 } 388 389 int targetIndex = 0; 390 synchronized (mLock) { 391 for (RoutingSessionInfo session : sessions) { 392 if (session == null) continue; 393 session = assignProviderIdForSession(session); 394 395 int sourceIndex = findSessionByIdLocked(session); 396 if (sourceIndex < 0) { 397 mSessionInfos.add(targetIndex++, session); 398 dispatchSessionCreated(REQUEST_ID_NONE, session); 399 } else if (sourceIndex < targetIndex) { 400 Slog.w(TAG, "Ignoring duplicate session ID: " + session.getId()); 401 } else { 402 mSessionInfos.set(sourceIndex, session); 403 Collections.swap(mSessionInfos, sourceIndex, targetIndex++); 404 dispatchSessionUpdated(session); 405 } 406 } 407 for (int i = mSessionInfos.size() - 1; i >= targetIndex; i--) { 408 RoutingSessionInfo releasedSession = mSessionInfos.remove(i); 409 dispatchSessionReleased(releasedSession); 410 } 411 } 412 } 413 onSessionReleased(Connection connection, RoutingSessionInfo releasedSession)414 private void onSessionReleased(Connection connection, RoutingSessionInfo releasedSession) { 415 if (mActiveConnection != connection) { 416 return; 417 } 418 if (releasedSession == null) { 419 Slog.w(TAG, "onSessionReleased: Ignoring null session sent from " + mComponentName); 420 return; 421 } 422 423 releasedSession = assignProviderIdForSession(releasedSession); 424 425 boolean found = false; 426 synchronized (mLock) { 427 for (RoutingSessionInfo session : mSessionInfos) { 428 if (TextUtils.equals(session.getId(), releasedSession.getId())) { 429 mSessionInfos.remove(session); 430 found = true; 431 break; 432 } 433 } 434 if (!found) { 435 for (RoutingSessionInfo session : mReleasingSessions) { 436 if (TextUtils.equals(session.getId(), releasedSession.getId())) { 437 mReleasingSessions.remove(session); 438 return; 439 } 440 } 441 } 442 } 443 444 if (!found) { 445 Slog.w(TAG, "onSessionReleased: Matching session info not found"); 446 return; 447 } 448 449 mCallback.onSessionReleased(this, releasedSession); 450 } 451 dispatchSessionCreated(long requestId, RoutingSessionInfo session)452 private void dispatchSessionCreated(long requestId, RoutingSessionInfo session) { 453 mHandler.sendMessage( 454 obtainMessage(mCallback::onSessionCreated, this, requestId, session)); 455 } 456 dispatchSessionUpdated(RoutingSessionInfo session)457 private void dispatchSessionUpdated(RoutingSessionInfo session) { 458 mHandler.sendMessage( 459 obtainMessage(mCallback::onSessionUpdated, this, session)); 460 } 461 dispatchSessionReleased(RoutingSessionInfo session)462 private void dispatchSessionReleased(RoutingSessionInfo session) { 463 mHandler.sendMessage( 464 obtainMessage(mCallback::onSessionReleased, this, session)); 465 } 466 assignProviderIdForSession(RoutingSessionInfo sessionInfo)467 private RoutingSessionInfo assignProviderIdForSession(RoutingSessionInfo sessionInfo) { 468 return new RoutingSessionInfo.Builder(sessionInfo) 469 .setOwnerPackageName(mComponentName.getPackageName()) 470 .setProviderId(getUniqueId()) 471 .build(); 472 } 473 onRequestFailed(Connection connection, long requestId, int reason)474 private void onRequestFailed(Connection connection, long requestId, int reason) { 475 if (mActiveConnection != connection) { 476 return; 477 } 478 479 if (requestId == REQUEST_ID_NONE) { 480 Slog.w(TAG, "onRequestFailed: Ignoring requestId REQUEST_ID_NONE"); 481 return; 482 } 483 484 mCallback.onRequestFailed(this, requestId, reason); 485 } 486 disconnect()487 private void disconnect() { 488 if (mActiveConnection != null) { 489 mConnectionReady = false; 490 mActiveConnection.dispose(); 491 mActiveConnection = null; 492 setAndNotifyProviderState(null); 493 synchronized (mLock) { 494 for (RoutingSessionInfo sessionInfo : mSessionInfos) { 495 mCallback.onSessionReleased(this, sessionInfo); 496 } 497 mSessionInfos.clear(); 498 mReleasingSessions.clear(); 499 } 500 } 501 } 502 503 @Override getDebugString()504 protected String getDebugString() { 505 return TextUtils.formatSimple( 506 "ProviderServiceProxy - package: %s, bound: %b, connection (active:%b, ready:%b)", 507 mComponentName.getPackageName(), 508 mBound, 509 mActiveConnection != null, 510 mConnectionReady); 511 } 512 513 private final class Connection implements DeathRecipient { 514 private final IMediaRoute2ProviderService mService; 515 private final ServiceCallbackStub mCallbackStub; 516 Connection(IMediaRoute2ProviderService serviceBinder)517 Connection(IMediaRoute2ProviderService serviceBinder) { 518 mService = serviceBinder; 519 mCallbackStub = new ServiceCallbackStub(this); 520 } 521 register()522 public boolean register() { 523 try { 524 mService.asBinder().linkToDeath(this, 0); 525 mService.setCallback(mCallbackStub); 526 mHandler.post(() -> onConnectionReady(Connection.this)); 527 return true; 528 } catch (RemoteException ex) { 529 binderDied(); 530 } 531 return false; 532 } 533 dispose()534 public void dispose() { 535 mService.asBinder().unlinkToDeath(this, 0); 536 mCallbackStub.dispose(); 537 } 538 requestCreateSession(long requestId, String packageName, String routeId, Bundle sessionHints)539 public void requestCreateSession(long requestId, String packageName, String routeId, 540 Bundle sessionHints) { 541 try { 542 mService.requestCreateSession(requestId, packageName, routeId, sessionHints); 543 } catch (RemoteException ex) { 544 Slog.e(TAG, "requestCreateSession: Failed to deliver request."); 545 } 546 } 547 releaseSession(long requestId, String sessionId)548 public void releaseSession(long requestId, String sessionId) { 549 try { 550 mService.releaseSession(requestId, sessionId); 551 } catch (RemoteException ex) { 552 Slog.e(TAG, "releaseSession: Failed to deliver request."); 553 } 554 } 555 updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference)556 public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) { 557 try { 558 mService.updateDiscoveryPreference(discoveryPreference); 559 } catch (RemoteException ex) { 560 Slog.e(TAG, "updateDiscoveryPreference: Failed to deliver request."); 561 } 562 } 563 selectRoute(long requestId, String sessionId, String routeId)564 public void selectRoute(long requestId, String sessionId, String routeId) { 565 try { 566 mService.selectRoute(requestId, sessionId, routeId); 567 } catch (RemoteException ex) { 568 Slog.e(TAG, "selectRoute: Failed to deliver request.", ex); 569 } 570 } 571 deselectRoute(long requestId, String sessionId, String routeId)572 public void deselectRoute(long requestId, String sessionId, String routeId) { 573 try { 574 mService.deselectRoute(requestId, sessionId, routeId); 575 } catch (RemoteException ex) { 576 Slog.e(TAG, "deselectRoute: Failed to deliver request.", ex); 577 } 578 } 579 transferToRoute(long requestId, String sessionId, String routeId)580 public void transferToRoute(long requestId, String sessionId, String routeId) { 581 try { 582 mService.transferToRoute(requestId, sessionId, routeId); 583 } catch (RemoteException ex) { 584 Slog.e(TAG, "transferToRoute: Failed to deliver request.", ex); 585 } 586 } 587 setRouteVolume(long requestId, String routeId, int volume)588 public void setRouteVolume(long requestId, String routeId, int volume) { 589 try { 590 mService.setRouteVolume(requestId, routeId, volume); 591 } catch (RemoteException ex) { 592 Slog.e(TAG, "setRouteVolume: Failed to deliver request.", ex); 593 } 594 } 595 setSessionVolume(long requestId, String sessionId, int volume)596 public void setSessionVolume(long requestId, String sessionId, int volume) { 597 try { 598 mService.setSessionVolume(requestId, sessionId, volume); 599 } catch (RemoteException ex) { 600 Slog.e(TAG, "setSessionVolume: Failed to deliver request.", ex); 601 } 602 } 603 604 @Override binderDied()605 public void binderDied() { 606 mHandler.post(() -> onConnectionDied(Connection.this)); 607 } 608 postProviderUpdated(MediaRoute2ProviderInfo providerInfo)609 void postProviderUpdated(MediaRoute2ProviderInfo providerInfo) { 610 mHandler.post(() -> onProviderUpdated(Connection.this, providerInfo)); 611 } 612 postSessionCreated(long requestId, RoutingSessionInfo sessionInfo)613 void postSessionCreated(long requestId, RoutingSessionInfo sessionInfo) { 614 mHandler.post(() -> onSessionCreated(Connection.this, requestId, sessionInfo)); 615 } 616 postSessionsUpdated(List<RoutingSessionInfo> sessionInfo)617 void postSessionsUpdated(List<RoutingSessionInfo> sessionInfo) { 618 mHandler.post(() -> onSessionsUpdated(Connection.this, sessionInfo)); 619 } 620 postSessionReleased(RoutingSessionInfo sessionInfo)621 void postSessionReleased(RoutingSessionInfo sessionInfo) { 622 mHandler.post(() -> onSessionReleased(Connection.this, sessionInfo)); 623 } 624 postRequestFailed(long requestId, int reason)625 void postRequestFailed(long requestId, int reason) { 626 mHandler.post(() -> onRequestFailed(Connection.this, requestId, reason)); 627 } 628 } 629 630 private static final class ServiceCallbackStub extends 631 IMediaRoute2ProviderServiceCallback.Stub { 632 private final WeakReference<Connection> mConnectionRef; 633 ServiceCallbackStub(Connection connection)634 ServiceCallbackStub(Connection connection) { 635 mConnectionRef = new WeakReference<>(connection); 636 } 637 dispose()638 public void dispose() { 639 mConnectionRef.clear(); 640 } 641 642 @Override notifyProviderUpdated(MediaRoute2ProviderInfo providerInfo)643 public void notifyProviderUpdated(MediaRoute2ProviderInfo providerInfo) { 644 Connection connection = mConnectionRef.get(); 645 if (connection != null) { 646 connection.postProviderUpdated(providerInfo); 647 } 648 } 649 650 @Override notifySessionCreated(long requestId, RoutingSessionInfo sessionInfo)651 public void notifySessionCreated(long requestId, RoutingSessionInfo sessionInfo) { 652 Connection connection = mConnectionRef.get(); 653 if (connection != null) { 654 connection.postSessionCreated(requestId, sessionInfo); 655 } 656 } 657 658 @Override notifySessionsUpdated(List<RoutingSessionInfo> sessionInfo)659 public void notifySessionsUpdated(List<RoutingSessionInfo> sessionInfo) { 660 Connection connection = mConnectionRef.get(); 661 if (connection != null) { 662 connection.postSessionsUpdated(sessionInfo); 663 } 664 } 665 666 @Override notifySessionReleased(RoutingSessionInfo sessionInfo)667 public void notifySessionReleased(RoutingSessionInfo sessionInfo) { 668 Connection connection = mConnectionRef.get(); 669 if (connection != null) { 670 connection.postSessionReleased(sessionInfo); 671 } 672 } 673 674 @Override notifyRequestFailed(long requestId, int reason)675 public void notifyRequestFailed(long requestId, int reason) { 676 Connection connection = mConnectionRef.get(); 677 if (connection != null) { 678 connection.postRequestFailed(requestId, reason); 679 } 680 } 681 } 682 } 683