ImsManager.java revision d7133654b09bffcada982d2a5d37d0eeb0ffbd2d
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 WFC setting 290 */ 291 public static boolean isWfcEnabledByUser(Context context) { 292 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(), 293 android.provider.Settings.Global.WFC_IMS_ENABLED, 294 ImsConfig.FeatureValueConstants.ON); 295 return (enabled == 1) ? true : false; 296 } 297 298 /** 299 * Change persistent WFC enabled setting 300 */ 301 public static void setWfcSetting(Context context, boolean enabled) { 302 int value = enabled ? 1 : 0; 303 android.provider.Settings.Global.putInt(context.getContentResolver(), 304 android.provider.Settings.Global.WFC_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 // FIXME: replace NETWORK_TYPE_LTE with NETWORK_TYPE_IWLAN 312 // when available 313 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI, 314 TelephonyManager.NETWORK_TYPE_LTE, 315 enabled ? ImsConfig.FeatureValueConstants.ON 316 : ImsConfig.FeatureValueConstants.OFF, null); 317 318 if (enabled) { 319 imsManager.turnOnIms(); 320 } else if (context.getResources().getBoolean( 321 com.android.internal.R.bool.imsServiceAllowTurnOff) 322 && (!isVolteEnabledByPlatform(context) 323 || !isEnhanced4gLteModeSettingEnabledByUser(context))) { 324 log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms"); 325 imsManager.turnOffIms(); 326 } 327 328 // Force IMS to register over LTE when turning off WFC 329 setWfcModeInternal(context, enabled 330 ? getWfcMode(context) 331 : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED); 332 } catch (ImsException e) { 333 loge("setWfcSetting(): " + e); 334 } 335 } 336 } 337 338 /** 339 * Returns the user configuration of WFC modem setting 340 */ 341 public static int getWfcMode(Context context) { 342 int setting = android.provider.Settings.Global.getInt(context.getContentResolver(), 343 android.provider.Settings.Global.WFC_IMS_MODE, 344 ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED); 345 if (DBG) log("getWfcMode - setting=" + setting); 346 return setting; 347 } 348 349 /** 350 * Returns the user configuration of WFC modem setting 351 */ 352 public static void setWfcMode(Context context, int wfcMode) { 353 if (DBG) log("setWfcMode - setting=" + wfcMode); 354 android.provider.Settings.Global.putInt(context.getContentResolver(), 355 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode); 356 357 setWfcModeInternal(context, wfcMode); 358 } 359 360 private static void setWfcModeInternal(Context context, int wfcMode) { 361 final ImsManager imsManager = ImsManager.getInstance(context, 362 SubscriptionManager.getDefaultVoicePhoneId()); 363 if (imsManager != null) { 364 final int value = wfcMode; 365 QueuedWork.singleThreadExecutor().submit(new Runnable() { 366 public void run() { 367 try { 368 imsManager.getConfigInterface().setProvisionedValue( 369 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE, 370 value); 371 } catch (ImsException e) { 372 // do nothing 373 } 374 } 375 }); 376 } 377 } 378 379 /** 380 * Returns the user configuration of WFC roaming setting 381 */ 382 public static boolean isWfcRoamingEnabledByUser(Context context) { 383 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(), 384 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED, 385 ImsConfig.FeatureValueConstants.OFF); 386 return (enabled == 1) ? true : false; 387 } 388 389 /** 390 * Change persistent WFC roaming enabled setting 391 */ 392 public static void setWfcRoamingSetting(Context context, boolean enabled) { 393 final int value = enabled 394 ? ImsConfig.FeatureValueConstants.ON 395 : ImsConfig.FeatureValueConstants.OFF; 396 android.provider.Settings.Global.putInt(context.getContentResolver(), 397 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED, value); 398 399 final ImsManager imsManager = ImsManager.getInstance(context, 400 SubscriptionManager.getDefaultVoicePhoneId()); 401 if (imsManager != null) { 402 QueuedWork.singleThreadExecutor().submit(new Runnable() { 403 public void run() { 404 try { 405 imsManager.getConfigInterface().setProvisionedValue( 406 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING, 407 value); 408 } catch (ImsException e) { 409 // do nothing 410 } 411 } 412 }); 413 } 414 } 415 416 /** 417 * Returns a platform configuration for WFC which may override the user 418 * setting. Note: WFC presumes that VoLTE is enabled (these are 419 * configuration settings which must be done correctly). 420 */ 421 public static boolean isWfcEnabledByPlatform(Context context) { 422 if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE, 423 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) { 424 return true; 425 } 426 427 return 428 context.getResources().getBoolean( 429 com.android.internal.R.bool.config_device_wfc_ims_available) && 430 context.getResources().getBoolean( 431 com.android.internal.R.bool.config_carrier_wfc_ims_available); 432 } 433 434 private ImsManager(Context context, int phoneId) { 435 mContext = context; 436 mPhoneId = phoneId; 437 createImsService(true); 438 } 439 440 /* 441 * Returns a flag indicating whether the IMS service is available. 442 */ 443 public boolean isServiceAvailable() { 444 if (mImsService != null) { 445 return true; 446 } 447 448 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId)); 449 if (binder != null) { 450 return true; 451 } 452 453 return false; 454 } 455 456 /** 457 * Opens the IMS service for making calls and/or receiving generic IMS calls. 458 * The caller may make subsquent calls through {@link #makeCall}. 459 * The IMS service will register the device to the operator's network with the credentials 460 * (from ISIM) periodically in order to receive calls from the operator's network. 461 * When the IMS service receives a new call, it will send out an intent with 462 * the provided action string. 463 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call. 464 * 465 * @param serviceClass a service class specified in {@link ImsServiceClass} 466 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}. 467 * @param incomingCallPendingIntent When an incoming call is received, 468 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to 469 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} 470 * as the result code and the intent to fill in the call ID; It cannot be null 471 * @param listener To listen to IMS registration events; It cannot be null 472 * @return identifier (greater than 0) for the specified service 473 * @throws NullPointerException if {@code incomingCallPendingIntent} 474 * or {@code listener} is null 475 * @throws ImsException if calling the IMS service results in an error 476 * @see #getCallId 477 * @see #getServiceId 478 */ 479 public int open(int serviceClass, PendingIntent incomingCallPendingIntent, 480 ImsConnectionStateListener listener) throws ImsException { 481 checkAndThrowExceptionIfServiceUnavailable(); 482 483 if (incomingCallPendingIntent == null) { 484 throw new NullPointerException("incomingCallPendingIntent can't be null"); 485 } 486 487 if (listener == null) { 488 throw new NullPointerException("listener can't be null"); 489 } 490 491 int result = 0; 492 493 try { 494 result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent, 495 createRegistrationListenerProxy(serviceClass, listener)); 496 } catch (RemoteException e) { 497 throw new ImsException("open()", e, 498 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 499 } 500 501 if (result <= 0) { 502 // If the return value is a minus value, 503 // it means that an error occurred in the service. 504 // So, it needs to convert to the reason code specified in ImsReasonInfo. 505 throw new ImsException("open()", (result * (-1))); 506 } 507 508 return result; 509 } 510 511 /** 512 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls. 513 * All the resources that were allocated to the service are also released. 514 * 515 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open} 516 * @throws ImsException if calling the IMS service results in an error 517 */ 518 public void close(int serviceId) throws ImsException { 519 checkAndThrowExceptionIfServiceUnavailable(); 520 521 try { 522 mImsService.close(serviceId); 523 } catch (RemoteException e) { 524 throw new ImsException("close()", e, 525 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 526 } finally { 527 mUt = null; 528 mConfig = null; 529 mEcbm = null; 530 } 531 } 532 533 /** 534 * Gets the configuration interface to provision / withdraw the supplementary service settings. 535 * 536 * @param serviceId a service id which is obtained from {@link ImsManager#open} 537 * @return the Ut interface instance 538 * @throws ImsException if getting the Ut interface results in an error 539 */ 540 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId) 541 throws ImsException { 542 // FIXME: manage the multiple Ut interfaces based on the service id 543 if (mUt == null) { 544 checkAndThrowExceptionIfServiceUnavailable(); 545 546 try { 547 IImsUt iUt = mImsService.getUtInterface(serviceId); 548 549 if (iUt == null) { 550 throw new ImsException("getSupplementaryServiceConfiguration()", 551 ImsReasonInfo.CODE_UT_NOT_SUPPORTED); 552 } 553 554 mUt = new ImsUt(iUt); 555 } catch (RemoteException e) { 556 throw new ImsException("getSupplementaryServiceConfiguration()", e, 557 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 558 } 559 } 560 561 return mUt; 562 } 563 564 /** 565 * Checks if the IMS service has successfully registered to the IMS network 566 * with the specified service & call type. 567 * 568 * @param serviceId a service id which is obtained from {@link ImsManager#open} 569 * @param serviceType a service type that is specified in {@link ImsCallProfile} 570 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} 571 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} 572 * @param callType a call type that is specified in {@link ImsCallProfile} 573 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO} 574 * {@link ImsCallProfile#CALL_TYPE_VOICE} 575 * {@link ImsCallProfile#CALL_TYPE_VT} 576 * {@link ImsCallProfile#CALL_TYPE_VS} 577 * @return true if the specified service id is connected to the IMS network; 578 * false otherwise 579 * @throws ImsException if calling the IMS service results in an error 580 */ 581 public boolean isConnected(int serviceId, int serviceType, int callType) 582 throws ImsException { 583 checkAndThrowExceptionIfServiceUnavailable(); 584 585 try { 586 return mImsService.isConnected(serviceId, serviceType, callType); 587 } catch (RemoteException e) { 588 throw new ImsException("isServiceConnected()", e, 589 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 590 } 591 } 592 593 /** 594 * Checks if the specified IMS service is opend. 595 * 596 * @param serviceId a service id which is obtained from {@link ImsManager#open} 597 * @return true if the specified service id is opened; false otherwise 598 * @throws ImsException if calling the IMS service results in an error 599 */ 600 public boolean isOpened(int serviceId) throws ImsException { 601 checkAndThrowExceptionIfServiceUnavailable(); 602 603 try { 604 return mImsService.isOpened(serviceId); 605 } catch (RemoteException e) { 606 throw new ImsException("isOpened()", e, 607 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 608 } 609 } 610 611 /** 612 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. 613 * 614 * @param serviceId a service id which is obtained from {@link ImsManager#open} 615 * @param serviceType a service type that is specified in {@link ImsCallProfile} 616 * {@link ImsCallProfile#SERVICE_TYPE_NONE} 617 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} 618 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} 619 * @param callType a call type that is specified in {@link ImsCallProfile} 620 * {@link ImsCallProfile#CALL_TYPE_VOICE} 621 * {@link ImsCallProfile#CALL_TYPE_VT} 622 * {@link ImsCallProfile#CALL_TYPE_VT_TX} 623 * {@link ImsCallProfile#CALL_TYPE_VT_RX} 624 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR} 625 * {@link ImsCallProfile#CALL_TYPE_VS} 626 * {@link ImsCallProfile#CALL_TYPE_VS_TX} 627 * {@link ImsCallProfile#CALL_TYPE_VS_RX} 628 * @return a {@link ImsCallProfile} object 629 * @throws ImsException if calling the IMS service results in an error 630 */ 631 public ImsCallProfile createCallProfile(int serviceId, 632 int serviceType, int callType) throws ImsException { 633 checkAndThrowExceptionIfServiceUnavailable(); 634 635 try { 636 return mImsService.createCallProfile(serviceId, serviceType, callType); 637 } catch (RemoteException e) { 638 throw new ImsException("createCallProfile()", e, 639 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 640 } 641 } 642 643 /** 644 * Creates a {@link ImsCall} to make a call. 645 * 646 * @param serviceId a service id which is obtained from {@link ImsManager#open} 647 * @param profile a call profile to make the call 648 * (it contains service type, call type, media information, etc.) 649 * @param participants participants to invite the conference call 650 * @param listener listen to the call events from {@link ImsCall} 651 * @return a {@link ImsCall} object 652 * @throws ImsException if calling the IMS service results in an error 653 */ 654 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees, 655 ImsCall.Listener listener) throws ImsException { 656 if (DBG) { 657 log("makeCall :: serviceId=" + serviceId 658 + ", profile=" + profile + ", callees=" + callees); 659 } 660 661 checkAndThrowExceptionIfServiceUnavailable(); 662 663 ImsCall call = new ImsCall(mContext, profile); 664 665 call.setListener(listener); 666 ImsCallSession session = createCallSession(serviceId, profile); 667 668 if ((callees != null) && (callees.length == 1)) { 669 call.start(session, callees[0]); 670 } else { 671 call.start(session, callees); 672 } 673 674 return call; 675 } 676 677 /** 678 * Creates a {@link ImsCall} to take an incoming call. 679 * 680 * @param serviceId a service id which is obtained from {@link ImsManager#open} 681 * @param incomingCallIntent the incoming call broadcast intent 682 * @param listener to listen to the call events from {@link ImsCall} 683 * @return a {@link ImsCall} object 684 * @throws ImsException if calling the IMS service results in an error 685 */ 686 public ImsCall takeCall(int serviceId, Intent incomingCallIntent, 687 ImsCall.Listener listener) throws ImsException { 688 if (DBG) { 689 log("takeCall :: serviceId=" + serviceId 690 + ", incomingCall=" + incomingCallIntent); 691 } 692 693 checkAndThrowExceptionIfServiceUnavailable(); 694 695 if (incomingCallIntent == null) { 696 throw new ImsException("Can't retrieve session with null intent", 697 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 698 } 699 700 int incomingServiceId = getServiceId(incomingCallIntent); 701 702 if (serviceId != incomingServiceId) { 703 throw new ImsException("Service id is mismatched in the incoming call intent", 704 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 705 } 706 707 String callId = getCallId(incomingCallIntent); 708 709 if (callId == null) { 710 throw new ImsException("Call ID missing in the incoming call intent", 711 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 712 } 713 714 try { 715 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId); 716 717 if (session == null) { 718 throw new ImsException("No pending session for the call", 719 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL); 720 } 721 722 ImsCall call = new ImsCall(mContext, session.getCallProfile()); 723 724 call.attachSession(new ImsCallSession(session)); 725 call.setListener(listener); 726 727 return call; 728 } catch (Throwable t) { 729 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED); 730 } 731 } 732 733 /** 734 * Gets the config interface to get/set service/capability parameters. 735 * 736 * @return the ImsConfig instance. 737 * @throws ImsException if getting the setting interface results in an error. 738 */ 739 public ImsConfig getConfigInterface() throws ImsException { 740 741 if (mConfig == null) { 742 checkAndThrowExceptionIfServiceUnavailable(); 743 744 try { 745 IImsConfig config = mImsService.getConfigInterface(mPhoneId); 746 if (config == null) { 747 throw new ImsException("getConfigInterface()", 748 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); 749 } 750 mConfig = new ImsConfig(config, mContext); 751 } catch (RemoteException e) { 752 throw new ImsException("getConfigInterface()", e, 753 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 754 } 755 } 756 if (DBG) log("getConfigInterface(), mConfig= " + mConfig); 757 return mConfig; 758 } 759 760 public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete) 761 throws ImsException { 762 763 checkAndThrowExceptionIfServiceUnavailable(); 764 765 try { 766 mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete); 767 } catch (RemoteException e) { 768 throw new ImsException("setTTYMode()", e, 769 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 770 } 771 772 if (!context.getResources().getBoolean( 773 com.android.internal.R.bool.config_carrier_volte_tty_supported)) { 774 setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) && 775 isEnhanced4gLteModeSettingEnabledByUser(context)); 776 } 777 } 778 779 /** 780 * Gets the call ID from the specified incoming call broadcast intent. 781 * 782 * @param incomingCallIntent the incoming call broadcast intent 783 * @return the call ID or null if the intent does not contain it 784 */ 785 private static String getCallId(Intent incomingCallIntent) { 786 if (incomingCallIntent == null) { 787 return null; 788 } 789 790 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID); 791 } 792 793 /** 794 * Gets the service type from the specified incoming call broadcast intent. 795 * 796 * @param incomingCallIntent the incoming call broadcast intent 797 * @return the service identifier or -1 if the intent does not contain it 798 */ 799 private static int getServiceId(Intent incomingCallIntent) { 800 if (incomingCallIntent == null) { 801 return (-1); 802 } 803 804 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1); 805 } 806 807 /** 808 * Binds the IMS service only if the service is not created. 809 */ 810 private void checkAndThrowExceptionIfServiceUnavailable() 811 throws ImsException { 812 if (mImsService == null) { 813 createImsService(true); 814 815 if (mImsService == null) { 816 throw new ImsException("Service is unavailable", 817 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 818 } 819 } 820 } 821 822 private static String getImsServiceName(int phoneId) { 823 // TODO: MSIM implementation needs to decide on service name as a function of phoneId 824 return IMS_SERVICE; 825 } 826 827 /** 828 * Binds the IMS service to make/receive the call. 829 */ 830 private void createImsService(boolean checkService) { 831 if (checkService) { 832 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId)); 833 834 if (binder == null) { 835 return; 836 } 837 } 838 839 IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId)); 840 841 if (b != null) { 842 try { 843 b.linkToDeath(mDeathRecipient, 0); 844 } catch (RemoteException e) { 845 } 846 } 847 848 mImsService = IImsService.Stub.asInterface(b); 849 } 850 851 /** 852 * Creates a {@link ImsCallSession} with the specified call profile. 853 * Use other methods, if applicable, instead of interacting with 854 * {@link ImsCallSession} directly. 855 * 856 * @param serviceId a service id which is obtained from {@link ImsManager#open} 857 * @param profile a call profile to make the call 858 */ 859 private ImsCallSession createCallSession(int serviceId, 860 ImsCallProfile profile) throws ImsException { 861 try { 862 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null)); 863 } catch (RemoteException e) { 864 return null; 865 } 866 } 867 868 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass, 869 ImsConnectionStateListener listener) { 870 ImsRegistrationListenerProxy proxy = 871 new ImsRegistrationListenerProxy(serviceClass, listener); 872 return proxy; 873 } 874 875 private static void log(String s) { 876 Rlog.d(TAG, s); 877 } 878 879 private static void loge(String s) { 880 Rlog.e(TAG, s); 881 } 882 883 private static void loge(String s, Throwable t) { 884 Rlog.e(TAG, s, t); 885 } 886 887 /** 888 * Used for turning on IMS.if its off already 889 */ 890 private void turnOnIms() throws ImsException { 891 checkAndThrowExceptionIfServiceUnavailable(); 892 893 try { 894 mImsService.turnOnIms(mPhoneId); 895 } catch (RemoteException e) { 896 throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 897 } 898 } 899 900 private void setAdvanced4GMode(boolean turnOn) throws ImsException { 901 checkAndThrowExceptionIfServiceUnavailable(); 902 903 ImsConfig config = getConfigInterface(); 904 if (config != null) { 905 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 906 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null); 907 if (isVtEnabledByPlatform(mContext)) { 908 // TODO: once VT is available on platform replace the '1' with the current 909 // user configuration of VT. 910 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE, 911 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null); 912 } 913 } 914 915 if (turnOn) { 916 turnOnIms(); 917 } else if (mContext.getResources().getBoolean( 918 com.android.internal.R.bool.imsServiceAllowTurnOff) 919 && (!isWfcEnabledByPlatform(mContext) 920 || !isWfcEnabledByUser(mContext))) { 921 log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms"); 922 turnOffIms(); 923 } 924 } 925 926 /** 927 * Used for turning off IMS completely in order to make the device CSFB'ed. 928 * Once turned off, all calls will be over CS. 929 */ 930 private void turnOffIms() throws ImsException { 931 checkAndThrowExceptionIfServiceUnavailable(); 932 933 try { 934 mImsService.turnOffIms(mPhoneId); 935 } catch (RemoteException e) { 936 throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 937 } 938 } 939 940 /** 941 * Death recipient class for monitoring IMS service. 942 */ 943 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient { 944 @Override 945 public void binderDied() { 946 mImsService = null; 947 mUt = null; 948 mConfig = null; 949 mEcbm = null; 950 951 if (mContext != null) { 952 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN); 953 intent.putExtra(EXTRA_PHONE_ID, mPhoneId); 954 mContext.sendBroadcast(new Intent(intent)); 955 } 956 } 957 } 958 959 /** 960 * Adapter class for {@link IImsRegistrationListener}. 961 */ 962 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub { 963 private int mServiceClass; 964 private ImsConnectionStateListener mListener; 965 966 public ImsRegistrationListenerProxy(int serviceClass, 967 ImsConnectionStateListener listener) { 968 mServiceClass = serviceClass; 969 mListener = listener; 970 } 971 972 public boolean isSameProxy(int serviceClass) { 973 return (mServiceClass == serviceClass); 974 } 975 976 @Override 977 public void registrationConnected() { 978 if (DBG) { 979 log("registrationConnected ::"); 980 } 981 982 if (mListener != null) { 983 mListener.onImsConnected(); 984 } 985 } 986 987 @Override 988 public void registrationProgressing() { 989 if (DBG) { 990 log("registrationProgressing ::"); 991 } 992 993 if (mListener != null) { 994 mListener.onImsProgressing(); 995 } 996 } 997 998 @Override 999 public void registrationDisconnected(ImsReasonInfo imsReasonInfo) { 1000 if (DBG) { 1001 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo); 1002 } 1003 1004 if (mListener != null) { 1005 mListener.onImsDisconnected(imsReasonInfo); 1006 } 1007 } 1008 1009 @Override 1010 public void registrationResumed() { 1011 if (DBG) { 1012 log("registrationResumed ::"); 1013 } 1014 1015 if (mListener != null) { 1016 mListener.onImsResumed(); 1017 } 1018 } 1019 1020 @Override 1021 public void registrationSuspended() { 1022 if (DBG) { 1023 log("registrationSuspended ::"); 1024 } 1025 1026 if (mListener != null) { 1027 mListener.onImsSuspended(); 1028 } 1029 } 1030 1031 @Override 1032 public void registrationServiceCapabilityChanged(int serviceClass, int event) { 1033 log("registrationServiceCapabilityChanged :: serviceClass=" + 1034 serviceClass + ", event=" + event); 1035 1036 if (mListener != null) { 1037 mListener.onImsConnected(); 1038 } 1039 } 1040 1041 @Override 1042 public void registrationFeatureCapabilityChanged(int serviceClass, 1043 int[] enabledFeatures, int[] disabledFeatures) { 1044 log("registrationFeatureCapabilityChanged :: serviceClass=" + 1045 serviceClass); 1046 if (mListener != null) { 1047 mListener.onFeatureCapabilityChanged(serviceClass, 1048 enabledFeatures, disabledFeatures); 1049 } 1050 } 1051 1052 } 1053 /** 1054 * Gets the ECBM interface to request ECBM exit. 1055 * 1056 * @param serviceId a service id which is obtained from {@link ImsManager#open} 1057 * @return the ECBM interface instance 1058 * @throws ImsException if getting the ECBM interface results in an error 1059 */ 1060 public ImsEcbm getEcbmInterface(int serviceId) throws ImsException { 1061 if (mEcbm == null) { 1062 checkAndThrowExceptionIfServiceUnavailable(); 1063 1064 try { 1065 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId); 1066 1067 if (iEcbm == null) { 1068 throw new ImsException("getEcbmInterface()", 1069 ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED); 1070 } 1071 mEcbm = new ImsEcbm(iEcbm); 1072 } catch (RemoteException e) { 1073 throw new ImsException("getEcbmInterface()", e, 1074 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 1075 } 1076 } 1077 return mEcbm; 1078 } 1079} 1080