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