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