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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 277 private ImsManager(Context context, int phoneId) { 278 mContext = context; 279 mPhoneId = phoneId; 280 createImsService(true); 281 } 282 283 /* 284 * Returns a flag indicating whether the IMS service is available. 285 */ 286 public boolean isServiceAvailable() { 287 if (mImsService != null) { 288 return true; 289 } 290 291 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId)); 292 if (binder != null) { 293 return true; 294 } 295 296 return false; 297 } 298 299 /** 300 * Opens the IMS service for making calls and/or receiving generic IMS calls. 301 * The caller may make subsquent calls through {@link #makeCall}. 302 * The IMS service will register the device to the operator's network with the credentials 303 * (from ISIM) periodically in order to receive calls from the operator's network. 304 * When the IMS service receives a new call, it will send out an intent with 305 * the provided action string. 306 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call. 307 * 308 * @param serviceClass a service class specified in {@link ImsServiceClass} 309 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}. 310 * @param incomingCallPendingIntent When an incoming call is received, 311 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to 312 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} 313 * as the result code and the intent to fill in the call ID; It cannot be null 314 * @param listener To listen to IMS registration events; It cannot be null 315 * @return identifier (greater than 0) for the specified service 316 * @throws NullPointerException if {@code incomingCallPendingIntent} 317 * or {@code listener} is null 318 * @throws ImsException if calling the IMS service results in an error 319 * @see #getCallId 320 * @see #getServiceId 321 */ 322 public int open(int serviceClass, PendingIntent incomingCallPendingIntent, 323 ImsConnectionStateListener listener) throws ImsException { 324 checkAndThrowExceptionIfServiceUnavailable(); 325 326 if (incomingCallPendingIntent == null) { 327 throw new NullPointerException("incomingCallPendingIntent can't be null"); 328 } 329 330 if (listener == null) { 331 throw new NullPointerException("listener can't be null"); 332 } 333 334 int result = 0; 335 336 try { 337 result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent, 338 createRegistrationListenerProxy(serviceClass, listener)); 339 } catch (RemoteException e) { 340 throw new ImsException("open()", e, 341 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 342 } 343 344 if (result <= 0) { 345 // If the return value is a minus value, 346 // it means that an error occurred in the service. 347 // So, it needs to convert to the reason code specified in ImsReasonInfo. 348 throw new ImsException("open()", (result * (-1))); 349 } 350 351 return result; 352 } 353 354 /** 355 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls. 356 * All the resources that were allocated to the service are also released. 357 * 358 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open} 359 * @throws ImsException if calling the IMS service results in an error 360 */ 361 public void close(int serviceId) throws ImsException { 362 checkAndThrowExceptionIfServiceUnavailable(); 363 364 try { 365 mImsService.close(serviceId); 366 } catch (RemoteException e) { 367 throw new ImsException("close()", e, 368 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 369 } finally { 370 mUt = null; 371 mConfig = null; 372 mEcbm = null; 373 } 374 } 375 376 /** 377 * Gets the configuration interface to provision / withdraw the supplementary service settings. 378 * 379 * @param serviceId a service id which is obtained from {@link ImsManager#open} 380 * @return the Ut interface instance 381 * @throws ImsException if getting the Ut interface results in an error 382 */ 383 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId) 384 throws ImsException { 385 // FIXME: manage the multiple Ut interfaces based on the service id 386 if (mUt == null) { 387 checkAndThrowExceptionIfServiceUnavailable(); 388 389 try { 390 IImsUt iUt = mImsService.getUtInterface(serviceId); 391 392 if (iUt == null) { 393 throw new ImsException("getSupplementaryServiceConfiguration()", 394 ImsReasonInfo.CODE_UT_NOT_SUPPORTED); 395 } 396 397 mUt = new ImsUt(iUt); 398 } catch (RemoteException e) { 399 throw new ImsException("getSupplementaryServiceConfiguration()", e, 400 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 401 } 402 } 403 404 return mUt; 405 } 406 407 /** 408 * Checks if the IMS service has successfully registered to the IMS network 409 * with the specified service & call type. 410 * 411 * @param serviceId a service id which is obtained from {@link ImsManager#open} 412 * @param serviceType a service type that is specified in {@link ImsCallProfile} 413 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} 414 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} 415 * @param callType a call type that is specified in {@link ImsCallProfile} 416 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO} 417 * {@link ImsCallProfile#CALL_TYPE_VOICE} 418 * {@link ImsCallProfile#CALL_TYPE_VT} 419 * {@link ImsCallProfile#CALL_TYPE_VS} 420 * @return true if the specified service id is connected to the IMS network; 421 * false otherwise 422 * @throws ImsException if calling the IMS service results in an error 423 */ 424 public boolean isConnected(int serviceId, int serviceType, int callType) 425 throws ImsException { 426 checkAndThrowExceptionIfServiceUnavailable(); 427 428 try { 429 return mImsService.isConnected(serviceId, serviceType, callType); 430 } catch (RemoteException e) { 431 throw new ImsException("isServiceConnected()", e, 432 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 433 } 434 } 435 436 /** 437 * Checks if the specified IMS service is opend. 438 * 439 * @param serviceId a service id which is obtained from {@link ImsManager#open} 440 * @return true if the specified service id is opened; false otherwise 441 * @throws ImsException if calling the IMS service results in an error 442 */ 443 public boolean isOpened(int serviceId) throws ImsException { 444 checkAndThrowExceptionIfServiceUnavailable(); 445 446 try { 447 return mImsService.isOpened(serviceId); 448 } catch (RemoteException e) { 449 throw new ImsException("isOpened()", e, 450 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 451 } 452 } 453 454 /** 455 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. 456 * 457 * @param serviceId a service id which is obtained from {@link ImsManager#open} 458 * @param serviceType a service type that is specified in {@link ImsCallProfile} 459 * {@link ImsCallProfile#SERVICE_TYPE_NONE} 460 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} 461 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} 462 * @param callType a call type that is specified in {@link ImsCallProfile} 463 * {@link ImsCallProfile#CALL_TYPE_VOICE} 464 * {@link ImsCallProfile#CALL_TYPE_VT} 465 * {@link ImsCallProfile#CALL_TYPE_VT_TX} 466 * {@link ImsCallProfile#CALL_TYPE_VT_RX} 467 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR} 468 * {@link ImsCallProfile#CALL_TYPE_VS} 469 * {@link ImsCallProfile#CALL_TYPE_VS_TX} 470 * {@link ImsCallProfile#CALL_TYPE_VS_RX} 471 * @return a {@link ImsCallProfile} object 472 * @throws ImsException if calling the IMS service results in an error 473 */ 474 public ImsCallProfile createCallProfile(int serviceId, 475 int serviceType, int callType) throws ImsException { 476 checkAndThrowExceptionIfServiceUnavailable(); 477 478 try { 479 return mImsService.createCallProfile(serviceId, serviceType, callType); 480 } catch (RemoteException e) { 481 throw new ImsException("createCallProfile()", e, 482 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 483 } 484 } 485 486 /** 487 * Creates a {@link ImsCall} to make a call. 488 * 489 * @param serviceId a service id which is obtained from {@link ImsManager#open} 490 * @param profile a call profile to make the call 491 * (it contains service type, call type, media information, etc.) 492 * @param participants participants to invite the conference call 493 * @param listener listen to the call events from {@link ImsCall} 494 * @return a {@link ImsCall} object 495 * @throws ImsException if calling the IMS service results in an error 496 */ 497 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees, 498 ImsCall.Listener listener) throws ImsException { 499 if (DBG) { 500 log("makeCall :: serviceId=" + serviceId 501 + ", profile=" + profile + ", callees=" + callees); 502 } 503 504 checkAndThrowExceptionIfServiceUnavailable(); 505 506 ImsCall call = new ImsCall(mContext, profile); 507 508 call.setListener(listener); 509 ImsCallSession session = createCallSession(serviceId, profile); 510 511 if ((callees != null) && (callees.length == 1)) { 512 call.start(session, callees[0]); 513 } else { 514 call.start(session, callees); 515 } 516 517 return call; 518 } 519 520 /** 521 * Creates a {@link ImsCall} to take an incoming call. 522 * 523 * @param serviceId a service id which is obtained from {@link ImsManager#open} 524 * @param incomingCallIntent the incoming call broadcast intent 525 * @param listener to listen to the call events from {@link ImsCall} 526 * @return a {@link ImsCall} object 527 * @throws ImsException if calling the IMS service results in an error 528 */ 529 public ImsCall takeCall(int serviceId, Intent incomingCallIntent, 530 ImsCall.Listener listener) throws ImsException { 531 if (DBG) { 532 log("takeCall :: serviceId=" + serviceId 533 + ", incomingCall=" + incomingCallIntent); 534 } 535 536 checkAndThrowExceptionIfServiceUnavailable(); 537 538 if (incomingCallIntent == null) { 539 throw new ImsException("Can't retrieve session with null intent", 540 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 541 } 542 543 int incomingServiceId = getServiceId(incomingCallIntent); 544 545 if (serviceId != incomingServiceId) { 546 throw new ImsException("Service id is mismatched in the incoming call intent", 547 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 548 } 549 550 String callId = getCallId(incomingCallIntent); 551 552 if (callId == null) { 553 throw new ImsException("Call ID missing in the incoming call intent", 554 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 555 } 556 557 try { 558 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId); 559 560 if (session == null) { 561 throw new ImsException("No pending session for the call", 562 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL); 563 } 564 565 ImsCall call = new ImsCall(mContext, session.getCallProfile()); 566 567 call.attachSession(new ImsCallSession(session)); 568 call.setListener(listener); 569 570 return call; 571 } catch (Throwable t) { 572 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED); 573 } 574 } 575 576 /** 577 * Gets the config interface to get/set service/capability parameters. 578 * 579 * @return the ImsConfig instance. 580 * @throws ImsException if getting the setting interface results in an error. 581 */ 582 public ImsConfig getConfigInterface() throws ImsException { 583 584 if (mConfig == null) { 585 checkAndThrowExceptionIfServiceUnavailable(); 586 587 try { 588 IImsConfig config = mImsService.getConfigInterface(mPhoneId); 589 if (config == null) { 590 throw new ImsException("getConfigInterface()", 591 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); 592 } 593 mConfig = new ImsConfig(config, mContext); 594 } catch (RemoteException e) { 595 throw new ImsException("getConfigInterface()", e, 596 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 597 } 598 } 599 if (DBG) log("getConfigInterface(), mConfig= " + mConfig); 600 return mConfig; 601 } 602 603 public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete) 604 throws ImsException { 605 606 checkAndThrowExceptionIfServiceUnavailable(); 607 608 try { 609 mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete); 610 } catch (RemoteException e) { 611 throw new ImsException("setTTYMode()", e, 612 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 613 } 614 615 if (!context.getResources().getBoolean( 616 com.android.internal.R.bool.config_carrier_volte_tty_supported)) { 617 setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) && 618 isEnhanced4gLteModeSettingEnabledByUser(context)); 619 } 620 } 621 622 /** 623 * Gets the call ID from the specified incoming call broadcast intent. 624 * 625 * @param incomingCallIntent the incoming call broadcast intent 626 * @return the call ID or null if the intent does not contain it 627 */ 628 private static String getCallId(Intent incomingCallIntent) { 629 if (incomingCallIntent == null) { 630 return null; 631 } 632 633 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID); 634 } 635 636 /** 637 * Gets the service type from the specified incoming call broadcast intent. 638 * 639 * @param incomingCallIntent the incoming call broadcast intent 640 * @return the service identifier or -1 if the intent does not contain it 641 */ 642 private static int getServiceId(Intent incomingCallIntent) { 643 if (incomingCallIntent == null) { 644 return (-1); 645 } 646 647 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1); 648 } 649 650 /** 651 * Binds the IMS service only if the service is not created. 652 */ 653 private void checkAndThrowExceptionIfServiceUnavailable() 654 throws ImsException { 655 if (mImsService == null) { 656 createImsService(true); 657 658 if (mImsService == null) { 659 throw new ImsException("Service is unavailable", 660 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 661 } 662 } 663 } 664 665 private static String getImsServiceName(int phoneId) { 666 // TODO: MSIM implementation needs to decide on service name as a function of phoneId 667 return IMS_SERVICE; 668 } 669 670 /** 671 * Binds the IMS service to make/receive the call. 672 */ 673 private void createImsService(boolean checkService) { 674 if (checkService) { 675 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId)); 676 677 if (binder == null) { 678 return; 679 } 680 } 681 682 IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId)); 683 684 if (b != null) { 685 try { 686 b.linkToDeath(mDeathRecipient, 0); 687 } catch (RemoteException e) { 688 } 689 } 690 691 mImsService = IImsService.Stub.asInterface(b); 692 } 693 694 /** 695 * Creates a {@link ImsCallSession} with the specified call profile. 696 * Use other methods, if applicable, instead of interacting with 697 * {@link ImsCallSession} directly. 698 * 699 * @param serviceId a service id which is obtained from {@link ImsManager#open} 700 * @param profile a call profile to make the call 701 */ 702 private ImsCallSession createCallSession(int serviceId, 703 ImsCallProfile profile) throws ImsException { 704 try { 705 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null)); 706 } catch (RemoteException e) { 707 return null; 708 } 709 } 710 711 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass, 712 ImsConnectionStateListener listener) { 713 ImsRegistrationListenerProxy proxy = 714 new ImsRegistrationListenerProxy(serviceClass, listener); 715 return proxy; 716 } 717 718 private void log(String s) { 719 Rlog.d(TAG, s); 720 } 721 722 private void loge(String s) { 723 Rlog.e(TAG, s); 724 } 725 726 private void loge(String s, Throwable t) { 727 Rlog.e(TAG, s, t); 728 } 729 730 /** 731 * Used for turning on IMS.if its off already 732 */ 733 private void turnOnIms() throws ImsException { 734 checkAndThrowExceptionIfServiceUnavailable(); 735 736 try { 737 mImsService.turnOnIms(mPhoneId); 738 } catch (RemoteException e) { 739 throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 740 } 741 } 742 743 private void setAdvanced4GMode(boolean turnOn) throws ImsException { 744 checkAndThrowExceptionIfServiceUnavailable(); 745 746 ImsConfig config = getConfigInterface(); 747 if (config != null) { 748 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 749 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null); 750 if (isVtEnabledByPlatform(mContext)) { 751 // TODO: once VT is available on platform replace the '1' with the current 752 // user configuration of VT. 753 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE, 754 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null); 755 } 756 } 757 758 if (turnOn) { 759 turnOnIms(); 760 } else if (mContext.getResources().getBoolean( 761 com.android.internal.R.bool.imsServiceAllowTurnOff)) { 762 log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms"); 763 turnOffIms(); 764 } 765 } 766 767 /** 768 * Used for turning off IMS completely in order to make the device CSFB'ed. 769 * Once turned off, all calls will be over CS. 770 */ 771 private void turnOffIms() throws ImsException { 772 checkAndThrowExceptionIfServiceUnavailable(); 773 774 try { 775 mImsService.turnOffIms(mPhoneId); 776 } catch (RemoteException e) { 777 throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 778 } 779 } 780 781 /** 782 * Death recipient class for monitoring IMS service. 783 */ 784 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient { 785 @Override 786 public void binderDied() { 787 mImsService = null; 788 mUt = null; 789 mConfig = null; 790 mEcbm = null; 791 792 if (mContext != null) { 793 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN); 794 intent.putExtra(EXTRA_PHONE_ID, mPhoneId); 795 mContext.sendBroadcast(new Intent(intent)); 796 } 797 } 798 } 799 800 /** 801 * Adapter class for {@link IImsRegistrationListener}. 802 */ 803 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub { 804 private int mServiceClass; 805 private ImsConnectionStateListener mListener; 806 807 public ImsRegistrationListenerProxy(int serviceClass, 808 ImsConnectionStateListener listener) { 809 mServiceClass = serviceClass; 810 mListener = listener; 811 } 812 813 public boolean isSameProxy(int serviceClass) { 814 return (mServiceClass == serviceClass); 815 } 816 817 @Override 818 public void registrationConnected() { 819 if (DBG) { 820 log("registrationConnected ::"); 821 } 822 823 if (mListener != null) { 824 mListener.onImsConnected(); 825 } 826 } 827 828 @Override 829 public void registrationDisconnected() { 830 if (DBG) { 831 log("registrationDisconnected ::"); 832 } 833 834 if (mListener != null) { 835 mListener.onImsDisconnected(); 836 } 837 } 838 839 @Override 840 public void registrationResumed() { 841 if (DBG) { 842 log("registrationResumed ::"); 843 } 844 845 if (mListener != null) { 846 mListener.onImsResumed(); 847 } 848 } 849 850 @Override 851 public void registrationSuspended() { 852 if (DBG) { 853 log("registrationSuspended ::"); 854 } 855 856 if (mListener != null) { 857 mListener.onImsSuspended(); 858 } 859 } 860 861 @Override 862 public void registrationServiceCapabilityChanged(int serviceClass, int event) { 863 log("registrationServiceCapabilityChanged :: serviceClass=" + 864 serviceClass + ", event=" + event); 865 866 if (mListener != null) { 867 mListener.onImsConnected(); 868 } 869 } 870 871 @Override 872 public void registrationFeatureCapabilityChanged(int serviceClass, 873 int[] enabledFeatures, int[] disabledFeatures) { 874 log("registrationFeatureCapabilityChanged :: serviceClass=" + 875 serviceClass); 876 if (mListener != null) { 877 mListener.onFeatureCapabilityChanged(serviceClass, 878 enabledFeatures, disabledFeatures); 879 } 880 } 881 882 } 883 /** 884 * Gets the ECBM interface to request ECBM exit. 885 * 886 * @param serviceId a service id which is obtained from {@link ImsManager#open} 887 * @return the ECBM interface instance 888 * @throws ImsException if getting the ECBM interface results in an error 889 */ 890 public ImsEcbm getEcbmInterface(int serviceId) throws ImsException { 891 if (mEcbm == null) { 892 checkAndThrowExceptionIfServiceUnavailable(); 893 894 try { 895 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId); 896 897 if (iEcbm == null) { 898 throw new ImsException("getEcbmInterface()", 899 ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED); 900 } 901 mEcbm = new ImsEcbm(iEcbm); 902 } catch (RemoteException e) { 903 throw new ImsException("getEcbmInterface()", e, 904 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 905 } 906 } 907 return mEcbm; 908 } 909 } 910