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