1 /* 2 * Copyright (c) 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.ims; 18 19 import android.content.Context; 20 import android.net.Uri; 21 import android.os.IBinder; 22 import android.os.PersistableBundle; 23 import android.os.RemoteException; 24 import android.os.ServiceSpecificException; 25 import android.telephony.BinderCacheManager; 26 import android.telephony.CarrierConfigManager; 27 import android.telephony.SubscriptionManager; 28 import android.telephony.TelephonyFrameworkInitializer; 29 import android.telephony.ims.ImsException; 30 import android.telephony.ims.ImsService; 31 import android.telephony.ims.RcsUceAdapter.StackPublishTriggerType; 32 import android.telephony.ims.RegistrationManager; 33 import android.telephony.ims.aidl.ICapabilityExchangeEventListener; 34 import android.telephony.ims.aidl.IImsCapabilityCallback; 35 import android.telephony.ims.aidl.IImsConfig; 36 import android.telephony.ims.aidl.IImsRcsController; 37 import android.telephony.ims.aidl.IImsRcsFeature; 38 import android.telephony.ims.aidl.IImsRegistration; 39 import android.telephony.ims.aidl.IImsRegistrationCallback; 40 import android.telephony.ims.aidl.IOptionsRequestCallback; 41 import android.telephony.ims.aidl.IOptionsResponseCallback; 42 import android.telephony.ims.aidl.IPublishResponseCallback; 43 import android.telephony.ims.aidl.ISipTransport; 44 import android.telephony.ims.aidl.ISubscribeResponseCallback; 45 import android.telephony.ims.feature.CapabilityChangeRequest; 46 import android.telephony.ims.feature.ImsFeature; 47 import android.telephony.ims.feature.RcsFeature; 48 import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities; 49 import android.telephony.ims.stub.ImsRegistrationImplBase; 50 import android.util.Log; 51 52 import com.android.ims.internal.IImsServiceFeatureCallback; 53 import com.android.internal.annotations.VisibleForTesting; 54 import com.android.telephony.Rlog; 55 56 import java.util.ArrayList; 57 import java.util.List; 58 import java.util.Set; 59 import java.util.concurrent.CopyOnWriteArraySet; 60 import java.util.concurrent.CountDownLatch; 61 import java.util.concurrent.Executor; 62 import java.util.concurrent.atomic.AtomicReference; 63 import java.util.function.Consumer; 64 65 /** 66 * Encapsulates all logic related to the RcsFeature: 67 * - Updating RcsFeature capabilities. 68 * - Registering/Unregistering availability/registration callbacks. 69 * - Querying Registration and Capability information. 70 */ 71 public class RcsFeatureManager implements FeatureUpdates { 72 private static final String TAG = "RcsFeatureManager"; 73 private static boolean DBG = true; 74 75 private static final int CAPABILITY_OPTIONS = RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE; 76 private static final int CAPABILITY_PRESENCE = RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE; 77 78 /** 79 * The capability exchange event callbacks from the RcsFeature. 80 */ 81 public interface CapabilityExchangeEventCallback { 82 /** 83 * Triggered by RcsFeature to publish the device's capabilities to the network. 84 */ onRequestPublishCapabilities(@tackPublishTriggerType int publishTriggerType)85 void onRequestPublishCapabilities(@StackPublishTriggerType int publishTriggerType); 86 87 /** 88 * Notify that the devices is unpublished. 89 */ onUnpublish()90 void onUnpublish(); 91 92 /** 93 * Receive a capabilities request from the remote client. 94 */ onRemoteCapabilityRequest(Uri contactUri, List<String> remoteCapabilities, IOptionsRequestCallback cb)95 void onRemoteCapabilityRequest(Uri contactUri, 96 List<String> remoteCapabilities, IOptionsRequestCallback cb); 97 } 98 99 /* 100 * Setup the listener to listen to the requests and updates from ImsService. 101 */ 102 private ICapabilityExchangeEventListener mCapabilityEventListener = 103 new ICapabilityExchangeEventListener.Stub() { 104 @Override 105 public void onRequestPublishCapabilities(@StackPublishTriggerType int type) { 106 mCapabilityEventCallback.forEach( 107 callback -> callback.onRequestPublishCapabilities(type)); 108 } 109 110 @Override 111 public void onUnpublish() { 112 mCapabilityEventCallback.forEach(callback -> callback.onUnpublish()); 113 } 114 115 @Override 116 public void onRemoteCapabilityRequest(Uri contactUri, 117 List<String> remoteCapabilities, IOptionsRequestCallback cb) { 118 mCapabilityEventCallback.forEach( 119 callback -> callback.onRemoteCapabilityRequest( 120 contactUri, remoteCapabilities, cb)); 121 } 122 }; 123 124 private final int mSlotId; 125 private final Context mContext; 126 private final Set<CapabilityExchangeEventCallback> mCapabilityEventCallback 127 = new CopyOnWriteArraySet<>(); 128 private final BinderCacheManager<IImsRcsController> mBinderCache 129 = new BinderCacheManager<>(RcsFeatureManager::getIImsRcsControllerInterface); 130 131 @VisibleForTesting 132 public RcsFeatureConnection mRcsFeatureConnection; 133 134 /** 135 * Use to obtain a FeatureConnector, which will maintain a consistent listener to the 136 * RcsFeature attached to the specified slotId. If the RcsFeature changes (due to things like 137 * SIM swap), a new RcsFeatureManager will be delivered to this Listener. 138 * @param context The Context this connector should use. 139 * @param slotId The slotId associated with the Listener and requested RcsFeature 140 * @param listener The listener, which will be used to generate RcsFeatureManager instances. 141 * @param executor The executor that the Listener callbacks will be called on. 142 * @param logPrefix The prefix used in logging of the FeatureConnector for notable events. 143 * @return A FeatureConnector, which will start delivering RcsFeatureManagers as the underlying 144 * RcsFeature instances become available to the platform. 145 * @see {@link FeatureConnector#connect()}. 146 */ getConnector(Context context, int slotId, FeatureConnector.Listener<RcsFeatureManager> listener, Executor executor, String logPrefix)147 public static FeatureConnector<RcsFeatureManager> getConnector(Context context, int slotId, 148 FeatureConnector.Listener<RcsFeatureManager> listener, Executor executor, 149 String logPrefix) { 150 ArrayList<Integer> filter = new ArrayList<>(); 151 filter.add(ImsFeature.STATE_READY); 152 return new FeatureConnector<>(context, slotId, RcsFeatureManager::new, logPrefix, filter, 153 listener, executor); 154 } 155 156 /** 157 * Use {@link #getConnector} to get an instance of this class. 158 */ RcsFeatureManager(Context context, int slotId)159 private RcsFeatureManager(Context context, int slotId) { 160 mContext = context; 161 mSlotId = slotId; 162 } 163 164 /** 165 * Opens a persistent connection to the RcsFeature. This must be called before the RcsFeature 166 * can be used to communicate. 167 */ openConnection()168 public void openConnection() throws android.telephony.ims.ImsException { 169 try { 170 mRcsFeatureConnection.setCapabilityExchangeEventListener(mCapabilityEventListener); 171 } catch (RemoteException e){ 172 throw new android.telephony.ims.ImsException("Service is not available.", 173 android.telephony.ims.ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 174 } 175 } 176 177 /** 178 * Closes the persistent connection to the RcsFeature. This must be called when this manager 179 * wishes to no longer be used to communicate with the RcsFeature. 180 */ releaseConnection()181 public void releaseConnection() { 182 try { 183 mRcsFeatureConnection.setCapabilityExchangeEventListener(null); 184 } catch (RemoteException e){ 185 // Connection may not be available at this point. 186 } 187 mRcsFeatureConnection.close(); 188 mCapabilityEventCallback.clear(); 189 } 190 191 /** 192 * Adds a callback for {@link CapabilityExchangeEventCallback}. 193 * Note: These callbacks will be sent on the binder thread used to notify the callback. 194 */ addCapabilityEventCallback(CapabilityExchangeEventCallback listener)195 public void addCapabilityEventCallback(CapabilityExchangeEventCallback listener) { 196 mCapabilityEventCallback.add(listener); 197 } 198 199 /** 200 * Removes an existing {@link CapabilityExchangeEventCallback}. 201 */ removeCapabilityEventCallback(CapabilityExchangeEventCallback listener)202 public void removeCapabilityEventCallback(CapabilityExchangeEventCallback listener) { 203 mCapabilityEventCallback.remove(listener); 204 } 205 206 /** 207 * Update the capabilities for this RcsFeature. 208 */ updateCapabilities(int newSubId)209 public void updateCapabilities(int newSubId) throws android.telephony.ims.ImsException { 210 boolean optionsSupport = isOptionsSupported(newSubId); 211 boolean presenceSupported = isPresenceSupported(newSubId); 212 213 logi("Update capabilities for slot " + mSlotId + " and sub " + newSubId + ": options=" 214 + optionsSupport+ ", presence=" + presenceSupported); 215 216 if (optionsSupport || presenceSupported) { 217 CapabilityChangeRequest request = new CapabilityChangeRequest(); 218 if (optionsSupport) { 219 addRcsUceCapability(request, CAPABILITY_OPTIONS); 220 } 221 if (presenceSupported) { 222 addRcsUceCapability(request, CAPABILITY_PRESENCE); 223 } 224 sendCapabilityChangeRequest(request); 225 } else { 226 disableAllRcsUceCapabilities(); 227 } 228 } 229 230 /** 231 * Add a {@link RegistrationManager.RegistrationCallback} callback that gets called when IMS 232 * registration has changed for a specific subscription. 233 */ registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback)234 public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) 235 throws android.telephony.ims.ImsException { 236 try { 237 mRcsFeatureConnection.addCallbackForSubscription(subId, callback); 238 } catch (IllegalStateException e) { 239 loge("registerImsRegistrationCallback error: ", e); 240 throw new android.telephony.ims.ImsException("Can not register callback", 241 android.telephony.ims.ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 242 } 243 } 244 245 /** 246 * Add a {@link RegistrationManager.RegistrationCallback} callback that gets called when IMS 247 * registration has changed, independent of the subscription it is currently on. 248 */ registerImsRegistrationCallback(IImsRegistrationCallback callback)249 public void registerImsRegistrationCallback(IImsRegistrationCallback callback) 250 throws android.telephony.ims.ImsException { 251 try { 252 mRcsFeatureConnection.addCallback(callback); 253 } catch (IllegalStateException e) { 254 loge("registerImsRegistrationCallback error: ", e); 255 throw new android.telephony.ims.ImsException("Can not register callback", 256 android.telephony.ims.ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 257 } 258 } 259 260 /** 261 * Removes a previously registered {@link RegistrationManager.RegistrationCallback} callback 262 * that is associated with a specific subscription. 263 */ unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback)264 public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) { 265 mRcsFeatureConnection.removeCallbackForSubscription(subId, callback); 266 } 267 268 /** 269 * Removes a previously registered {@link RegistrationManager.RegistrationCallback} callback 270 * that was not associated with a subscription. 271 */ unregisterImsRegistrationCallback(IImsRegistrationCallback callback)272 public void unregisterImsRegistrationCallback(IImsRegistrationCallback callback) { 273 mRcsFeatureConnection.removeCallback(callback); 274 } 275 276 /** 277 * Get the IMS RCS registration technology for this Phone, 278 * defined in {@link ImsRegistrationImplBase}. 279 */ getImsRegistrationTech(Consumer<Integer> callback)280 public void getImsRegistrationTech(Consumer<Integer> callback) { 281 try { 282 int tech = mRcsFeatureConnection.getRegistrationTech(); 283 callback.accept(tech); 284 } catch (RemoteException e) { 285 loge("getImsRegistrationTech error: ", e); 286 callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE); 287 } 288 } 289 290 /** 291 * Register an ImsCapabilityCallback with RCS service, which will provide RCS availability 292 * updates. 293 */ registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)294 public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) 295 throws android.telephony.ims.ImsException { 296 try { 297 mRcsFeatureConnection.addCallbackForSubscription(subId, callback); 298 } catch (IllegalStateException e) { 299 loge("registerRcsAvailabilityCallback: ", e); 300 throw new android.telephony.ims.ImsException("Can not register callback", 301 android.telephony.ims.ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 302 } 303 } 304 305 /** 306 * Remove an registered ImsCapabilityCallback from RCS service. 307 */ unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)308 public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) { 309 mRcsFeatureConnection.removeCallbackForSubscription(subId, callback); 310 } 311 isImsServiceCapable(@msService.ImsServiceCapability long capabilities)312 public boolean isImsServiceCapable(@ImsService.ImsServiceCapability long capabilities) 313 throws ImsException { 314 try { 315 return mRcsFeatureConnection.isCapable(capabilities); 316 } catch (RemoteException e) { 317 throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 318 } 319 } 320 321 /** 322 * @return The SipTransport interface if it exists or {@code null} if it does not exist due to 323 * the ImsService not supporting it. 324 */ getSipTransport()325 public ISipTransport getSipTransport() throws ImsException { 326 if (!isImsServiceCapable(ImsService.CAPABILITY_SIP_DELEGATE_CREATION)) { 327 return null; 328 } 329 return mRcsFeatureConnection.getSipTransport(); 330 } 331 getImsRegistration()332 public IImsRegistration getImsRegistration() { 333 return mRcsFeatureConnection.getRegistration(); 334 } 335 336 /** 337 * Query for the specific capability. 338 */ isCapable( @csImsCapabilities.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)339 public boolean isCapable( 340 @RcsImsCapabilities.RcsImsCapabilityFlag int capability, 341 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) 342 throws android.telephony.ims.ImsException { 343 CountDownLatch latch = new CountDownLatch(1); 344 AtomicReference<Boolean> capableRef = new AtomicReference<>(); 345 346 IImsCapabilityCallback callback = new IImsCapabilityCallback.Stub() { 347 @Override 348 public void onQueryCapabilityConfiguration( 349 int resultCapability, int resultRadioTech, boolean enabled) { 350 if ((capability != resultCapability) || (radioTech != resultRadioTech)) { 351 return; 352 } 353 if (DBG) log("capable result:capability=" + capability + ", enabled=" + enabled); 354 capableRef.set(enabled); 355 latch.countDown(); 356 } 357 358 @Override 359 public void onCapabilitiesStatusChanged(int config) { 360 // Don't handle it 361 } 362 363 @Override 364 public void onChangeCapabilityConfigurationError(int capability, int radioTech, 365 int reason) { 366 // Don't handle it 367 } 368 }; 369 370 try { 371 if (DBG) log("Query capability: " + capability + ", radioTech=" + radioTech); 372 mRcsFeatureConnection.queryCapabilityConfiguration(capability, radioTech, callback); 373 return awaitResult(latch, capableRef); 374 } catch (RemoteException e) { 375 loge("isCapable error: ", e); 376 throw new android.telephony.ims.ImsException("Can not determine capabilities", 377 android.telephony.ims.ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 378 } 379 } 380 awaitResult(CountDownLatch latch, AtomicReference<T> resultRef)381 private static <T> T awaitResult(CountDownLatch latch, AtomicReference<T> resultRef) { 382 try { 383 latch.await(); 384 } catch (InterruptedException e) { 385 Thread.currentThread().interrupt(); 386 } 387 return resultRef.get(); 388 } 389 390 /** 391 * Query the availability of an IMS RCS capability. 392 */ isAvailable(@csImsCapabilities.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)393 public boolean isAvailable(@RcsImsCapabilities.RcsImsCapabilityFlag int capability, 394 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) 395 throws android.telephony.ims.ImsException { 396 try { 397 if (mRcsFeatureConnection.getRegistrationTech() != radioTech) { 398 return false; 399 } 400 int currentStatus = mRcsFeatureConnection.queryCapabilityStatus(); 401 return new RcsImsCapabilities(currentStatus).isCapable(capability); 402 } catch (RemoteException e) { 403 loge("isAvailable error: ", e); 404 throw new android.telephony.ims.ImsException("Can not determine availability", 405 android.telephony.ims.ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 406 } 407 } 408 409 /** 410 * Add UCE capabilities with given type. 411 * @param capability the specific RCS UCE capability wants to enable 412 */ addRcsUceCapability(CapabilityChangeRequest request, @RcsImsCapabilities.RcsImsCapabilityFlag int capability)413 public void addRcsUceCapability(CapabilityChangeRequest request, 414 @RcsImsCapabilities.RcsImsCapabilityFlag int capability) { 415 request.addCapabilitiesToEnableForTech(capability, 416 ImsRegistrationImplBase.REGISTRATION_TECH_NR); 417 request.addCapabilitiesToEnableForTech(capability, 418 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 419 request.addCapabilitiesToEnableForTech(capability, 420 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN); 421 } 422 requestPublication(String pidfXml, IPublishResponseCallback responseCallback)423 public void requestPublication(String pidfXml, IPublishResponseCallback responseCallback) 424 throws RemoteException { 425 mRcsFeatureConnection.requestPublication(pidfXml, responseCallback); 426 } 427 requestCapabilities(List<Uri> uris, ISubscribeResponseCallback c)428 public void requestCapabilities(List<Uri> uris, ISubscribeResponseCallback c) 429 throws RemoteException { 430 mRcsFeatureConnection.requestCapabilities(uris, c); 431 } 432 sendOptionsCapabilityRequest(Uri contactUri, List<String> myCapabilities, IOptionsResponseCallback callback)433 public void sendOptionsCapabilityRequest(Uri contactUri, List<String> myCapabilities, 434 IOptionsResponseCallback callback) throws RemoteException { 435 mRcsFeatureConnection.sendOptionsCapabilityRequest(contactUri, myCapabilities, callback); 436 } 437 438 /** 439 * Disable all of the UCE capabilities. 440 */ disableAllRcsUceCapabilities()441 private void disableAllRcsUceCapabilities() throws android.telephony.ims.ImsException { 442 final int techNr = ImsRegistrationImplBase.REGISTRATION_TECH_NR; 443 final int techLte = ImsRegistrationImplBase.REGISTRATION_TECH_LTE; 444 final int techIWlan = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN; 445 CapabilityChangeRequest request = new CapabilityChangeRequest(); 446 request.addCapabilitiesToDisableForTech(CAPABILITY_OPTIONS, techNr); 447 request.addCapabilitiesToDisableForTech(CAPABILITY_OPTIONS, techLte); 448 request.addCapabilitiesToDisableForTech(CAPABILITY_OPTIONS, techIWlan); 449 request.addCapabilitiesToDisableForTech(CAPABILITY_PRESENCE, techNr); 450 request.addCapabilitiesToDisableForTech(CAPABILITY_PRESENCE, techLte); 451 request.addCapabilitiesToDisableForTech(CAPABILITY_PRESENCE, techIWlan); 452 sendCapabilityChangeRequest(request); 453 } 454 sendCapabilityChangeRequest(CapabilityChangeRequest request)455 private void sendCapabilityChangeRequest(CapabilityChangeRequest request) 456 throws android.telephony.ims.ImsException { 457 try { 458 if (DBG) log("sendCapabilityChangeRequest: " + request); 459 mRcsFeatureConnection.changeEnabledCapabilities(request, null); 460 } catch (RemoteException e) { 461 throw new android.telephony.ims.ImsException("Can not connect to service", 462 android.telephony.ims.ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 463 } 464 } 465 isOptionsSupported(int subId)466 private boolean isOptionsSupported(int subId) { 467 return isCapabilityTypeSupported(mContext, subId, CAPABILITY_OPTIONS); 468 } 469 isPresenceSupported(int subId)470 private boolean isPresenceSupported(int subId) { 471 return isCapabilityTypeSupported(mContext, subId, CAPABILITY_PRESENCE); 472 } 473 474 /* 475 * Check if the given type of capability is supported. 476 */ isCapabilityTypeSupported( Context context, int subId, int capabilityType)477 private static boolean isCapabilityTypeSupported( 478 Context context, int subId, int capabilityType) { 479 480 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 481 Log.e(TAG, "isCapabilityTypeSupported: Invalid subId=" + subId); 482 return false; 483 } 484 485 CarrierConfigManager configManager = 486 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 487 if (configManager == null) { 488 Log.e(TAG, "isCapabilityTypeSupported: CarrierConfigManager is null, " + subId); 489 return false; 490 } 491 492 PersistableBundle b = configManager.getConfigForSubId(subId); 493 if (b == null) { 494 Log.e(TAG, "isCapabilityTypeSupported: PersistableBundle is null, " + subId); 495 return false; 496 } 497 498 if (capabilityType == CAPABILITY_OPTIONS) { 499 return b.getBoolean(CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, false); 500 } else if (capabilityType == CAPABILITY_PRESENCE) { 501 return b.getBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false); 502 } 503 return false; 504 } 505 506 @Override registerFeatureCallback(int slotId, IImsServiceFeatureCallback cb)507 public void registerFeatureCallback(int slotId, IImsServiceFeatureCallback cb) { 508 IImsRcsController controller = mBinderCache.listenOnBinder(cb, () -> { 509 try { 510 cb.imsFeatureRemoved( 511 FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE); 512 } catch (RemoteException ignore) {} // This is local. 513 }); 514 515 try { 516 if (controller == null) { 517 Log.e(TAG, "registerRcsFeatureListener: IImsRcsController is null"); 518 cb.imsFeatureRemoved(FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE); 519 return; 520 } 521 controller.registerRcsFeatureCallback(slotId, cb); 522 } catch (ServiceSpecificException e) { 523 try { 524 switch (e.errorCode) { 525 case ImsException.CODE_ERROR_UNSUPPORTED_OPERATION: 526 cb.imsFeatureRemoved(FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED); 527 break; 528 default: { 529 cb.imsFeatureRemoved( 530 FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE); 531 } 532 } 533 } catch (RemoteException ignore) {} // Already dead anyway if this happens. 534 } catch (RemoteException e) { 535 try { 536 cb.imsFeatureRemoved(FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE); 537 } catch (RemoteException ignore) {} // Already dead if this happens. 538 } 539 } 540 541 @Override unregisterFeatureCallback(IImsServiceFeatureCallback cb)542 public void unregisterFeatureCallback(IImsServiceFeatureCallback cb) { 543 try { 544 IImsRcsController imsRcsController = mBinderCache.removeRunnable(cb); 545 if (imsRcsController != null) { 546 imsRcsController.unregisterImsFeatureCallback(cb); 547 } 548 } catch (RemoteException e) { 549 // This means that telephony died, so do not worry about it. 550 Rlog.e(TAG, "unregisterImsFeatureCallback (RCS), RemoteException: " 551 + e.getMessage()); 552 } 553 } 554 getIImsRcsController()555 private IImsRcsController getIImsRcsController() { 556 return mBinderCache.getBinder(); 557 } 558 getIImsRcsControllerInterface()559 private static IImsRcsController getIImsRcsControllerInterface() { 560 IBinder binder = TelephonyFrameworkInitializer 561 .getTelephonyServiceManager() 562 .getTelephonyImsServiceRegisterer() 563 .get(); 564 IImsRcsController c = IImsRcsController.Stub.asInterface(binder); 565 return c; 566 } 567 568 @Override associate(ImsFeatureContainer c)569 public void associate(ImsFeatureContainer c) { 570 IImsRcsFeature f = IImsRcsFeature.Stub.asInterface(c.imsFeature); 571 mRcsFeatureConnection = new RcsFeatureConnection(mContext, mSlotId, f, c.imsConfig, 572 c.imsRegistration, c.sipTransport); 573 } 574 575 @Override invalidate()576 public void invalidate() { 577 mRcsFeatureConnection.onRemovedOrDied(); 578 } 579 580 @Override updateFeatureState(int state)581 public void updateFeatureState(int state) { 582 mRcsFeatureConnection.updateFeatureState(state); 583 } 584 585 @Override updateFeatureCapabilities(long capabilities)586 public void updateFeatureCapabilities(long capabilities) { 587 mRcsFeatureConnection.updateFeatureCapabilities(capabilities); 588 } 589 590 /** 591 * Testing interface used to mock SubscriptionManager in testing 592 * @hide 593 */ 594 @VisibleForTesting 595 public interface SubscriptionManagerProxy { 596 /** 597 * Mock-able interface for {@link SubscriptionManager#getSubId(int)} used for testing. 598 */ getSubId(int slotId)599 int getSubId(int slotId); 600 } 601 getConfig()602 public IImsConfig getConfig() { 603 return mRcsFeatureConnection.getConfig(); 604 } 605 606 private static SubscriptionManagerProxy sSubscriptionManagerProxy 607 = slotId -> { 608 int[] subIds = SubscriptionManager.getSubId(slotId); 609 if (subIds != null) { 610 return subIds[0]; 611 } 612 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 613 }; 614 615 /** 616 * Testing function used to mock SubscriptionManager in testing 617 * @hide 618 */ 619 @VisibleForTesting setSubscriptionManager(SubscriptionManagerProxy proxy)620 public static void setSubscriptionManager(SubscriptionManagerProxy proxy) { 621 sSubscriptionManagerProxy = proxy; 622 } 623 log(String s)624 private void log(String s) { 625 Rlog.d(TAG + " [" + mSlotId + "]", s); 626 } 627 logi(String s)628 private void logi(String s) { 629 Rlog.i(TAG + " [" + mSlotId + "]", s); 630 } 631 loge(String s)632 private void loge(String s) { 633 Rlog.e(TAG + " [" + mSlotId + "]", s); 634 } 635 loge(String s, Throwable t)636 private void loge(String s, Throwable t) { 637 Rlog.e(TAG + " [" + mSlotId + "]", s, t); 638 } 639 } 640