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