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