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