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