ImsManager.java revision e7663c09acfdfa34d423be3c1ac7fecf2eb5e3e1
1/* 2 * Copyright (c) 2013 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 17package com.android.ims; 18 19import android.app.PendingIntent; 20import android.app.QueuedWork; 21import android.content.Context; 22import android.content.Intent; 23import android.os.IBinder; 24import android.os.Message; 25import android.os.RemoteException; 26import android.os.ServiceManager; 27import android.os.SystemProperties; 28import android.provider.Settings; 29import android.telecom.TelecomManager; 30import android.telephony.CarrierConfigManager; 31import android.telephony.Rlog; 32import android.telephony.SubscriptionManager; 33import android.telephony.TelephonyManager; 34 35import com.android.ims.internal.IImsCallSession; 36import com.android.ims.internal.IImsEcbm; 37import com.android.ims.internal.IImsEcbmListener; 38import com.android.ims.internal.IImsRegistrationListener; 39import com.android.ims.internal.IImsService; 40import com.android.ims.internal.IImsUt; 41import com.android.ims.internal.ImsCallSession; 42import com.android.ims.internal.IImsConfig; 43 44import java.util.HashMap; 45 46/** 47 * Provides APIs for IMS services, such as initiating IMS calls, and provides access to 48 * the operator's IMS network. This class is the starting point for any IMS actions. 49 * You can acquire an instance of it with {@link #getInstance getInstance()}.</p> 50 * <p>The APIs in this class allows you to:</p> 51 * 52 * @hide 53 */ 54public class ImsManager { 55 56 /* 57 * Debug flag to override configuration flag 58 */ 59 public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr"; 60 public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0; 61 public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr"; 62 public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0; 63 public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr"; 64 public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0; 65 66 /** 67 * For accessing the IMS related service. 68 * Internal use only. 69 * @hide 70 */ 71 private static final String IMS_SERVICE = "ims"; 72 73 /** 74 * The result code to be sent back with the incoming call {@link PendingIntent}. 75 * @see #open(PendingIntent, ImsConnectionStateListener) 76 */ 77 public static final int INCOMING_CALL_RESULT_CODE = 101; 78 79 /** 80 * Key to retrieve the call ID from an incoming call intent. 81 * @see #open(PendingIntent, ImsConnectionStateListener) 82 */ 83 public static final String EXTRA_CALL_ID = "android:imsCallID"; 84 85 /** 86 * Action to broadcast when ImsService is up. 87 * Internal use only. 88 * @hide 89 */ 90 public static final String ACTION_IMS_SERVICE_UP = 91 "com.android.ims.IMS_SERVICE_UP"; 92 93 /** 94 * Action to broadcast when ImsService is down. 95 * Internal use only. 96 * @hide 97 */ 98 public static final String ACTION_IMS_SERVICE_DOWN = 99 "com.android.ims.IMS_SERVICE_DOWN"; 100 101 /** 102 * Action to broadcast when ImsService registration fails. 103 * Internal use only. 104 * @hide 105 */ 106 public static final String ACTION_IMS_REGISTRATION_ERROR = 107 "com.android.ims.REGISTRATION_ERROR"; 108 109 /** 110 * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents. 111 * A long value; the phone ID corresponding to the IMS service coming up or down. 112 * Internal use only. 113 * @hide 114 */ 115 public static final String EXTRA_PHONE_ID = "android:phone_id"; 116 117 /** 118 * Action for the incoming call intent for the Phone app. 119 * Internal use only. 120 * @hide 121 */ 122 public static final String ACTION_IMS_INCOMING_CALL = 123 "com.android.ims.IMS_INCOMING_CALL"; 124 125 /** 126 * Part of the ACTION_IMS_INCOMING_CALL intents. 127 * An integer value; service identifier obtained from {@link ImsManager#open}. 128 * Internal use only. 129 * @hide 130 */ 131 public static final String EXTRA_SERVICE_ID = "android:imsServiceId"; 132 133 /** 134 * Part of the ACTION_IMS_INCOMING_CALL intents. 135 * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD. 136 * The value "true" indicates that the incoming call is for USSD. 137 * Internal use only. 138 * @hide 139 */ 140 public static final String EXTRA_USSD = "android:ussd"; 141 142 private static final String TAG = "ImsManager"; 143 private static final boolean DBG = true; 144 145 private static HashMap<Integer, ImsManager> sImsManagerInstances = 146 new HashMap<Integer, ImsManager>(); 147 148 private Context mContext; 149 private int mPhoneId; 150 private IImsService mImsService = null; 151 private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient(); 152 // Ut interface for the supplementary service configuration 153 private ImsUt mUt = null; 154 // Interface to get/set ims config items 155 private ImsConfig mConfig = null; 156 157 // ECBM interface 158 private ImsEcbm mEcbm = null; 159 160 /** 161 * Gets a manager instance. 162 * 163 * @param context application context for creating the manager object 164 * @param phoneId the phone ID for the IMS Service 165 * @return the manager instance corresponding to the phoneId 166 */ 167 public static ImsManager getInstance(Context context, int phoneId) { 168 synchronized (sImsManagerInstances) { 169 if (sImsManagerInstances.containsKey(phoneId)) 170 return sImsManagerInstances.get(phoneId); 171 172 ImsManager mgr = new ImsManager(context, phoneId); 173 sImsManagerInstances.put(phoneId, mgr); 174 175 return mgr; 176 } 177 } 178 179 /** 180 * Returns the user configuration of Enhanced 4G LTE Mode setting 181 */ 182 public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) { 183 int enabled = android.provider.Settings.Global.getInt( 184 context.getContentResolver(), 185 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON); 186 return (enabled == 1) ? true : false; 187 } 188 189 /** 190 * Change persistent Enhanced 4G LTE Mode setting 191 */ 192 public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) { 193 int value = enabled ? 1 : 0; 194 android.provider.Settings.Global.putInt( 195 context.getContentResolver(), 196 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value); 197 198 if (isNonTtyOrTtyOnVolteEnabled(context)) { 199 ImsManager imsManager = ImsManager.getInstance(context, 200 SubscriptionManager.getDefaultVoicePhoneId()); 201 if (imsManager != null) { 202 try { 203 imsManager.setAdvanced4GMode(enabled); 204 } catch (ImsException ie) { 205 // do nothing 206 } 207 } 208 } 209 } 210 211 /** 212 * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is 213 * supported. 214 */ 215 public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) { 216 if (getBooleanCarrierConfig(context, 217 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) { 218 return true; 219 } 220 221 return Settings.Secure.getInt(context.getContentResolver(), 222 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF) 223 == TelecomManager.TTY_MODE_OFF; 224 } 225 226 /** 227 * Returns a platform configuration for VoLTE which may override the user setting. 228 */ 229 public static boolean isVolteEnabledByPlatform(Context context) { 230 if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE, 231 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) { 232 return true; 233 } 234 235 boolean disabledByGlobalSetting = android.provider.Settings.Global.getInt( 236 context.getContentResolver(), 237 android.provider.Settings.Global.VOLTE_FEATURE_DISABLED, 0) == 1; 238 239 return context.getResources().getBoolean( 240 com.android.internal.R.bool.config_device_volte_available) 241 && getBooleanCarrierConfig(context, 242 CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL) 243 && !disabledByGlobalSetting; 244 } 245 246 /* 247 * Indicates whether VoLTE is provisioned on device 248 */ 249 public static boolean isVolteProvisionedOnDevice(Context context) { 250 boolean isProvisioned = true; 251 if (getBooleanCarrierConfig(context, 252 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) { 253 isProvisioned = false; // disable on any error 254 ImsManager mgr = ImsManager.getInstance(context, 255 SubscriptionManager.getDefaultVoicePhoneId()); 256 if (mgr != null) { 257 try { 258 ImsConfig config = mgr.getConfigInterface(); 259 if (config != null) { 260 isProvisioned = config.getVolteProvisioned(); 261 } 262 } catch (ImsException ie) { 263 // do nothing 264 } 265 } 266 } 267 268 return isProvisioned; 269 } 270 271 /** 272 * Returns a platform configuration for VT which may override the user setting. 273 * 274 * Note: VT presumes that VoLTE is enabled (these are configuration settings 275 * which must be done correctly). 276 */ 277 public static boolean isVtEnabledByPlatform(Context context) { 278 if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE, 279 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) { 280 return true; 281 } 282 283 return 284 context.getResources().getBoolean( 285 com.android.internal.R.bool.config_device_vt_available) && 286 getBooleanCarrierConfig(context, 287 CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL); 288 } 289 290 /** 291 * Returns the user configuration of WFC setting 292 */ 293 public static boolean isWfcEnabledByUser(Context context) { 294 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(), 295 android.provider.Settings.Global.WFC_IMS_ENABLED, 296 ImsConfig.FeatureValueConstants.OFF); 297 return (enabled == 1) ? true : false; 298 } 299 300 /** 301 * Change persistent WFC enabled setting 302 */ 303 public static void setWfcSetting(Context context, boolean enabled) { 304 int value = enabled ? 1 : 0; 305 android.provider.Settings.Global.putInt(context.getContentResolver(), 306 android.provider.Settings.Global.WFC_IMS_ENABLED, value); 307 308 ImsManager imsManager = ImsManager.getInstance(context, 309 SubscriptionManager.getDefaultVoicePhoneId()); 310 if (imsManager != null) { 311 try { 312 ImsConfig config = imsManager.getConfigInterface(); 313 // FIXME: replace NETWORK_TYPE_LTE with NETWORK_TYPE_IWLAN 314 // when available 315 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI, 316 TelephonyManager.NETWORK_TYPE_LTE, 317 enabled ? ImsConfig.FeatureValueConstants.ON 318 : ImsConfig.FeatureValueConstants.OFF, null); 319 320 if (enabled) { 321 imsManager.turnOnIms(); 322 } else if (getBooleanCarrierConfig(context, 323 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL) 324 && (!isVolteEnabledByPlatform(context) 325 || !isEnhanced4gLteModeSettingEnabledByUser(context))) { 326 log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms"); 327 imsManager.turnOffIms(); 328 } 329 330 // Force IMS to register over LTE when turning off WFC 331 setWfcModeInternal(context, enabled 332 ? getWfcMode(context) 333 : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED); 334 } catch (ImsException e) { 335 loge("setWfcSetting(): " + e); 336 } 337 } 338 } 339 340 /** 341 * Returns the user configuration of WFC modem setting 342 */ 343 public static int getWfcMode(Context context) { 344 int setting = android.provider.Settings.Global.getInt(context.getContentResolver(), 345 android.provider.Settings.Global.WFC_IMS_MODE, 346 ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED); 347 if (DBG) log("getWfcMode - setting=" + setting); 348 return setting; 349 } 350 351 /** 352 * Returns the user configuration of WFC modem setting 353 */ 354 public static void setWfcMode(Context context, int wfcMode) { 355 if (DBG) log("setWfcMode - setting=" + wfcMode); 356 android.provider.Settings.Global.putInt(context.getContentResolver(), 357 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode); 358 359 setWfcModeInternal(context, wfcMode); 360 } 361 362 private static void setWfcModeInternal(Context context, int wfcMode) { 363 final ImsManager imsManager = ImsManager.getInstance(context, 364 SubscriptionManager.getDefaultVoicePhoneId()); 365 if (imsManager != null) { 366 final int value = wfcMode; 367 QueuedWork.singleThreadExecutor().submit(new Runnable() { 368 public void run() { 369 try { 370 imsManager.getConfigInterface().setProvisionedValue( 371 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE, 372 value); 373 } catch (ImsException e) { 374 // do nothing 375 } 376 } 377 }); 378 } 379 } 380 381 /** 382 * Returns the user configuration of WFC roaming setting 383 */ 384 public static boolean isWfcRoamingEnabledByUser(Context context) { 385 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(), 386 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED, 387 ImsConfig.FeatureValueConstants.OFF); 388 return (enabled == 1) ? true : false; 389 } 390 391 /** 392 * Change persistent WFC roaming enabled setting 393 */ 394 public static void setWfcRoamingSetting(Context context, boolean enabled) { 395 final int value = enabled 396 ? ImsConfig.FeatureValueConstants.ON 397 : ImsConfig.FeatureValueConstants.OFF; 398 android.provider.Settings.Global.putInt(context.getContentResolver(), 399 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED, value); 400 401 final ImsManager imsManager = ImsManager.getInstance(context, 402 SubscriptionManager.getDefaultVoicePhoneId()); 403 if (imsManager != null) { 404 QueuedWork.singleThreadExecutor().submit(new Runnable() { 405 public void run() { 406 try { 407 imsManager.getConfigInterface().setProvisionedValue( 408 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING, 409 value); 410 } catch (ImsException e) { 411 // do nothing 412 } 413 } 414 }); 415 } 416 } 417 418 /** 419 * Returns a platform configuration for WFC which may override the user 420 * setting. Note: WFC presumes that VoLTE is enabled (these are 421 * configuration settings which must be done correctly). 422 */ 423 public static boolean isWfcEnabledByPlatform(Context context) { 424 if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE, 425 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) { 426 return true; 427 } 428 429 return 430 context.getResources().getBoolean( 431 com.android.internal.R.bool.config_device_wfc_ims_available) && 432 getBooleanCarrierConfig(context, 433 CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL); 434 } 435 436 private ImsManager(Context context, int phoneId) { 437 mContext = context; 438 mPhoneId = phoneId; 439 createImsService(true); 440 } 441 442 /* 443 * Returns a flag indicating whether the IMS service is available. 444 */ 445 public boolean isServiceAvailable() { 446 if (mImsService != null) { 447 return true; 448 } 449 450 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId)); 451 if (binder != null) { 452 return true; 453 } 454 455 return false; 456 } 457 458 /** 459 * Opens the IMS service for making calls and/or receiving generic IMS calls. 460 * The caller may make subsquent calls through {@link #makeCall}. 461 * The IMS service will register the device to the operator's network with the credentials 462 * (from ISIM) periodically in order to receive calls from the operator's network. 463 * When the IMS service receives a new call, it will send out an intent with 464 * the provided action string. 465 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call. 466 * 467 * @param serviceClass a service class specified in {@link ImsServiceClass} 468 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}. 469 * @param incomingCallPendingIntent When an incoming call is received, 470 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to 471 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} 472 * as the result code and the intent to fill in the call ID; It cannot be null 473 * @param listener To listen to IMS registration events; It cannot be null 474 * @return identifier (greater than 0) for the specified service 475 * @throws NullPointerException if {@code incomingCallPendingIntent} 476 * or {@code listener} is null 477 * @throws ImsException if calling the IMS service results in an error 478 * @see #getCallId 479 * @see #getServiceId 480 */ 481 public int open(int serviceClass, PendingIntent incomingCallPendingIntent, 482 ImsConnectionStateListener listener) throws ImsException { 483 checkAndThrowExceptionIfServiceUnavailable(); 484 485 if (incomingCallPendingIntent == null) { 486 throw new NullPointerException("incomingCallPendingIntent can't be null"); 487 } 488 489 if (listener == null) { 490 throw new NullPointerException("listener can't be null"); 491 } 492 493 int result = 0; 494 495 try { 496 result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent, 497 createRegistrationListenerProxy(serviceClass, listener)); 498 } catch (RemoteException e) { 499 throw new ImsException("open()", e, 500 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 501 } 502 503 if (result <= 0) { 504 // If the return value is a minus value, 505 // it means that an error occurred in the service. 506 // So, it needs to convert to the reason code specified in ImsReasonInfo. 507 throw new ImsException("open()", (result * (-1))); 508 } 509 510 return result; 511 } 512 513 /** 514 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls. 515 * All the resources that were allocated to the service are also released. 516 * 517 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open} 518 * @throws ImsException if calling the IMS service results in an error 519 */ 520 public void close(int serviceId) throws ImsException { 521 checkAndThrowExceptionIfServiceUnavailable(); 522 523 try { 524 mImsService.close(serviceId); 525 } catch (RemoteException e) { 526 throw new ImsException("close()", e, 527 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 528 } finally { 529 mUt = null; 530 mConfig = null; 531 mEcbm = null; 532 } 533 } 534 535 /** 536 * Gets the configuration interface to provision / withdraw the supplementary service settings. 537 * 538 * @param serviceId a service id which is obtained from {@link ImsManager#open} 539 * @return the Ut interface instance 540 * @throws ImsException if getting the Ut interface results in an error 541 */ 542 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId) 543 throws ImsException { 544 // FIXME: manage the multiple Ut interfaces based on the service id 545 if (mUt == null) { 546 checkAndThrowExceptionIfServiceUnavailable(); 547 548 try { 549 IImsUt iUt = mImsService.getUtInterface(serviceId); 550 551 if (iUt == null) { 552 throw new ImsException("getSupplementaryServiceConfiguration()", 553 ImsReasonInfo.CODE_UT_NOT_SUPPORTED); 554 } 555 556 mUt = new ImsUt(iUt); 557 } catch (RemoteException e) { 558 throw new ImsException("getSupplementaryServiceConfiguration()", e, 559 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 560 } 561 } 562 563 return mUt; 564 } 565 566 /** 567 * Checks if the IMS service has successfully registered to the IMS network 568 * with the specified service & call type. 569 * 570 * @param serviceId a service id which is obtained from {@link ImsManager#open} 571 * @param serviceType a service type that is specified in {@link ImsCallProfile} 572 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} 573 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} 574 * @param callType a call type that is specified in {@link ImsCallProfile} 575 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO} 576 * {@link ImsCallProfile#CALL_TYPE_VOICE} 577 * {@link ImsCallProfile#CALL_TYPE_VT} 578 * {@link ImsCallProfile#CALL_TYPE_VS} 579 * @return true if the specified service id is connected to the IMS network; 580 * false otherwise 581 * @throws ImsException if calling the IMS service results in an error 582 */ 583 public boolean isConnected(int serviceId, int serviceType, int callType) 584 throws ImsException { 585 checkAndThrowExceptionIfServiceUnavailable(); 586 587 try { 588 return mImsService.isConnected(serviceId, serviceType, callType); 589 } catch (RemoteException e) { 590 throw new ImsException("isServiceConnected()", e, 591 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 592 } 593 } 594 595 /** 596 * Checks if the specified IMS service is opend. 597 * 598 * @param serviceId a service id which is obtained from {@link ImsManager#open} 599 * @return true if the specified service id is opened; false otherwise 600 * @throws ImsException if calling the IMS service results in an error 601 */ 602 public boolean isOpened(int serviceId) throws ImsException { 603 checkAndThrowExceptionIfServiceUnavailable(); 604 605 try { 606 return mImsService.isOpened(serviceId); 607 } catch (RemoteException e) { 608 throw new ImsException("isOpened()", e, 609 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 610 } 611 } 612 613 /** 614 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. 615 * 616 * @param serviceId a service id which is obtained from {@link ImsManager#open} 617 * @param serviceType a service type that is specified in {@link ImsCallProfile} 618 * {@link ImsCallProfile#SERVICE_TYPE_NONE} 619 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} 620 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} 621 * @param callType a call type that is specified in {@link ImsCallProfile} 622 * {@link ImsCallProfile#CALL_TYPE_VOICE} 623 * {@link ImsCallProfile#CALL_TYPE_VT} 624 * {@link ImsCallProfile#CALL_TYPE_VT_TX} 625 * {@link ImsCallProfile#CALL_TYPE_VT_RX} 626 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR} 627 * {@link ImsCallProfile#CALL_TYPE_VS} 628 * {@link ImsCallProfile#CALL_TYPE_VS_TX} 629 * {@link ImsCallProfile#CALL_TYPE_VS_RX} 630 * @return a {@link ImsCallProfile} object 631 * @throws ImsException if calling the IMS service results in an error 632 */ 633 public ImsCallProfile createCallProfile(int serviceId, 634 int serviceType, int callType) throws ImsException { 635 checkAndThrowExceptionIfServiceUnavailable(); 636 637 try { 638 return mImsService.createCallProfile(serviceId, serviceType, callType); 639 } catch (RemoteException e) { 640 throw new ImsException("createCallProfile()", e, 641 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 642 } 643 } 644 645 /** 646 * Creates a {@link ImsCall} to make a call. 647 * 648 * @param serviceId a service id which is obtained from {@link ImsManager#open} 649 * @param profile a call profile to make the call 650 * (it contains service type, call type, media information, etc.) 651 * @param participants participants to invite the conference call 652 * @param listener listen to the call events from {@link ImsCall} 653 * @return a {@link ImsCall} object 654 * @throws ImsException if calling the IMS service results in an error 655 */ 656 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees, 657 ImsCall.Listener listener) throws ImsException { 658 if (DBG) { 659 log("makeCall :: serviceId=" + serviceId 660 + ", profile=" + profile + ", callees=" + callees); 661 } 662 663 checkAndThrowExceptionIfServiceUnavailable(); 664 665 ImsCall call = new ImsCall(mContext, profile); 666 667 call.setListener(listener); 668 ImsCallSession session = createCallSession(serviceId, profile); 669 670 if ((callees != null) && (callees.length == 1)) { 671 call.start(session, callees[0]); 672 } else { 673 call.start(session, callees); 674 } 675 676 return call; 677 } 678 679 /** 680 * Creates a {@link ImsCall} to take an incoming call. 681 * 682 * @param serviceId a service id which is obtained from {@link ImsManager#open} 683 * @param incomingCallIntent the incoming call broadcast intent 684 * @param listener to listen to the call events from {@link ImsCall} 685 * @return a {@link ImsCall} object 686 * @throws ImsException if calling the IMS service results in an error 687 */ 688 public ImsCall takeCall(int serviceId, Intent incomingCallIntent, 689 ImsCall.Listener listener) throws ImsException { 690 if (DBG) { 691 log("takeCall :: serviceId=" + serviceId 692 + ", incomingCall=" + incomingCallIntent); 693 } 694 695 checkAndThrowExceptionIfServiceUnavailable(); 696 697 if (incomingCallIntent == null) { 698 throw new ImsException("Can't retrieve session with null intent", 699 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 700 } 701 702 int incomingServiceId = getServiceId(incomingCallIntent); 703 704 if (serviceId != incomingServiceId) { 705 throw new ImsException("Service id is mismatched in the incoming call intent", 706 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 707 } 708 709 String callId = getCallId(incomingCallIntent); 710 711 if (callId == null) { 712 throw new ImsException("Call ID missing in the incoming call intent", 713 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 714 } 715 716 try { 717 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId); 718 719 if (session == null) { 720 throw new ImsException("No pending session for the call", 721 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL); 722 } 723 724 ImsCall call = new ImsCall(mContext, session.getCallProfile()); 725 726 call.attachSession(new ImsCallSession(session)); 727 call.setListener(listener); 728 729 return call; 730 } catch (Throwable t) { 731 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED); 732 } 733 } 734 735 /** 736 * Gets the config interface to get/set service/capability parameters. 737 * 738 * @return the ImsConfig instance. 739 * @throws ImsException if getting the setting interface results in an error. 740 */ 741 public ImsConfig getConfigInterface() throws ImsException { 742 743 if (mConfig == null) { 744 checkAndThrowExceptionIfServiceUnavailable(); 745 746 try { 747 IImsConfig config = mImsService.getConfigInterface(mPhoneId); 748 if (config == null) { 749 throw new ImsException("getConfigInterface()", 750 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); 751 } 752 mConfig = new ImsConfig(config, mContext); 753 } catch (RemoteException e) { 754 throw new ImsException("getConfigInterface()", e, 755 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 756 } 757 } 758 if (DBG) log("getConfigInterface(), mConfig= " + mConfig); 759 return mConfig; 760 } 761 762 public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete) 763 throws ImsException { 764 765 checkAndThrowExceptionIfServiceUnavailable(); 766 767 try { 768 mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete); 769 } catch (RemoteException e) { 770 throw new ImsException("setTTYMode()", e, 771 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 772 } 773 774 if (!getBooleanCarrierConfig(context, 775 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) { 776 setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) && 777 isEnhanced4gLteModeSettingEnabledByUser(context)); 778 } 779 } 780 781 /** 782 * Get the boolean config from carrier config manager. 783 * 784 * @param context the context to get carrier service 785 * @param key config key defined in CarrierConfigManager 786 * @return boolean value of corresponding key. 787 */ 788 private static boolean getBooleanCarrierConfig(Context context, String key) { 789 CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService( 790 Context.CARRIER_CONFIG_SERVICE); 791 if (configManager != null) { 792 return configManager.getConfig().getBoolean(key); 793 } else { 794 // Return static default defined in CarrierConfigManager. 795 return CarrierConfigManager.getDefaultConfig().getBoolean(key); 796 } 797 } 798 799 /** 800 * Gets the call ID from the specified incoming call broadcast intent. 801 * 802 * @param incomingCallIntent the incoming call broadcast intent 803 * @return the call ID or null if the intent does not contain it 804 */ 805 private static String getCallId(Intent incomingCallIntent) { 806 if (incomingCallIntent == null) { 807 return null; 808 } 809 810 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID); 811 } 812 813 /** 814 * Gets the service type from the specified incoming call broadcast intent. 815 * 816 * @param incomingCallIntent the incoming call broadcast intent 817 * @return the service identifier or -1 if the intent does not contain it 818 */ 819 private static int getServiceId(Intent incomingCallIntent) { 820 if (incomingCallIntent == null) { 821 return (-1); 822 } 823 824 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1); 825 } 826 827 /** 828 * Binds the IMS service only if the service is not created. 829 */ 830 private void checkAndThrowExceptionIfServiceUnavailable() 831 throws ImsException { 832 if (mImsService == null) { 833 createImsService(true); 834 835 if (mImsService == null) { 836 throw new ImsException("Service is unavailable", 837 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 838 } 839 } 840 } 841 842 private static String getImsServiceName(int phoneId) { 843 // TODO: MSIM implementation needs to decide on service name as a function of phoneId 844 return IMS_SERVICE; 845 } 846 847 /** 848 * Binds the IMS service to make/receive the call. 849 */ 850 private void createImsService(boolean checkService) { 851 if (checkService) { 852 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId)); 853 854 if (binder == null) { 855 return; 856 } 857 } 858 859 IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId)); 860 861 if (b != null) { 862 try { 863 b.linkToDeath(mDeathRecipient, 0); 864 } catch (RemoteException e) { 865 } 866 } 867 868 mImsService = IImsService.Stub.asInterface(b); 869 } 870 871 /** 872 * Creates a {@link ImsCallSession} with the specified call profile. 873 * Use other methods, if applicable, instead of interacting with 874 * {@link ImsCallSession} directly. 875 * 876 * @param serviceId a service id which is obtained from {@link ImsManager#open} 877 * @param profile a call profile to make the call 878 */ 879 private ImsCallSession createCallSession(int serviceId, 880 ImsCallProfile profile) throws ImsException { 881 try { 882 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null)); 883 } catch (RemoteException e) { 884 return null; 885 } 886 } 887 888 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass, 889 ImsConnectionStateListener listener) { 890 ImsRegistrationListenerProxy proxy = 891 new ImsRegistrationListenerProxy(serviceClass, listener); 892 return proxy; 893 } 894 895 private static void log(String s) { 896 Rlog.d(TAG, s); 897 } 898 899 private static void loge(String s) { 900 Rlog.e(TAG, s); 901 } 902 903 private static void loge(String s, Throwable t) { 904 Rlog.e(TAG, s, t); 905 } 906 907 /** 908 * Used for turning on IMS.if its off already 909 */ 910 private void turnOnIms() throws ImsException { 911 checkAndThrowExceptionIfServiceUnavailable(); 912 913 try { 914 mImsService.turnOnIms(mPhoneId); 915 } catch (RemoteException e) { 916 throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 917 } 918 } 919 920 private void setAdvanced4GMode(boolean turnOn) throws ImsException { 921 checkAndThrowExceptionIfServiceUnavailable(); 922 923 ImsConfig config = getConfigInterface(); 924 if (config != null) { 925 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 926 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null); 927 if (isVtEnabledByPlatform(mContext)) { 928 // TODO: once VT is available on platform replace the '1' with the current 929 // user configuration of VT. 930 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE, 931 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null); 932 } 933 } 934 935 if (turnOn) { 936 turnOnIms(); 937 } else if (getBooleanCarrierConfig(mContext, 938 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL) 939 && (!isWfcEnabledByPlatform(mContext) 940 || !isWfcEnabledByUser(mContext))) { 941 log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms"); 942 turnOffIms(); 943 } 944 } 945 946 /** 947 * Used for turning off IMS completely in order to make the device CSFB'ed. 948 * Once turned off, all calls will be over CS. 949 */ 950 private void turnOffIms() throws ImsException { 951 checkAndThrowExceptionIfServiceUnavailable(); 952 953 try { 954 mImsService.turnOffIms(mPhoneId); 955 } catch (RemoteException e) { 956 throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 957 } 958 } 959 960 /** 961 * Death recipient class for monitoring IMS service. 962 */ 963 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient { 964 @Override 965 public void binderDied() { 966 mImsService = null; 967 mUt = null; 968 mConfig = null; 969 mEcbm = null; 970 971 if (mContext != null) { 972 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN); 973 intent.putExtra(EXTRA_PHONE_ID, mPhoneId); 974 mContext.sendBroadcast(new Intent(intent)); 975 } 976 } 977 } 978 979 /** 980 * Adapter class for {@link IImsRegistrationListener}. 981 */ 982 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub { 983 private int mServiceClass; 984 private ImsConnectionStateListener mListener; 985 986 public ImsRegistrationListenerProxy(int serviceClass, 987 ImsConnectionStateListener listener) { 988 mServiceClass = serviceClass; 989 mListener = listener; 990 } 991 992 public boolean isSameProxy(int serviceClass) { 993 return (mServiceClass == serviceClass); 994 } 995 996 @Override 997 public void registrationConnected() { 998 if (DBG) { 999 log("registrationConnected ::"); 1000 } 1001 1002 if (mListener != null) { 1003 mListener.onImsConnected(); 1004 } 1005 } 1006 1007 @Override 1008 public void registrationProgressing() { 1009 if (DBG) { 1010 log("registrationProgressing ::"); 1011 } 1012 1013 if (mListener != null) { 1014 mListener.onImsProgressing(); 1015 } 1016 } 1017 1018 @Override 1019 public void registrationDisconnected(ImsReasonInfo imsReasonInfo) { 1020 if (DBG) { 1021 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo); 1022 } 1023 1024 if (mListener != null) { 1025 mListener.onImsDisconnected(imsReasonInfo); 1026 } 1027 } 1028 1029 @Override 1030 public void registrationResumed() { 1031 if (DBG) { 1032 log("registrationResumed ::"); 1033 } 1034 1035 if (mListener != null) { 1036 mListener.onImsResumed(); 1037 } 1038 } 1039 1040 @Override 1041 public void registrationSuspended() { 1042 if (DBG) { 1043 log("registrationSuspended ::"); 1044 } 1045 1046 if (mListener != null) { 1047 mListener.onImsSuspended(); 1048 } 1049 } 1050 1051 @Override 1052 public void registrationServiceCapabilityChanged(int serviceClass, int event) { 1053 log("registrationServiceCapabilityChanged :: serviceClass=" + 1054 serviceClass + ", event=" + event); 1055 1056 if (mListener != null) { 1057 mListener.onImsConnected(); 1058 } 1059 } 1060 1061 @Override 1062 public void registrationFeatureCapabilityChanged(int serviceClass, 1063 int[] enabledFeatures, int[] disabledFeatures) { 1064 log("registrationFeatureCapabilityChanged :: serviceClass=" + 1065 serviceClass); 1066 if (mListener != null) { 1067 mListener.onFeatureCapabilityChanged(serviceClass, 1068 enabledFeatures, disabledFeatures); 1069 } 1070 } 1071 1072 } 1073 /** 1074 * Gets the ECBM interface to request ECBM exit. 1075 * 1076 * @param serviceId a service id which is obtained from {@link ImsManager#open} 1077 * @return the ECBM interface instance 1078 * @throws ImsException if getting the ECBM interface results in an error 1079 */ 1080 public ImsEcbm getEcbmInterface(int serviceId) throws ImsException { 1081 if (mEcbm == null) { 1082 checkAndThrowExceptionIfServiceUnavailable(); 1083 1084 try { 1085 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId); 1086 1087 if (iEcbm == null) { 1088 throw new ImsException("getEcbmInterface()", 1089 ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED); 1090 } 1091 mEcbm = new ImsEcbm(iEcbm); 1092 } catch (RemoteException e) { 1093 throw new ImsException("getEcbmInterface()", e, 1094 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 1095 } 1096 } 1097 return mEcbm; 1098 } 1099} 1100