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.content.Context;
20 import android.os.Binder;
21 import android.os.IBinder;
22 import android.os.IInterface;
23 import android.os.Message;
24 import android.os.RemoteException;
25 import android.telephony.SubscriptionManager;
26 import android.telephony.ims.ImsCallProfile;
27 import android.telephony.ims.ImsService;
28 import android.telephony.ims.MediaQualityStatus;
29 import android.telephony.ims.MediaThreshold;
30 import android.telephony.ims.RtpHeaderExtensionType;
31 import android.telephony.ims.aidl.IImsCapabilityCallback;
32 import android.telephony.ims.aidl.IImsConfig;
33 import android.telephony.ims.aidl.IImsConfigCallback;
34 import android.telephony.ims.aidl.IImsMmTelFeature;
35 import android.telephony.ims.aidl.IImsRegistration;
36 import android.telephony.ims.aidl.IImsRegistrationCallback;
37 import android.telephony.ims.aidl.IImsSmsListener;
38 import android.telephony.ims.aidl.ISipTransport;
39 import android.telephony.ims.aidl.ISrvccStartedCallback;
40 import android.telephony.ims.feature.CapabilityChangeRequest;
41 import android.telephony.ims.feature.MmTelFeature;
42 import android.telephony.ims.stub.ImsEcbmImplBase;
43 import android.telephony.ims.stub.ImsRegistrationImplBase;
44 import android.telephony.ims.stub.ImsSmsImplBase;
45 import android.util.Log;
46 import android.util.SparseArray;
47 
48 import com.android.ims.internal.IImsCallSession;
49 import com.android.ims.internal.IImsEcbm;
50 import com.android.ims.internal.IImsMultiEndpoint;
51 import com.android.ims.internal.IImsUt;
52 
53 import java.util.ArrayList;
54 import java.util.HashMap;
55 import java.util.Set;
56 
57 /**
58  * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
59  * the platform currently supports: MMTel
60  */
61 
62 public class MmTelFeatureConnection extends FeatureConnection {
63     protected static final String TAG = "MmTelFeatureConn";
64 
65     private class ImsRegistrationCallbackAdapter extends
66             ImsCallbackAdapterManager<IImsRegistrationCallback> {
67 
ImsRegistrationCallbackAdapter(Context context, Object lock)68         public ImsRegistrationCallbackAdapter(Context context, Object lock) {
69             super(context, lock, mSlotId, mSubId);
70         }
71 
72         @Override
registerCallback(IImsRegistrationCallback localCallback)73         public void registerCallback(IImsRegistrationCallback localCallback) {
74             IImsRegistration imsRegistration = getRegistration();
75             if (imsRegistration != null) {
76                 try {
77                     imsRegistration.addRegistrationCallback(localCallback);
78                 } catch (RemoteException e) {
79                     throw new IllegalStateException("ImsRegistrationCallbackAdapter: MmTelFeature"
80                             + " binder is dead.");
81                 }
82             } else {
83                 Log.e(TAG + " [" + mSlotId + "]", "ImsRegistrationCallbackAdapter: ImsRegistration"
84                         + " is null");
85                 throw new IllegalStateException("ImsRegistrationCallbackAdapter: MmTelFeature is"
86                         + "not available!");
87             }
88         }
89 
90         @Override
unregisterCallback(IImsRegistrationCallback localCallback)91         public void unregisterCallback(IImsRegistrationCallback localCallback) {
92             IImsRegistration imsRegistration = getRegistration();
93             if (imsRegistration != null) {
94                 try {
95                     imsRegistration.removeRegistrationCallback(localCallback);
96                 } catch (RemoteException | IllegalStateException e) {
97                     Log.w(TAG + " [" + mSlotId + "]", "ImsRegistrationCallbackAdapter -"
98                             + " unregisterCallback: couldn't remove registration callback"
99                             + " Exception: " + e.getMessage());
100                 }
101             } else {
102                 Log.e(TAG + " [" + mSlotId + "]", "ImsRegistrationCallbackAdapter: ImsRegistration"
103                         + " is null");
104             }
105         }
106     }
107 
108     private class ImsEmergencyRegistrationCallbackAdapter extends
109             ImsCallbackAdapterManager<IImsRegistrationCallback> {
110 
ImsEmergencyRegistrationCallbackAdapter(Context context, Object lock)111         public ImsEmergencyRegistrationCallbackAdapter(Context context, Object lock) {
112             super(context, lock, mSlotId, mSubId);
113         }
114 
115         @Override
registerCallback(IImsRegistrationCallback localCallback)116         public void registerCallback(IImsRegistrationCallback localCallback) {
117             IImsRegistration imsRegistration = getRegistration();
118             if (imsRegistration != null) {
119                 try {
120                     imsRegistration.addEmergencyRegistrationCallback(localCallback);
121                 } catch (RemoteException e) {
122                     throw new IllegalStateException("ImsEmergencyRegistrationCallbackAdapter: "
123                             + "MmTelFeature binder is dead.");
124                 }
125             } else {
126                 Log.e(TAG + " [" + mSlotId + "]", "ImsEmergencyRegistrationCallbackAdapter: "
127                         + " ImsEmergencyRegistration is null");
128                 throw new IllegalStateException("ImsEmergencyRegistrationCallbackAdapter: "
129                         + "MmTelFeature is not available!");
130             }
131         }
132 
133         @Override
unregisterCallback(IImsRegistrationCallback localCallback)134         public void unregisterCallback(IImsRegistrationCallback localCallback) {
135             IImsRegistration imsRegistration = getRegistration();
136             if (imsRegistration != null) {
137                 try {
138                     imsRegistration.removeEmergencyRegistrationCallback(localCallback);
139                 } catch (RemoteException | IllegalStateException e) {
140                     Log.w(TAG + " [" + mSlotId + "]", "ImsEmergencyRegistrationCallbackAdapter -"
141                             + " unregisterCallback: couldn't remove emergency registration callback"
142                             + " Exception: " + e.getMessage());
143                 }
144             } else {
145                 Log.e(TAG + " [" + mSlotId + "]", "ImsEmergencyRegistrationCallbackAdapter: "
146                         + " ImsEmergencyRegistration is null");
147             }
148         }
149     }
150 
151     private class CapabilityCallbackManager extends ImsCallbackAdapterManager<IImsCapabilityCallback> {
CapabilityCallbackManager(Context context, Object lock)152         public CapabilityCallbackManager(Context context, Object lock) {
153             super(context, lock, mSlotId, mSubId);
154         }
155 
156         @Override
registerCallback(IImsCapabilityCallback localCallback)157         public void registerCallback(IImsCapabilityCallback localCallback) {
158             IImsMmTelFeature binder;
159             synchronized (mLock) {
160                 try {
161                     checkServiceIsReady();
162                     binder = getServiceInterface(mBinder);
163                 } catch (RemoteException e) {
164                     throw new IllegalStateException("CapabilityCallbackManager - MmTelFeature"
165                             + " binder is dead.");
166                 }
167             }
168             if (binder != null) {
169                 try {
170                 binder.addCapabilityCallback(localCallback);
171                 } catch (RemoteException e) {
172                     throw new IllegalStateException(" CapabilityCallbackManager - MmTelFeature"
173                             + " binder is null.");
174                 }
175             } else {
176                 Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, register: Couldn't"
177                         + " get binder");
178                 throw new IllegalStateException("CapabilityCallbackManager: MmTelFeature is"
179                         + " not available!");
180             }
181         }
182 
183         @Override
unregisterCallback(IImsCapabilityCallback localCallback)184         public void unregisterCallback(IImsCapabilityCallback localCallback) {
185             IImsMmTelFeature binder;
186             synchronized (mLock) {
187                 if (!isBinderAlive()) {
188                     Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, unregister:"
189                             + " binder is not alive");
190                     return;
191                 }
192                 binder = getServiceInterface(mBinder);
193             }
194             if (binder != null) {
195                 try {
196                     binder.removeCapabilityCallback(localCallback);
197                 } catch (RemoteException | IllegalStateException e) {
198                     Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, unregister:"
199                             + " Binder is dead. Exception: " + e.getMessage());
200                 }
201             } else {
202                 Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, unregister:"
203                         + " binder is null.");
204             }
205         }
206     }
207 
208     private class ProvisioningCallbackManager extends ImsCallbackAdapterManager<IImsConfigCallback> {
ProvisioningCallbackManager(Context context, Object lock)209         public ProvisioningCallbackManager (Context context, Object lock) {
210             super(context, lock, mSlotId, mSubId);
211         }
212 
213         @Override
registerCallback(IImsConfigCallback localCallback)214         public void registerCallback(IImsConfigCallback localCallback) {
215             IImsConfig binder = getConfig();
216             if (binder == null) {
217                 // Config interface is not currently available.
218                 Log.w(TAG + " [" + mSlotId + "]", "ProvisioningCallbackManager - couldn't register,"
219                         + " binder is null.");
220                 throw new IllegalStateException("ImsConfig is not available!");
221             }
222             try {
223                 binder.addImsConfigCallback(localCallback);
224             }catch (RemoteException e) {
225                 throw new IllegalStateException("ImsService is not available!");
226             }
227         }
228 
229         @Override
unregisterCallback(IImsConfigCallback localCallback)230         public void unregisterCallback(IImsConfigCallback localCallback) {
231             IImsConfig binder = getConfig();
232             if (binder == null) {
233                 Log.w(TAG + " [" + mSlotId + "]", "ProvisioningCallbackManager - couldn't"
234                         + " unregister, binder is null.");
235                 return;
236             }
237             try {
238                 binder.removeImsConfigCallback(localCallback);
239             } catch (RemoteException | IllegalStateException e) {
240                 Log.w(TAG + " [" + mSlotId + "]", "ProvisioningCallbackManager - couldn't"
241                         + " unregister, binder is dead. Exception: " + e.getMessage());
242             }
243         }
244     }
245 
246     private static final class BinderAccessState<T> {
247         /**
248          * We have not tried to get the interface yet.
249          */
250         static final int STATE_NOT_SET = 0;
251         /**
252          * We have tried to get the interface, but it is not supported.
253          */
254         static final int STATE_NOT_SUPPORTED = 1;
255         /**
256          * The interface is available from the service.
257          */
258         static final int STATE_AVAILABLE = 2;
259 
of(T value)260         public static <T> BinderAccessState<T> of(T value) {
261             return new BinderAccessState<>(value);
262         }
263 
264         private final int mState;
265         private final T mInterface;
266 
BinderAccessState(int state)267         public BinderAccessState(int state) {
268             mState = state;
269             mInterface = null;
270         }
271 
BinderAccessState(T binderInterface)272         public BinderAccessState(T binderInterface) {
273             mState = STATE_AVAILABLE;
274             mInterface = binderInterface;
275         }
276 
getState()277         public int getState() {
278             return mState;
279         }
280 
getInterface()281         public T getInterface() {
282             return mInterface;
283         }
284     }
285 
286     // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent.
287     private boolean mSupportsEmergencyCalling = false;
288     private BinderAccessState<ImsEcbm> mEcbm =
289             new BinderAccessState<>(BinderAccessState.STATE_NOT_SET);
290     private BinderAccessState<ImsMultiEndpoint> mMultiEndpoint =
291             new BinderAccessState<>(BinderAccessState.STATE_NOT_SET);
292     private MmTelFeature.Listener mMmTelFeatureListener;
293     private ImsUt mUt;
294 
295     private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager;
296     private final ImsEmergencyRegistrationCallbackAdapter mEmergencyRegistrationCallbackManager;
297     private final CapabilityCallbackManager mCapabilityCallbackManager;
298     private final ProvisioningCallbackManager mProvisioningCallbackManager;
299 
MmTelFeatureConnection(Context context, int slotId, int subId, IImsMmTelFeature f, IImsConfig c, IImsRegistration r, ISipTransport s)300     public MmTelFeatureConnection(Context context, int slotId, int subId, IImsMmTelFeature f,
301             IImsConfig c, IImsRegistration r, ISipTransport s) {
302         super(context, slotId, subId, c, r, s);
303 
304         setBinder((f != null) ? f.asBinder() : null);
305         mRegistrationCallbackManager = new ImsRegistrationCallbackAdapter(context, mLock);
306         mEmergencyRegistrationCallbackManager = new ImsEmergencyRegistrationCallbackAdapter(context,
307                 mLock);
308         mCapabilityCallbackManager = new CapabilityCallbackManager(context, mLock);
309         mProvisioningCallbackManager = new ProvisioningCallbackManager(context, mLock);
310     }
311 
312     @Override
onRemovedOrDied()313     protected void onRemovedOrDied() {
314         // Release all callbacks being tracked and unregister them from the connected MmTelFeature.
315         mRegistrationCallbackManager.close();
316         mEmergencyRegistrationCallbackManager.close();
317         mCapabilityCallbackManager.close();
318         mProvisioningCallbackManager.close();
319         // Close mUt interface separately from other listeners, as it is not tied directly to
320         // calling. There is still a limitation currently that only one UT listener can be set
321         // (through ImsPhoneCallTracker), but this could be relaxed in the future via the ability
322         // to register multiple callbacks.
323         synchronized (mLock) {
324             if (mUt != null) {
325                 mUt.close();
326                 mUt = null;
327             }
328             closeConnection();
329             super.onRemovedOrDied();
330         }
331     }
332 
isEmergencyMmTelAvailable()333     public boolean isEmergencyMmTelAvailable() {
334         return mSupportsEmergencyCalling;
335     }
336 
337     /**
338      * Opens the connection to the {@link MmTelFeature} and establishes a listener back to the
339      * framework. Calling this method multiple times will reset the listener attached to the
340      * {@link MmTelFeature}.
341      * @param mmTelListener A {@link MmTelFeature.Listener} that will be used by the
342      *         {@link MmTelFeature} to notify the framework of mmtel calling updates.
343      * @param ecbmListener Listener used to listen for ECBM updates from {@link ImsEcbmImplBase}
344      *         implementation.
345      */
openConnection(MmTelFeature.Listener mmTelListener, ImsEcbmStateListener ecbmListener, ImsExternalCallStateListener multiEndpointListener)346     public void openConnection(MmTelFeature.Listener mmTelListener,
347             ImsEcbmStateListener ecbmListener,
348             ImsExternalCallStateListener multiEndpointListener) throws RemoteException {
349         synchronized (mLock) {
350             checkServiceIsReady();
351             mMmTelFeatureListener = mmTelListener;
352             getServiceInterface(mBinder).setListener(mmTelListener);
353             setEcbmInterface(ecbmListener);
354             setMultiEndpointInterface(multiEndpointListener);
355         }
356     }
357 
358     /**
359      * Closes the connection to the {@link MmTelFeature} if it was previously opened via
360      * {@link #openConnection} by removing all listeners.
361      */
closeConnection()362     public void closeConnection() {
363         synchronized (mLock) {
364             if (!isBinderAlive()) return;
365             try {
366                 if (mMmTelFeatureListener != null) {
367                     mMmTelFeatureListener = null;
368                     getServiceInterface(mBinder).setListener(null);
369                 }
370                 if (mEcbm.getState() == BinderAccessState.STATE_AVAILABLE) {
371                     mEcbm.getInterface().setEcbmStateListener(null);
372                     mEcbm = new BinderAccessState<>(BinderAccessState.STATE_NOT_SET);
373                 }
374                 if (mMultiEndpoint.getState() == BinderAccessState.STATE_AVAILABLE) {
375                     mMultiEndpoint.getInterface().setExternalCallStateListener(null);
376                     mMultiEndpoint = new BinderAccessState<>(BinderAccessState.STATE_NOT_SET);
377                 }
378             } catch (RemoteException | IllegalStateException e) {
379                 Log.w(TAG + " [" + mSlotId + "]", "closeConnection: couldn't remove listeners!" +
380                         " Exception: " + e.getMessage());
381             }
382         }
383     }
384 
addRegistrationCallback(IImsRegistrationCallback callback)385     public void addRegistrationCallback(IImsRegistrationCallback callback) {
386         mRegistrationCallbackManager.addCallback(callback);
387     }
388 
addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)389     public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
390             int subId) {
391         mRegistrationCallbackManager.addCallbackForSubscription(callback , subId);
392     }
393 
removeRegistrationCallback(IImsRegistrationCallback callback)394     public void removeRegistrationCallback(IImsRegistrationCallback callback) {
395         mRegistrationCallbackManager.removeCallback(callback);
396     }
397 
removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)398     public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
399             int subId) {
400         mRegistrationCallbackManager.removeCallback(callback);
401     }
402 
addEmergencyRegistrationCallbackForSubscription( IImsRegistrationCallback callback, int subId)403     public void addEmergencyRegistrationCallbackForSubscription(
404             IImsRegistrationCallback callback, int subId) {
405         mEmergencyRegistrationCallbackManager.addCallbackForSubscription(callback , subId);
406     }
407 
removeEmergencyRegistrationCallbackForSubscription( IImsRegistrationCallback callback, int subId)408     public void removeEmergencyRegistrationCallbackForSubscription(
409             IImsRegistrationCallback callback, int subId) {
410         mEmergencyRegistrationCallbackManager.removeCallback(callback);
411     }
412 
addCapabilityCallback(IImsCapabilityCallback callback)413     public void addCapabilityCallback(IImsCapabilityCallback callback) {
414         mCapabilityCallbackManager.addCallback(callback);
415     }
416 
addCapabilityCallbackForSubscription(IImsCapabilityCallback callback, int subId)417     public void addCapabilityCallbackForSubscription(IImsCapabilityCallback callback,
418             int subId) {
419         mCapabilityCallbackManager.addCallbackForSubscription(callback, subId);
420     }
421 
removeCapabilityCallback(IImsCapabilityCallback callback)422     public void removeCapabilityCallback(IImsCapabilityCallback callback) {
423         mCapabilityCallbackManager.removeCallback(callback);
424     }
425 
removeCapabilityCallbackForSubscription(IImsCapabilityCallback callback, int subId)426     public void removeCapabilityCallbackForSubscription(IImsCapabilityCallback callback,
427             int subId) {
428         mCapabilityCallbackManager.removeCallback(callback);
429     }
430 
addProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)431     public void addProvisioningCallbackForSubscription(IImsConfigCallback callback,
432             int subId) {
433         mProvisioningCallbackManager.addCallbackForSubscription(callback, subId);
434     }
435 
removeProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)436     public void removeProvisioningCallbackForSubscription(IImsConfigCallback callback,
437             int subId) {
438         mProvisioningCallbackManager.removeCallback(callback);
439     }
440 
setMediaThreshold(@ediaQualityStatus.MediaSessionType int sessionType, MediaThreshold threshold)441     public void setMediaThreshold(@MediaQualityStatus.MediaSessionType int sessionType,
442             MediaThreshold threshold) throws RemoteException {
443         synchronized (mLock) {
444             checkServiceIsReady();
445             getServiceInterface(mBinder).setMediaQualityThreshold(sessionType, threshold);
446         }
447     }
448 
queryMediaQualityStatus( @ediaQualityStatus.MediaSessionType int sessionType)449     public MediaQualityStatus queryMediaQualityStatus(
450             @MediaQualityStatus.MediaSessionType int sessionType) throws RemoteException {
451         synchronized (mLock) {
452             checkServiceIsReady();
453             return getServiceInterface(mBinder).queryMediaQualityStatus(sessionType);
454         }
455     }
456 
changeEnabledCapabilities(CapabilityChangeRequest request, IImsCapabilityCallback callback)457     public void changeEnabledCapabilities(CapabilityChangeRequest request,
458             IImsCapabilityCallback callback) throws RemoteException {
459         synchronized (mLock) {
460             checkServiceIsReady();
461             getServiceInterface(mBinder).changeCapabilitiesConfiguration(request, callback);
462         }
463     }
464 
queryEnabledCapabilities(int capability, int radioTech, IImsCapabilityCallback callback)465     public void queryEnabledCapabilities(int capability, int radioTech,
466             IImsCapabilityCallback callback) throws RemoteException {
467         synchronized (mLock) {
468             checkServiceIsReady();
469             getServiceInterface(mBinder).queryCapabilityConfiguration(capability, radioTech,
470                     callback);
471         }
472     }
473 
queryCapabilityStatus()474     public MmTelFeature.MmTelCapabilities queryCapabilityStatus() throws RemoteException {
475         synchronized (mLock) {
476             checkServiceIsReady();
477             return new MmTelFeature.MmTelCapabilities(
478                     getServiceInterface(mBinder).queryCapabilityStatus());
479         }
480     }
481 
createCallProfile(int callServiceType, int callType)482     public ImsCallProfile createCallProfile(int callServiceType, int callType)
483             throws RemoteException {
484         synchronized (mLock) {
485             checkServiceIsReady();
486             return getServiceInterface(mBinder).createCallProfile(callServiceType, callType);
487         }
488     }
489 
changeOfferedRtpHeaderExtensionTypes(Set<RtpHeaderExtensionType> types)490     public void changeOfferedRtpHeaderExtensionTypes(Set<RtpHeaderExtensionType> types)
491             throws RemoteException {
492         synchronized (mLock) {
493             checkServiceIsReady();
494             getServiceInterface(mBinder).changeOfferedRtpHeaderExtensionTypes(
495                     new ArrayList<>(types));
496         }
497     }
498 
createCallSession(ImsCallProfile profile)499     public IImsCallSession createCallSession(ImsCallProfile profile)
500             throws RemoteException {
501         synchronized (mLock) {
502             checkServiceIsReady();
503             return getServiceInterface(mBinder).createCallSession(profile);
504         }
505     }
506 
createOrGetUtInterface()507     public ImsUt createOrGetUtInterface() throws RemoteException {
508         synchronized (mLock) {
509             if (mUt != null) return mUt;
510 
511             checkServiceIsReady();
512             IImsUt imsUt = getServiceInterface(mBinder).getUtInterface();
513             // This will internally set up a listener on the ImsUtImplBase interface, and there is
514             // a limitation that there can only be one. If multiple connections try to create this
515             // UT interface, it will throw an IllegalStateException.
516             mUt = (imsUt != null) ? new ImsUt(imsUt, mContext.getMainExecutor()) : null;
517             return mUt;
518         }
519     }
520 
setEcbmInterface(ImsEcbmStateListener ecbmListener)521     private void setEcbmInterface(ImsEcbmStateListener ecbmListener) throws RemoteException {
522         synchronized (mLock) {
523             if (mEcbm.getState() != BinderAccessState.STATE_NOT_SET) {
524                 throw new IllegalStateException("ECBM interface already open");
525             }
526 
527             checkServiceIsReady();
528             IImsEcbm imsEcbm = getServiceInterface(mBinder).getEcbmInterface();
529             mEcbm = (imsEcbm != null) ? BinderAccessState.of(new ImsEcbm(imsEcbm)) :
530                     new BinderAccessState<>(BinderAccessState.STATE_NOT_SUPPORTED);
531             if (mEcbm.getState() == BinderAccessState.STATE_AVAILABLE) {
532                 // May throw an IllegalStateException if a listener already exists.
533                 mEcbm.getInterface().setEcbmStateListener(ecbmListener);
534             }
535         }
536     }
537 
getEcbmInterface()538     public ImsEcbm getEcbmInterface() {
539         synchronized (mLock) {
540             if (mEcbm.getState() == BinderAccessState.STATE_NOT_SET) {
541                 throw new IllegalStateException("ECBM interface has not been opened");
542             }
543 
544             return mEcbm.getState() == BinderAccessState.STATE_AVAILABLE ?
545                     mEcbm.getInterface() : null;
546         }
547     }
548 
setUiTTYMode(int uiTtyMode, Message onComplete)549     public void setUiTTYMode(int uiTtyMode, Message onComplete)
550             throws RemoteException {
551         synchronized (mLock) {
552             checkServiceIsReady();
553             getServiceInterface(mBinder).setUiTtyMode(uiTtyMode, onComplete);
554         }
555     }
556 
setMultiEndpointInterface(ImsExternalCallStateListener listener)557     private void setMultiEndpointInterface(ImsExternalCallStateListener listener)
558             throws RemoteException {
559         synchronized (mLock) {
560             if (mMultiEndpoint.getState() != BinderAccessState.STATE_NOT_SET) {
561                 throw new IllegalStateException("multiendpoint interface is already open");
562             }
563 
564             checkServiceIsReady();
565             IImsMultiEndpoint imEndpoint = getServiceInterface(mBinder).getMultiEndpointInterface();
566             mMultiEndpoint = (imEndpoint != null)
567                     ? BinderAccessState.of(new ImsMultiEndpoint(imEndpoint)) :
568                     new BinderAccessState<>(BinderAccessState.STATE_NOT_SUPPORTED);
569             if (mMultiEndpoint.getState() == BinderAccessState.STATE_AVAILABLE) {
570                 // May throw an IllegalStateException if a listener already exists.
571                 mMultiEndpoint.getInterface().setExternalCallStateListener(listener);
572             }
573         }
574     }
575 
sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)576     public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
577             byte[] pdu) throws RemoteException {
578         synchronized (mLock) {
579             checkServiceIsReady();
580             getServiceInterface(mBinder).sendSms(token, messageRef, format, smsc, isRetry,
581                     pdu);
582         }
583     }
584 
onMemoryAvailable(int token)585     public void onMemoryAvailable(int token) throws RemoteException {
586         synchronized (mLock) {
587             checkServiceIsReady();
588             getServiceInterface(mBinder).onMemoryAvailable(token);
589         }
590     }
591 
acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.SendStatusResult int result)592     public void acknowledgeSms(int token, int messageRef,
593             @ImsSmsImplBase.SendStatusResult int result) throws RemoteException {
594         synchronized (mLock) {
595             checkServiceIsReady();
596             getServiceInterface(mBinder).acknowledgeSms(token, messageRef, result);
597         }
598     }
599 
acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.SendStatusResult int result, byte[] pdu)600     public void acknowledgeSms(int token, int messageRef,
601             @ImsSmsImplBase.SendStatusResult int result, byte[] pdu) throws RemoteException {
602         synchronized (mLock) {
603             checkServiceIsReady();
604             getServiceInterface(mBinder).acknowledgeSmsWithPdu(token, messageRef, result, pdu);
605         }
606     }
607 
acknowledgeSmsReport(int token, int messageRef, @ImsSmsImplBase.StatusReportResult int result)608     public void acknowledgeSmsReport(int token, int messageRef,
609             @ImsSmsImplBase.StatusReportResult int result) throws RemoteException {
610         synchronized (mLock) {
611             checkServiceIsReady();
612             getServiceInterface(mBinder).acknowledgeSmsReport(token, messageRef, result);
613         }
614     }
615 
getSmsFormat()616     public String getSmsFormat() throws RemoteException {
617         synchronized (mLock) {
618             checkServiceIsReady();
619             return getServiceInterface(mBinder).getSmsFormat();
620         }
621     }
622 
onSmsReady()623     public void onSmsReady() throws RemoteException {
624         synchronized (mLock) {
625             checkServiceIsReady();
626             getServiceInterface(mBinder).onSmsReady();
627         }
628     }
629 
setSmsListener(IImsSmsListener listener)630     public void setSmsListener(IImsSmsListener listener) throws RemoteException {
631         synchronized (mLock) {
632             checkServiceIsReady();
633             getServiceInterface(mBinder).setSmsListener(listener);
634         }
635     }
636 
notifySrvccStarted(ISrvccStartedCallback cb)637     public void notifySrvccStarted(ISrvccStartedCallback cb)
638             throws RemoteException {
639         synchronized (mLock) {
640             checkServiceIsReady();
641             getServiceInterface(mBinder).notifySrvccStarted(cb);
642         }
643     }
644 
notifySrvccCompleted()645     public void notifySrvccCompleted() throws RemoteException {
646         synchronized (mLock) {
647             checkServiceIsReady();
648             getServiceInterface(mBinder).notifySrvccCompleted();
649         }
650     }
651 
notifySrvccFailed()652     public void notifySrvccFailed() throws RemoteException {
653         synchronized (mLock) {
654             checkServiceIsReady();
655             getServiceInterface(mBinder).notifySrvccFailed();
656         }
657     }
658 
notifySrvccCanceled()659     public void notifySrvccCanceled() throws RemoteException {
660         synchronized (mLock) {
661             checkServiceIsReady();
662             getServiceInterface(mBinder).notifySrvccCanceled();
663         }
664     }
665 
triggerDeregistration(@msRegistrationImplBase.ImsDeregistrationReason int reason)666     public void triggerDeregistration(@ImsRegistrationImplBase.ImsDeregistrationReason int reason)
667             throws RemoteException {
668         IImsRegistration registration = getRegistration();
669         if (registration != null) {
670             registration.triggerDeregistration(reason);
671         } else {
672             Log.e(TAG + " [" + mSlotId + "]", "triggerDeregistration IImsRegistration is null");
673         }
674     }
675 
shouldProcessCall(boolean isEmergency, String[] numbers)676     public @MmTelFeature.ProcessCallResult int shouldProcessCall(boolean isEmergency,
677             String[] numbers) throws RemoteException {
678         if (isEmergency && !isEmergencyMmTelAvailable()) {
679             // Don't query the ImsService if emergency calling is not available on the ImsService.
680             Log.i(TAG + " [" + mSlotId + "]", "MmTel does not support emergency over IMS, fallback"
681                     + " to CS.");
682             return MmTelFeature.PROCESS_CALL_CSFB;
683         }
684         synchronized (mLock) {
685             checkServiceIsReady();
686             return getServiceInterface(mBinder).shouldProcessCall(numbers);
687         }
688     }
689 
690     @Override
retrieveFeatureState()691     protected Integer retrieveFeatureState() {
692         if (mBinder != null) {
693             try {
694                 return getServiceInterface(mBinder).getFeatureState();
695             } catch (RemoteException e) {
696                 // Status check failed, don't update cache
697             }
698         }
699         return null;
700     }
701 
702     @Override
onFeatureCapabilitiesUpdated(long capabilities)703     public void onFeatureCapabilitiesUpdated(long capabilities)
704     {
705         synchronized (mLock) {
706             mSupportsEmergencyCalling =
707                     ((capabilities | ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL) > 0);
708         }
709     }
710 
711     /**
712      * Notifies the MmTelFeature of the enablement status of terminal based call waiting
713      *
714      * @param enabled indicates whether the user setting for call waiting is enabled or not.
715      */
setTerminalBasedCallWaitingStatus(boolean enabled)716     public void setTerminalBasedCallWaitingStatus(boolean enabled)
717             throws RemoteException {
718         synchronized (mLock) {
719             checkServiceIsReady();
720             getServiceInterface(mBinder).setTerminalBasedCallWaitingStatus(enabled);
721         }
722     }
723 
getServiceInterface(IBinder b)724     private IImsMmTelFeature getServiceInterface(IBinder b) {
725         return IImsMmTelFeature.Stub.asInterface(b);
726     }
727 }
728