1 /*
2  * Copyright (C) 2017 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.os.Handler;
23 import android.os.HandlerExecutor;
24 import android.os.IBinder;
25 import android.os.IInterface;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.os.RemoteCallbackList;
29 import android.os.RemoteException;
30 import android.telephony.Rlog;
31 import android.telephony.SubscriptionInfo;
32 import android.telephony.SubscriptionManager;
33 import android.telephony.TelephonyManager;
34 import android.telephony.ims.ImsCallProfile;
35 import android.telephony.ims.aidl.IImsCapabilityCallback;
36 import android.telephony.ims.aidl.IImsConfig;
37 import android.telephony.ims.aidl.IImsConfigCallback;
38 import android.telephony.ims.aidl.IImsMmTelFeature;
39 import android.telephony.ims.aidl.IImsRegistration;
40 import android.telephony.ims.aidl.IImsRegistrationCallback;
41 import android.telephony.ims.aidl.IImsSmsListener;
42 import android.telephony.ims.feature.CapabilityChangeRequest;
43 import android.telephony.ims.feature.ImsFeature;
44 import android.telephony.ims.feature.MmTelFeature;
45 import android.telephony.ims.stub.ImsRegistrationImplBase;
46 import android.telephony.ims.stub.ImsSmsImplBase;
47 import android.util.ArraySet;
48 import android.util.Log;
49 import android.util.SparseArray;
50 
51 import com.android.ims.internal.IImsCallSession;
52 import com.android.ims.internal.IImsEcbm;
53 import com.android.ims.internal.IImsMultiEndpoint;
54 import com.android.ims.internal.IImsServiceFeatureCallback;
55 import com.android.ims.internal.IImsUt;
56 import com.android.internal.annotations.VisibleForTesting;
57 
58 import java.util.ArrayList;
59 import java.util.Collections;
60 import java.util.List;
61 import java.util.Set;
62 import java.util.concurrent.Executor;
63 import java.util.stream.Collectors;
64 
65 /**
66  * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
67  * the platform currently supports: MMTel and RCS.
68  * @hide
69  */
70 
71 public class MmTelFeatureConnection {
72     protected static final String TAG = "MmTelFeatureConnection";
73 
74     // Manages callbacks to the associated MmTelFeature in mMmTelFeatureConnection.
75     @VisibleForTesting
76     public static abstract class CallbackAdapterManager<T extends IInterface> {
77         private static final String TAG = "CallbackAdapterManager";
78 
79         private final Context mContext;
80         private final Object mLock;
81         // Map of sub id -> List<callbacks> for sub id linked callbacks.
82         private final SparseArray<Set<T>> mCallbackSubscriptionMap = new SparseArray<>();
83         // List of all active callbacks to ImsService
84         private final RemoteCallbackList<T> mRemoteCallbacks = new RemoteCallbackList<>();
85         @VisibleForTesting
86         public SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
87 
CallbackAdapterManager(Context context, Object lock)88         public CallbackAdapterManager(Context context, Object lock) {
89             mContext = context;
90             mLock = lock;
91             if (Looper.myLooper() == null) {
92                 Looper.prepare();
93             }
94             // Must be created after Looper.prepare() is called, or else we will get an exception.
95             mSubChangedListener = new SubscriptionManager.OnSubscriptionsChangedListener() {
96                 @Override
97                 public void onSubscriptionsChanged() {
98                     SubscriptionManager manager = mContext.getSystemService(
99                             SubscriptionManager.class);
100                     if (manager == null) {
101                         Log.w(TAG, "onSubscriptionsChanged: could not find SubscriptionManager.");
102                         return;
103                     }
104                     List<SubscriptionInfo> subInfos = manager.getActiveSubscriptionInfoList(false);
105                     if (subInfos == null) {
106                         subInfos = Collections.emptyList();
107                     }
108                     Set<Integer> newSubIds = subInfos.stream()
109                             .map(SubscriptionInfo::getSubscriptionId)
110                             .collect(Collectors.toSet());
111                     synchronized (mLock) {
112                         Set<Integer> storedSubIds = new ArraySet<>(mCallbackSubscriptionMap.size());
113                         for (int keyIndex = 0; keyIndex < mCallbackSubscriptionMap.size();
114                                 keyIndex++) {
115                             storedSubIds.add(mCallbackSubscriptionMap.keyAt(keyIndex));
116                         }
117                         // Get the set of sub ids that are in storedSubIds that are not in newSubIds.
118                         // This is the set of sub ids that need to be removed.
119                         storedSubIds.removeAll(newSubIds);
120                         for (Integer subId : storedSubIds) {
121                             removeCallbacksForSubscription(subId);
122                         }
123                     }
124                 }
125             };
126 
127         }
128 
129         // Add a callback to the MmTelFeature associated with this manager (independent of the)
130         // current subscription.
addCallback(T localCallback)131         public final void addCallback(T localCallback) {
132             synchronized (mLock) {
133                 // Skip registering to callback subscription map here, because we are registering
134                 // for the slot, independent of subscription (deprecated behavior).
135                 // Throws a IllegalStateException if this registration fails.
136                 registerCallback(localCallback);
137                 Log.i(TAG, "Local callback added: " + localCallback);
138                 mRemoteCallbacks.register(localCallback);
139             }
140         }
141 
142         // Add a callback to be associated with a subscription. If that subscription is removed,
143         // remove the callback and notify the callback that the subscription has been removed.
addCallbackForSubscription(T localCallback, int subId)144         public final void addCallbackForSubscription(T localCallback, int subId) {
145             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
146                 return;
147             }
148             synchronized (mLock) {
149                 addCallback(localCallback);
150                 linkCallbackToSubscription(localCallback, subId);
151             }
152         }
153 
154         // Removes a callback associated with the MmTelFeature.
removeCallback(T localCallback)155         public final void removeCallback(T localCallback) {
156             Log.i(TAG, "Local callback removed: " + localCallback);
157             synchronized (mLock) {
158                 if (mRemoteCallbacks.unregister(localCallback)) {
159                     // Will only occur if we have record of this callback in mRemoteCallbacks.
160                     unregisterCallback(localCallback);
161                 }
162             }
163         }
164 
165         // Remove an existing callback that has been linked to a subscription.
removeCallbackForSubscription(T localCallback, int subId)166         public final void removeCallbackForSubscription(T localCallback, int subId) {
167             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
168                 return;
169             }
170             synchronized (mLock) {
171                 removeCallback(localCallback);
172                 unlinkCallbackFromSubscription(localCallback, subId);
173             }
174         }
175 
176         // Links a callback to be tracked by a subscription. If it goes away, emove.
linkCallbackToSubscription(T callback, int subId)177         private void linkCallbackToSubscription(T callback, int subId) {
178             synchronized (mLock) {
179                 if (mCallbackSubscriptionMap.size() == 0) {
180                     // we are about to add the first entry to the map, register for subscriptions
181                     //changed listener.
182                     registerForSubscriptionsChanged();
183                 }
184                 Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId);
185                 if (callbacksPerSub == null) {
186                     // the callback list has not been created yet for this subscription.
187                     callbacksPerSub = new ArraySet<>();
188                     mCallbackSubscriptionMap.put(subId, callbacksPerSub);
189                 }
190                 callbacksPerSub.add(callback);
191             }
192         }
193 
194         // Unlink the callback from the associated subscription.
unlinkCallbackFromSubscription(T callback, int subId)195         private void unlinkCallbackFromSubscription(T callback, int subId) {
196             synchronized (mLock) {
197                 Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId);
198                 if (callbacksPerSub != null) {
199                     callbacksPerSub.remove(callback);
200                     if (callbacksPerSub.isEmpty()) {
201                         mCallbackSubscriptionMap.remove(subId);
202                     }
203                 }
204                 if (mCallbackSubscriptionMap.size() == 0) {
205                     unregisterForSubscriptionsChanged();
206                 }
207             }
208         }
209 
210         // Removes all of the callbacks that have been registered to the subscription specified.
211         // This happens when Telephony sends an indication that the subscriptions have changed.
removeCallbacksForSubscription(int subId)212         private void removeCallbacksForSubscription(int subId) {
213             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
214                 return;
215             }
216             synchronized (mLock) {
217                 Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId);
218                 if (callbacksPerSub == null) {
219                     // no callbacks registered for this subscription.
220                     return;
221                 }
222                 // clear all registered callbacks in the subscription map for this subscription.
223                 mCallbackSubscriptionMap.remove(subId);
224                 for (T callback : callbacksPerSub) {
225                     removeCallback(callback);
226                 }
227                 // If there are no more callbacks being tracked, remove subscriptions changed
228                 // listener.
229                 if (mCallbackSubscriptionMap.size() == 0) {
230                     unregisterForSubscriptionsChanged();
231                 }
232             }
233         }
234 
235         // Clear the Subscription -> Callback map because the ImsService connection is no longer
236         // current.
clearCallbacksForAllSubscriptions()237         private void clearCallbacksForAllSubscriptions() {
238             synchronized (mLock) {
239                 List<Integer> keys = new ArrayList<>();
240                 for (int keyIndex = 0; keyIndex < mCallbackSubscriptionMap.size(); keyIndex++) {
241                     keys.add(mCallbackSubscriptionMap.keyAt(keyIndex));
242                 }
243                 keys.forEach(this::removeCallbacksForSubscription);
244             }
245         }
246 
registerForSubscriptionsChanged()247         private void registerForSubscriptionsChanged() {
248             SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
249             if (manager != null) {
250                 manager.addOnSubscriptionsChangedListener(mSubChangedListener);
251             } else {
252                 Log.w(TAG, "registerForSubscriptionsChanged: could not find SubscriptionManager.");
253             }
254         }
255 
unregisterForSubscriptionsChanged()256         private void unregisterForSubscriptionsChanged() {
257             SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
258             if (manager != null) {
259             manager.removeOnSubscriptionsChangedListener(mSubChangedListener);
260             } else {
261                 Log.w(TAG, "unregisterForSubscriptionsChanged: could not find"
262                         + " SubscriptionManager.");
263             }
264         }
265 
266         // The ImsService these callbacks are registered to has become unavailable or crashed, or
267         // the ImsResolver has switched to a new ImsService. In these cases, clean up all existing
268         // callbacks.
close()269         public final void close() {
270             synchronized (mLock) {
271                 final int lastCallbackIndex = mRemoteCallbacks.getRegisteredCallbackCount() - 1;
272                 for(int ii = lastCallbackIndex; ii >= 0; ii --) {
273                     T callbackItem = mRemoteCallbacks.getRegisteredCallbackItem(ii);
274                     unregisterCallback(callbackItem);
275                     mRemoteCallbacks.unregister(callbackItem);
276                 }
277                 clearCallbacksForAllSubscriptions();
278                 Log.i(TAG, "Closing connection and clearing callbacks");
279             }
280         }
281 
282         // A callback has been registered. Register that callback with the MmTelFeature.
registerCallback(T localCallback)283         public abstract void registerCallback(T localCallback);
284 
285         // A callback has been removed, unregister that callback with the MmTelFeature.
unregisterCallback(T localCallback)286         public abstract void unregisterCallback(T localCallback);
287     }
288 
289     private class ImsRegistrationCallbackAdapter extends
290             CallbackAdapterManager<IImsRegistrationCallback> {
291 
ImsRegistrationCallbackAdapter(Context context, Object lock)292         public ImsRegistrationCallbackAdapter(Context context, Object lock) {
293             super(context, lock);
294         }
295 
296         @Override
registerCallback(IImsRegistrationCallback localCallback)297         public void registerCallback(IImsRegistrationCallback localCallback) {
298             IImsRegistration imsRegistration = getRegistration();
299             if (imsRegistration != null) {
300                 try {
301                     imsRegistration.addRegistrationCallback(localCallback);
302                 } catch (RemoteException e) {
303                     throw new IllegalStateException("ImsRegistrationCallbackAdapter: MmTelFeature"
304                             + " binder is dead.");
305                 }
306             } else {
307                 Log.e(TAG, "ImsRegistrationCallbackAdapter: ImsRegistration is null");
308                 throw new IllegalStateException("ImsRegistrationCallbackAdapter: MmTelFeature is"
309                         + "not available!");
310             }
311         }
312 
313         @Override
unregisterCallback(IImsRegistrationCallback localCallback)314         public void unregisterCallback(IImsRegistrationCallback localCallback) {
315             IImsRegistration imsRegistration = getRegistration();
316             if (imsRegistration != null) {
317                 try {
318                     imsRegistration.removeRegistrationCallback(localCallback);
319                 } catch (RemoteException e) {
320                     Log.w(TAG, "ImsRegistrationCallbackAdapter - unregisterCallback: couldn't"
321                             + " remove registration callback");
322                 }
323             } else {
324                 Log.e(TAG, "ImsRegistrationCallbackAdapter: ImsRegistration is null");
325             }
326         }
327     }
328 
329     private class CapabilityCallbackManager extends CallbackAdapterManager<IImsCapabilityCallback> {
330 
CapabilityCallbackManager(Context context, Object lock)331         public CapabilityCallbackManager(Context context, Object lock) {
332             super(context, lock);
333         }
334 
335         @Override
registerCallback(IImsCapabilityCallback localCallback)336         public void registerCallback(IImsCapabilityCallback localCallback) {
337             IImsMmTelFeature binder;
338             synchronized (mLock) {
339                 try {
340                     checkServiceIsReady();
341                     binder = getServiceInterface(mBinder);
342                 } catch (RemoteException e) {
343                     throw new IllegalStateException("CapabilityCallbackManager - MmTelFeature"
344                             + " binder is dead.");
345                 }
346             }
347             if (binder != null) {
348                 try {
349                 binder.addCapabilityCallback(localCallback);
350                 } catch (RemoteException e) {
351                     throw new IllegalStateException(" CapabilityCallbackManager - MmTelFeature"
352                             + " binder is null.");
353                 }
354             } else {
355                 Log.w(TAG, "CapabilityCallbackManager, register: Couldn't get binder");
356                 throw new IllegalStateException("CapabilityCallbackManager: MmTelFeature is"
357                         + " not available!");
358             }
359         }
360 
361         @Override
unregisterCallback(IImsCapabilityCallback localCallback)362         public void unregisterCallback(IImsCapabilityCallback localCallback) {
363             IImsMmTelFeature binder;
364             synchronized (mLock) {
365                 try {
366                     checkServiceIsReady();
367                     binder = getServiceInterface(mBinder);
368                 } catch (RemoteException e) {
369                     // binder is null
370                     Log.w(TAG, "CapabilityCallbackManager, unregister: couldn't get binder.");
371                     return;
372                 }
373             }
374             if (binder != null) {
375                 try {
376                     binder.removeCapabilityCallback(localCallback);
377                 } catch (RemoteException e) {
378                     Log.w(TAG, "CapabilityCallbackManager, unregister: Binder is dead.");
379                 }
380             } else {
381                 Log.w(TAG, "CapabilityCallbackManager, unregister: binder is null.");
382             }
383         }
384     }
385 
386     private class ProvisioningCallbackManager extends CallbackAdapterManager<IImsConfigCallback> {
ProvisioningCallbackManager(Context context, Object lock)387         public ProvisioningCallbackManager (Context context, Object lock) {
388             super(context, lock);
389         }
390 
391         @Override
registerCallback(IImsConfigCallback localCallback)392         public void registerCallback(IImsConfigCallback localCallback) {
393             IImsConfig binder = getConfigInterface();
394             if (binder == null) {
395                 // Config interface is not currently available.
396                 Log.w(TAG, "ProvisioningCallbackManager - couldn't register, binder is null.");
397                 throw new IllegalStateException("ImsConfig is not available!");
398             }
399             try {
400                 binder.addImsConfigCallback(localCallback);
401             }catch (RemoteException e) {
402                 throw new IllegalStateException("ImsService is not available!");
403             }
404         }
405 
406         @Override
unregisterCallback(IImsConfigCallback localCallback)407         public void unregisterCallback(IImsConfigCallback localCallback) {
408             IImsConfig binder = getConfigInterface();
409             if (binder == null) {
410                 Log.w(TAG, "ProvisioningCallbackManager - couldn't unregister, binder is null.");
411                 return;
412             }
413             try {
414                 binder.removeImsConfigCallback(localCallback);
415             } catch (RemoteException e) {
416                 Log.w(TAG, "ProvisioningCallbackManager - couldn't unregister, binder is dead.");
417             }
418         }
419     }
420 
421     protected final int mSlotId;
422     protected IBinder mBinder;
423     private Context mContext;
424     private Executor mExecutor;
425 
426     private volatile boolean mIsAvailable = false;
427     // ImsFeature Status from the ImsService. Cached.
428     private Integer mFeatureStateCached = null;
429     private IFeatureUpdate mStatusCallback;
430     private final Object mLock = new Object();
431     // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent.
432     private boolean mSupportsEmergencyCalling = false;
433     private static boolean sImsSupportedOnDevice = true;
434 
435     // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If
436     // it becomes disconnected, invalidate.
437     private IImsRegistration mRegistrationBinder;
438     private IImsConfig mConfigBinder;
439 
440     private final IBinder.DeathRecipient mDeathRecipient = () -> {
441         Log.w(TAG, "DeathRecipient triggered, binder died.");
442         if (mContext != null && Looper.getMainLooper() != null) {
443             // Move this signal to the main thread, notifying ImsManager of the Binder
444             // death on another thread may lead to deadlocks.
445             mContext.getMainExecutor().execute(this::onRemovedOrDied);
446             return;
447         }
448         // No choice - execute on the current Binder thread.
449         onRemovedOrDied();
450     };
451 
452     private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager;
453     private final CapabilityCallbackManager mCapabilityCallbackManager;
454     private final ProvisioningCallbackManager mProvisioningCallbackManager;
455 
create(Context context , int slotId)456     public static @NonNull MmTelFeatureConnection create(Context context , int slotId) {
457         MmTelFeatureConnection serviceProxy = new MmTelFeatureConnection(context, slotId);
458         if (!ImsManager.isImsSupportedOnDevice(context)) {
459             // Return empty service proxy in the case that IMS is not supported.
460             sImsSupportedOnDevice = false;
461             return serviceProxy;
462         }
463 
464         TelephonyManager tm  = getTelephonyManager(context);
465         if (tm == null) {
466             Rlog.w(TAG, "create: TelephonyManager is null!");
467             // Binder can be unset in this case because it will be torn down/recreated as part of
468             // a retry mechanism until the serviceProxy binder is set successfully.
469             return serviceProxy;
470         }
471 
472         IImsMmTelFeature binder = tm.getImsMmTelFeatureAndListen(slotId,
473                 serviceProxy.getListener());
474         if (binder != null) {
475             serviceProxy.setBinder(binder.asBinder());
476             // Trigger the cache to be updated for feature status.
477             serviceProxy.getFeatureState();
478         } else {
479             Rlog.w(TAG, "create: binder is null! Slot Id: " + slotId);
480         }
481         return serviceProxy;
482     }
483 
getTelephonyManager(Context context)484     public static TelephonyManager getTelephonyManager(Context context) {
485         return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
486     }
487 
488     public interface IFeatureUpdate {
489         /**
490          * Called when the ImsFeature has changed its state. Use
491          * {@link ImsFeature#getFeatureState()} to get the new state.
492          */
notifyStateChanged()493         void notifyStateChanged();
494 
495         /**
496          * Called when the ImsFeature has become unavailable due to the binder switching or app
497          * crashing. A new ImsServiceProxy should be requested for that feature.
498          */
notifyUnavailable()499         void notifyUnavailable();
500     }
501 
502     private final IImsServiceFeatureCallback mListenerBinder =
503             new IImsServiceFeatureCallback.Stub() {
504 
505         @Override
506         public void imsFeatureCreated(int slotId, int feature) {
507                 mExecutor.execute(() -> {
508                 // The feature has been enabled. This happens when the feature is first created and
509                 // may happen when the feature is re-enabled.
510                 synchronized (mLock) {
511                     if(mSlotId != slotId) {
512                         return;
513                     }
514                     switch (feature) {
515                         case ImsFeature.FEATURE_MMTEL: {
516                             if (!mIsAvailable) {
517                                 Log.i(TAG, "MmTel enabled on slotId: " + slotId);
518                                 mIsAvailable = true;
519                             }
520                             break;
521                         }
522                         case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
523                             mSupportsEmergencyCalling = true;
524                             Log.i(TAG, "Emergency calling enabled on slotId: " + slotId);
525                             break;
526                         }
527                     }
528                 }
529             });
530         }
531 
532         @Override
533         public void imsFeatureRemoved(int slotId, int feature) {
534             mExecutor.execute(() -> {
535                 synchronized (mLock) {
536                     if (mSlotId != slotId) {
537                         return;
538                     }
539                     switch (feature) {
540                         case ImsFeature.FEATURE_MMTEL: {
541                             Log.i(TAG, "MmTel removed on slotId: " + slotId);
542                             onRemovedOrDied();
543                             break;
544                         }
545                         case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
546                             mSupportsEmergencyCalling = false;
547                             Log.i(TAG, "Emergency calling disabled on slotId: " + slotId);
548                             break;
549                         }
550                     }
551                 }
552             });
553         }
554 
555         @Override
556         public void imsStatusChanged(int slotId, int feature, int status) {
557             mExecutor.execute(() -> {
558                 synchronized (mLock) {
559                     Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
560                             " status: " + status);
561                     if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
562                         mFeatureStateCached = status;
563                         if (mStatusCallback != null) {
564                             mStatusCallback.notifyStateChanged();
565                         }
566                     }
567                 }
568             });
569         }
570     };
571 
MmTelFeatureConnection(Context context, int slotId)572     public MmTelFeatureConnection(Context context, int slotId) {
573         mSlotId = slotId;
574         mContext = context;
575         // Callbacks should be scheduled on the main thread.
576         if (context.getMainLooper() != null) {
577             mExecutor = context.getMainExecutor();
578         } else {
579             // Fallback to the current thread.
580             if (Looper.myLooper() == null) {
581                 Looper.prepare();
582             }
583             mExecutor = new HandlerExecutor(new Handler(Looper.myLooper()));
584         }
585         mRegistrationCallbackManager = new ImsRegistrationCallbackAdapter(context, mLock);
586         mCapabilityCallbackManager = new CapabilityCallbackManager(context, mLock);
587         mProvisioningCallbackManager = new ProvisioningCallbackManager(context, mLock);
588     }
589 
590     /**
591      * Called when the MmTelFeature has either been removed by Telephony or crashed.
592      */
onRemovedOrDied()593     private void onRemovedOrDied() {
594         synchronized (mLock) {
595             mRegistrationCallbackManager.close();
596             mCapabilityCallbackManager.close();
597             mProvisioningCallbackManager.close();
598             if (mIsAvailable) {
599                 mIsAvailable = false;
600                 // invalidate caches.
601                 mRegistrationBinder = null;
602                 mConfigBinder = null;
603                 if (mBinder != null) {
604                     mBinder.unlinkToDeath(mDeathRecipient, 0);
605                 }
606                 if (mStatusCallback != null) {
607                     mStatusCallback.notifyUnavailable();
608                 }
609             }
610         }
611     }
612 
getRegistration()613     private @Nullable IImsRegistration getRegistration() {
614         synchronized (mLock) {
615             // null if cache is invalid;
616             if (mRegistrationBinder != null) {
617                 return mRegistrationBinder;
618             }
619         }
620         TelephonyManager tm = getTelephonyManager(mContext);
621         // We don't want to synchronize on a binder call to another process.
622         IImsRegistration regBinder = tm != null
623                 ? tm.getImsRegistration(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
624         synchronized (mLock) {
625             // mRegistrationBinder may have changed while we tried to get the registration
626             // interface.
627             if (mRegistrationBinder == null) {
628                 mRegistrationBinder = regBinder;
629             }
630         }
631         return mRegistrationBinder;
632     }
633 
getConfig()634     private IImsConfig getConfig() {
635         synchronized (mLock) {
636             // null if cache is invalid;
637             if (mConfigBinder != null) {
638                 return mConfigBinder;
639             }
640         }
641         TelephonyManager tm = getTelephonyManager(mContext);
642         IImsConfig configBinder = tm != null
643                 ? tm.getImsConfig(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
644         synchronized (mLock) {
645             // mConfigBinder may have changed while we tried to get the config interface.
646             if (mConfigBinder == null) {
647                 mConfigBinder = configBinder;
648             }
649         }
650         return mConfigBinder;
651     }
652 
isEmergencyMmTelAvailable()653     public boolean isEmergencyMmTelAvailable() {
654         return mSupportsEmergencyCalling;
655     }
656 
getListener()657     public IImsServiceFeatureCallback getListener() {
658         return mListenerBinder;
659     }
660 
setBinder(IBinder binder)661     public void setBinder(IBinder binder) {
662         synchronized (mLock) {
663             mBinder = binder;
664             try {
665                 if (mBinder != null) {
666                     mBinder.linkToDeath(mDeathRecipient, 0);
667                 }
668             } catch (RemoteException e) {
669                 // No need to do anything if the binder is already dead.
670             }
671         }
672     }
673 
674     /**
675      * Opens the connection to the {@link MmTelFeature} and establishes a listener back to the
676      * framework. Calling this method multiple times will reset the listener attached to the
677      * {@link MmTelFeature}.
678      * @param listener A {@link MmTelFeature.Listener} that will be used by the {@link MmTelFeature}
679      * to notify the framework of updates.
680      */
openConnection(MmTelFeature.Listener listener)681     public void openConnection(MmTelFeature.Listener listener) throws RemoteException {
682         synchronized (mLock) {
683             checkServiceIsReady();
684             getServiceInterface(mBinder).setListener(listener);
685         }
686     }
687 
closeConnection()688     public void closeConnection() {
689         mRegistrationCallbackManager.close();
690         mCapabilityCallbackManager.close();
691         mProvisioningCallbackManager.close();
692         try {
693             synchronized (mLock) {
694                 if (isBinderAlive()) {
695                     getServiceInterface(mBinder).setListener(null);
696                 }
697             }
698         } catch (RemoteException e) {
699             Log.w(TAG, "closeConnection: couldn't remove listener!");
700         }
701     }
702 
addRegistrationCallback(IImsRegistrationCallback callback)703     public void addRegistrationCallback(IImsRegistrationCallback callback) {
704         mRegistrationCallbackManager.addCallback(callback);
705     }
706 
addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)707     public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
708             int subId) {
709         mRegistrationCallbackManager.addCallbackForSubscription(callback , subId);
710     }
711 
removeRegistrationCallback(IImsRegistrationCallback callback)712     public void removeRegistrationCallback(IImsRegistrationCallback callback) {
713         mRegistrationCallbackManager.removeCallback(callback);
714     }
715 
removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)716     public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
717             int subId) {
718         mRegistrationCallbackManager.removeCallbackForSubscription(callback, subId);
719     }
720 
addCapabilityCallback(IImsCapabilityCallback callback)721     public void addCapabilityCallback(IImsCapabilityCallback callback) {
722         mCapabilityCallbackManager.addCallback(callback);
723     }
724 
addCapabilityCallbackForSubscription(IImsCapabilityCallback callback, int subId)725     public void addCapabilityCallbackForSubscription(IImsCapabilityCallback callback,
726             int subId) {
727         mCapabilityCallbackManager.addCallbackForSubscription(callback, subId);
728     }
729 
removeCapabilityCallback(IImsCapabilityCallback callback)730     public void removeCapabilityCallback(IImsCapabilityCallback callback) {
731         mCapabilityCallbackManager.removeCallback(callback);
732     }
733 
removeCapabilityCallbackForSubscription(IImsCapabilityCallback callback, int subId)734     public void removeCapabilityCallbackForSubscription(IImsCapabilityCallback callback,
735             int subId) {
736         mCapabilityCallbackManager.removeCallbackForSubscription(callback , subId);
737     }
738 
addProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)739     public void addProvisioningCallbackForSubscription(IImsConfigCallback callback,
740             int subId) {
741         mProvisioningCallbackManager.addCallbackForSubscription(callback, subId);
742     }
743 
removeProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)744     public void removeProvisioningCallbackForSubscription(IImsConfigCallback callback,
745             int subId) {
746         mProvisioningCallbackManager.removeCallbackForSubscription(callback , subId);
747     }
748 
changeEnabledCapabilities(CapabilityChangeRequest request, IImsCapabilityCallback callback)749     public void changeEnabledCapabilities(CapabilityChangeRequest request,
750             IImsCapabilityCallback callback) throws RemoteException {
751         synchronized (mLock) {
752             checkServiceIsReady();
753             getServiceInterface(mBinder).changeCapabilitiesConfiguration(request, callback);
754         }
755     }
756 
queryEnabledCapabilities(int capability, int radioTech, IImsCapabilityCallback callback)757     public void queryEnabledCapabilities(int capability, int radioTech,
758             IImsCapabilityCallback callback) throws RemoteException {
759         synchronized (mLock) {
760             checkServiceIsReady();
761             getServiceInterface(mBinder).queryCapabilityConfiguration(capability, radioTech,
762                     callback);
763         }
764     }
765 
queryCapabilityStatus()766     public MmTelFeature.MmTelCapabilities queryCapabilityStatus() throws RemoteException {
767         synchronized (mLock) {
768             checkServiceIsReady();
769             return new MmTelFeature.MmTelCapabilities(
770                     getServiceInterface(mBinder).queryCapabilityStatus());
771         }
772     }
773 
createCallProfile(int callServiceType, int callType)774     public ImsCallProfile createCallProfile(int callServiceType, int callType)
775             throws RemoteException {
776         synchronized (mLock) {
777             checkServiceIsReady();
778             return getServiceInterface(mBinder).createCallProfile(callServiceType, callType);
779         }
780     }
781 
createCallSession(ImsCallProfile profile)782     public IImsCallSession createCallSession(ImsCallProfile profile)
783             throws RemoteException {
784         synchronized (mLock) {
785             checkServiceIsReady();
786             return getServiceInterface(mBinder).createCallSession(profile);
787         }
788     }
789 
getUtInterface()790     public IImsUt getUtInterface() throws RemoteException {
791         synchronized (mLock) {
792             checkServiceIsReady();
793             return getServiceInterface(mBinder).getUtInterface();
794         }
795     }
796 
getConfigInterface()797     public IImsConfig getConfigInterface() {
798         return getConfig();
799     }
800 
getRegistrationTech()801     public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech()
802             throws RemoteException {
803         IImsRegistration registration = getRegistration();
804         if (registration != null) {
805                 return registration.getRegistrationTechnology();
806         } else {
807             return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
808         }
809     }
810 
getEcbmInterface()811     public IImsEcbm getEcbmInterface() throws RemoteException {
812         synchronized (mLock) {
813             checkServiceIsReady();
814             return getServiceInterface(mBinder).getEcbmInterface();
815         }
816     }
817 
setUiTTYMode(int uiTtyMode, Message onComplete)818     public void setUiTTYMode(int uiTtyMode, Message onComplete)
819             throws RemoteException {
820         synchronized (mLock) {
821             checkServiceIsReady();
822             getServiceInterface(mBinder).setUiTtyMode(uiTtyMode, onComplete);
823         }
824     }
825 
getMultiEndpointInterface()826     public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
827         synchronized (mLock) {
828             checkServiceIsReady();
829             return getServiceInterface(mBinder).getMultiEndpointInterface();
830         }
831     }
832 
sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)833     public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
834             byte[] pdu) throws RemoteException {
835         synchronized (mLock) {
836             checkServiceIsReady();
837             getServiceInterface(mBinder).sendSms(token, messageRef, format, smsc, isRetry,
838                     pdu);
839         }
840     }
841 
acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.SendStatusResult int result)842     public void acknowledgeSms(int token, int messageRef,
843             @ImsSmsImplBase.SendStatusResult int result) throws RemoteException {
844         synchronized (mLock) {
845             checkServiceIsReady();
846             getServiceInterface(mBinder).acknowledgeSms(token, messageRef, result);
847         }
848     }
849 
acknowledgeSmsReport(int token, int messageRef, @ImsSmsImplBase.StatusReportResult int result)850     public void acknowledgeSmsReport(int token, int messageRef,
851             @ImsSmsImplBase.StatusReportResult int result) throws RemoteException {
852         synchronized (mLock) {
853             checkServiceIsReady();
854             getServiceInterface(mBinder).acknowledgeSmsReport(token, messageRef, result);
855         }
856     }
857 
getSmsFormat()858     public String getSmsFormat() throws RemoteException {
859         synchronized (mLock) {
860             checkServiceIsReady();
861             return getServiceInterface(mBinder).getSmsFormat();
862         }
863     }
864 
onSmsReady()865     public void onSmsReady() throws RemoteException {
866         synchronized (mLock) {
867             checkServiceIsReady();
868             getServiceInterface(mBinder).onSmsReady();
869         }
870     }
871 
setSmsListener(IImsSmsListener listener)872     public void setSmsListener(IImsSmsListener listener) throws RemoteException {
873         synchronized (mLock) {
874             checkServiceIsReady();
875             getServiceInterface(mBinder).setSmsListener(listener);
876         }
877     }
878 
shouldProcessCall(boolean isEmergency, String[] numbers)879     public @MmTelFeature.ProcessCallResult int shouldProcessCall(boolean isEmergency,
880             String[] numbers) throws RemoteException {
881         if (isEmergency && !isEmergencyMmTelAvailable()) {
882             // Don't query the ImsService if emergency calling is not available on the ImsService.
883             Log.i(TAG, "MmTel does not support emergency over IMS, fallback to CS.");
884             return MmTelFeature.PROCESS_CALL_CSFB;
885         }
886         synchronized (mLock) {
887             checkServiceIsReady();
888             return getServiceInterface(mBinder).shouldProcessCall(numbers);
889         }
890     }
891 
892     /**
893      * @return an integer describing the current Feature Status, defined in
894      * {@link ImsFeature.ImsState}.
895      */
getFeatureState()896     public int getFeatureState() {
897         synchronized (mLock) {
898             if (isBinderAlive() && mFeatureStateCached != null) {
899                 return mFeatureStateCached;
900             }
901         }
902         // Don't synchronize on Binder call.
903         Integer status = retrieveFeatureState();
904         synchronized (mLock) {
905             if (status == null) {
906                 return ImsFeature.STATE_UNAVAILABLE;
907             }
908             // Cache only non-null value for feature status.
909             mFeatureStateCached = status;
910         }
911         Log.i(TAG, "getFeatureState - returning " + status);
912         return status;
913     }
914 
915     /**
916      * Internal method used to retrieve the feature status from the corresponding ImsService.
917      */
retrieveFeatureState()918     private Integer retrieveFeatureState() {
919         if (mBinder != null) {
920             try {
921                 return getServiceInterface(mBinder).getFeatureState();
922             } catch (RemoteException e) {
923                 // Status check failed, don't update cache
924             }
925         }
926         return null;
927     }
928 
929     /**
930      * @param c Callback that will fire when the feature status has changed.
931      */
setStatusCallback(IFeatureUpdate c)932     public void setStatusCallback(IFeatureUpdate c) {
933         mStatusCallback = c;
934     }
935 
936     /**
937      * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
938      * method returns false, it doesn't mean that the Binder connection is not available (use
939      * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
940      * at this time.
941      *
942      * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
943      * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}.
944      */
isBinderReady()945     public boolean isBinderReady() {
946         return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY;
947     }
948 
949     /**
950      * @return false if the binder connection is no longer alive.
951      */
isBinderAlive()952     public boolean isBinderAlive() {
953         return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
954     }
955 
checkServiceIsReady()956     private void checkServiceIsReady() throws RemoteException {
957         if (!sImsSupportedOnDevice) {
958             throw new RemoteException("IMS is not supported on this device.");
959         }
960         if (!isBinderReady()) {
961             throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
962         }
963     }
964 
getServiceInterface(IBinder b)965     private IImsMmTelFeature getServiceInterface(IBinder b) {
966         return IImsMmTelFeature.Stub.asInterface(b);
967     }
968 }
969