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