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