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