ImsManager.java revision 076c55df56906453aade4a741b435f1df72778a2
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.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.os.IBinder; 24import android.os.IBinder.DeathRecipient; 25import android.os.Message; 26import android.os.Process; 27import android.os.RemoteException; 28import android.os.ServiceManager; 29import android.telephony.Rlog; 30 31import com.android.ims.internal.IImsCallSession; 32import com.android.ims.internal.IImsRegistrationListener; 33import com.android.ims.internal.IImsService; 34import com.android.ims.internal.IImsUt; 35import com.android.ims.internal.ImsCallSession; 36import com.android.ims.internal.IImsConfig; 37 38 39/** 40 * Provides APIs for IMS services, such as initiating IMS calls, and provides access to 41 * the operator's IMS network. This class is the starting point for any IMS actions. 42 * You can acquire an instance of it with {@link #getInstance getInstance()}.</p> 43 * <p>The APIs in this class allows you to:</p> 44 * 45 * @hide 46 */ 47public class ImsManager { 48 /** 49 * For accessing the IMS related service. 50 * Internal use only. 51 * @hide 52 */ 53 public static final String IMS_SERVICE = "ims"; 54 55 /** 56 * The result code to be sent back with the incoming call {@link PendingIntent}. 57 * @see #open(PendingIntent, ImsConnectionStateListener) 58 */ 59 public static final int INCOMING_CALL_RESULT_CODE = 101; 60 61 /** 62 * Key to retrieve the call ID from an incoming call intent. 63 * @see #open(PendingIntent, ImsConnectionStateListener) 64 */ 65 public static final String EXTRA_CALL_ID = "android:imsCallID"; 66 67 /** 68 * Action to broadcast when ImsService is up. 69 * Internal use only. 70 * @hide 71 */ 72 public static final String ACTION_IMS_SERVICE_UP = 73 "com.android.ims.IMS_SERVICE_UP"; 74 75 /** 76 * Action to broadcast when ImsService is down. 77 * Internal use only. 78 * @hide 79 */ 80 public static final String ACTION_IMS_SERVICE_DOWN = 81 "com.android.ims.IMS_SERVICE_DOWN"; 82 83 /** 84 * Action for the incoming call intent for the Phone app. 85 * Internal use only. 86 * @hide 87 */ 88 public static final String ACTION_IMS_INCOMING_CALL = 89 "com.android.ims.IMS_INCOMING_CALL"; 90 91 /** 92 * Part of the ACTION_IMS_INCOMING_CALL intents. 93 * An integer value; service identifier obtained from {@link ImsManager#open}. 94 * Internal use only. 95 * @hide 96 */ 97 public static final String EXTRA_SERVICE_ID = "android:imsServiceId"; 98 99 /** 100 * Part of the ACTION_IMS_INCOMING_CALL intents. 101 * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD. 102 * The value "true" indicates that the incoming call is for USSD. 103 * Internal use only. 104 * @hide 105 */ 106 public static final String EXTRA_USSD = "android:ussd"; 107 108 109 110 private static final String TAG = "ImsManager"; 111 private static final boolean DBG = true; 112 113 private static ImsManager mImsManager = null; 114 private Context mContext; 115 private IImsService mImsService = null; 116 private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient(); 117 // Ut interface for the supplementary service configuration 118 private ImsUt mUt = null; 119 // Interface to get/set ims config items 120 private ImsConfig mConfig = null; 121 122 /** 123 * Gets a manager instance. 124 * 125 * @param context application context for creating the manager object 126 * @return the manager instance 127 */ 128 public static ImsManager getInstance(Context context) { 129 if (mImsManager == null) { 130 mImsManager = new ImsManager(context); 131 } 132 133 return mImsManager; 134 } 135 136 private ImsManager(Context context) { 137 mContext = context; 138 createImsService(true); 139 } 140 141 /** 142 * Opens the IMS service for making calls and/or receiving generic IMS calls. 143 * The caller may make subsquent calls through {@link #makeCall}. 144 * The IMS service will register the device to the operator's network with the credentials 145 * (from ISIM) periodically in order to receive calls from the operator's network. 146 * When the IMS service receives a new call, it will send out an intent with 147 * the provided action string. 148 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call. 149 * 150 * @param serviceClass a service class specified in {@link ImsServiceClass} 151 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}. 152 * @param incomingCallPendingIntent When an incoming call is received, 153 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to 154 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} 155 * as the result code and the intent to fill in the call ID; It cannot be null 156 * @param listener To listen to IMS registration events; It cannot be null 157 * @return identifier (greater than 0) for the specified service 158 * @throws NullPointerException if {@code incomingCallPendingIntent} 159 * or {@code listener} is null 160 * @throws ImsException if calling the IMS service results in an error 161 * @see #getCallId 162 * @see #getServiceId 163 */ 164 public int open(int serviceClass, PendingIntent incomingCallPendingIntent, 165 ImsConnectionStateListener listener) throws ImsException { 166 checkAndThrowExceptionIfServiceUnavailable(); 167 168 if (incomingCallPendingIntent == null) { 169 throw new NullPointerException("incomingCallPendingIntent can't be null"); 170 } 171 172 if (listener == null) { 173 throw new NullPointerException("listener can't be null"); 174 } 175 176 int result = 0; 177 178 try { 179 result = mImsService.open(serviceClass, incomingCallPendingIntent, 180 createRegistrationListenerProxy(serviceClass, listener)); 181 } catch (RemoteException e) { 182 throw new ImsException("open()", e, 183 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 184 } 185 186 if (result <= 0) { 187 // If the return value is a minus value, 188 // it means that an error occurred in the service. 189 // So, it needs to convert to the reason code specified in ImsReasonInfo. 190 throw new ImsException("open()", (result * (-1))); 191 } 192 193 return result; 194 } 195 196 /** 197 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls. 198 * All the resources that were allocated to the service are also released. 199 * 200 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open} 201 * @throws ImsException if calling the IMS service results in an error 202 */ 203 public void close(int serviceId) throws ImsException { 204 checkAndThrowExceptionIfServiceUnavailable(); 205 206 try { 207 mImsService.close(serviceId); 208 } catch (RemoteException e) { 209 throw new ImsException("close()", e, 210 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 211 } finally { 212 mUt = null; 213 mConfig = null; 214 } 215 } 216 217 /** 218 * Gets the configuration interface to provision / withdraw the supplementary service settings. 219 * 220 * @param serviceId a service id which is obtained from {@link ImsManager#open} 221 * @return the Ut interface instance 222 * @throws ImsException if getting the Ut interface results in an error 223 */ 224 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId) 225 throws ImsException { 226 // FIXME: manage the multiple Ut interfaces based on the service id 227 if (mUt == null) { 228 checkAndThrowExceptionIfServiceUnavailable(); 229 230 try { 231 IImsUt iUt = mImsService.getUtInterface(serviceId); 232 233 if (iUt == null) { 234 throw new ImsException("getSupplementaryServiceConfiguration()", 235 ImsReasonInfo.CODE_UT_NOT_SUPPORTED); 236 } 237 238 mUt = new ImsUt(iUt); 239 } catch (RemoteException e) { 240 throw new ImsException("getSupplementaryServiceConfiguration()", e, 241 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 242 } 243 } 244 245 return mUt; 246 } 247 248 /** 249 * Checks if the IMS service has successfully registered to the IMS network 250 * with the specified service & call type. 251 * 252 * @param serviceId a service id which is obtained from {@link ImsManager#open} 253 * @param serviceType a service type that is specified in {@link ImsCallProfile} 254 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} 255 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} 256 * @param callType a call type that is specified in {@link ImsCallProfile} 257 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO} 258 * {@link ImsCallProfile#CALL_TYPE_VOICE} 259 * {@link ImsCallProfile#CALL_TYPE_VT} 260 * {@link ImsCallProfile#CALL_TYPE_VS} 261 * @return true if the specified service id is connected to the IMS network; 262 * false otherwise 263 * @throws ImsException if calling the IMS service results in an error 264 */ 265 public boolean isConnected(int serviceId, int serviceType, int callType) 266 throws ImsException { 267 checkAndThrowExceptionIfServiceUnavailable(); 268 269 try { 270 return mImsService.isConnected(serviceId, serviceType, callType); 271 } catch (RemoteException e) { 272 throw new ImsException("isServiceConnected()", e, 273 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 274 } 275 } 276 277 /** 278 * Checks if the specified IMS service is opend. 279 * 280 * @param serviceId a service id which is obtained from {@link ImsManager#open} 281 * @return true if the specified service id is opened; false otherwise 282 * @throws ImsException if calling the IMS service results in an error 283 */ 284 public boolean isOpened(int serviceId) throws ImsException { 285 checkAndThrowExceptionIfServiceUnavailable(); 286 287 try { 288 return mImsService.isOpened(serviceId); 289 } catch (RemoteException e) { 290 throw new ImsException("isOpened()", e, 291 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 292 } 293 } 294 295 /** 296 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. 297 * 298 * @param serviceId a service id which is obtained from {@link ImsManager#open} 299 * @param serviceType a service type that is specified in {@link ImsCallProfile} 300 * {@link ImsCallProfile#SERVICE_TYPE_NONE} 301 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} 302 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} 303 * @param callType a call type that is specified in {@link ImsCallProfile} 304 * {@link ImsCallProfile#CALL_TYPE_VOICE} 305 * {@link ImsCallProfile#CALL_TYPE_VT} 306 * {@link ImsCallProfile#CALL_TYPE_VT_TX} 307 * {@link ImsCallProfile#CALL_TYPE_VT_RX} 308 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR} 309 * {@link ImsCallProfile#CALL_TYPE_VS} 310 * {@link ImsCallProfile#CALL_TYPE_VS_TX} 311 * {@link ImsCallProfile#CALL_TYPE_VS_RX} 312 * @return a {@link ImsCallProfile} object 313 * @throws ImsException if calling the IMS service results in an error 314 */ 315 public ImsCallProfile createCallProfile(int serviceId, 316 int serviceType, int callType) throws ImsException { 317 checkAndThrowExceptionIfServiceUnavailable(); 318 319 try { 320 return mImsService.createCallProfile(serviceId, serviceType, callType); 321 } catch (RemoteException e) { 322 throw new ImsException("createCallProfile()", e, 323 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 324 } 325 } 326 327 /** 328 * Creates a {@link ImsCall} to make a call. 329 * 330 * @param serviceId a service id which is obtained from {@link ImsManager#open} 331 * @param profile a call profile to make the call 332 * (it contains service type, call type, media information, etc.) 333 * @param participants participants to invite the conference call 334 * @param listener listen to the call events from {@link ImsCall} 335 * @return a {@link ImsCall} object 336 * @throws ImsException if calling the IMS service results in an error 337 */ 338 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees, 339 ImsCall.Listener listener) throws ImsException { 340 if (DBG) { 341 log("makeCall :: serviceId=" + serviceId 342 + ", profile=" + profile + ", callees=" + callees); 343 } 344 345 checkAndThrowExceptionIfServiceUnavailable(); 346 347 ImsCall call = new ImsCall(mContext, profile); 348 349 call.setListener(listener); 350 ImsCallSession session = createCallSession(serviceId, profile); 351 352 if ((callees != null) && (callees.length == 1)) { 353 call.start(session, callees[0]); 354 } else { 355 call.start(session, callees); 356 } 357 358 return call; 359 } 360 361 /** 362 * Creates a {@link ImsCall} to take an incoming call. 363 * 364 * @param serviceId a service id which is obtained from {@link ImsManager#open} 365 * @param incomingCallIntent the incoming call broadcast intent 366 * @param listener to listen to the call events from {@link ImsCall} 367 * @return a {@link ImsCall} object 368 * @throws ImsException if calling the IMS service results in an error 369 */ 370 public ImsCall takeCall(int serviceId, Intent incomingCallIntent, 371 ImsCall.Listener listener) throws ImsException { 372 if (DBG) { 373 log("takeCall :: serviceId=" + serviceId 374 + ", incomingCall=" + incomingCallIntent); 375 } 376 377 checkAndThrowExceptionIfServiceUnavailable(); 378 379 if (incomingCallIntent == null) { 380 throw new ImsException("Can't retrieve session with null intent", 381 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 382 } 383 384 int incomingServiceId = getServiceId(incomingCallIntent); 385 386 if (serviceId != incomingServiceId) { 387 throw new ImsException("Service id is mismatched in the incoming call intent", 388 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 389 } 390 391 String callId = getCallId(incomingCallIntent); 392 393 if (callId == null) { 394 throw new ImsException("Call ID missing in the incoming call intent", 395 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 396 } 397 398 try { 399 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId); 400 401 if (session == null) { 402 throw new ImsException("No pending session for the call", 403 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL); 404 } 405 406 ImsCall call = new ImsCall(mContext, session.getCallProfile()); 407 408 call.attachSession(new ImsCallSession(session)); 409 call.setListener(listener); 410 411 return call; 412 } catch (Throwable t) { 413 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED); 414 } 415 } 416 417 /** 418 * Gets the config interface to get/set service/capability parameters. 419 * 420 * @return the ImsConfig instance. 421 * @throws ImsException if getting the setting interface results in an error. 422 */ 423 public ImsConfig getConfigInterface() throws ImsException { 424 425 if (mConfig == null) { 426 checkAndThrowExceptionIfServiceUnavailable(); 427 428 try { 429 IImsConfig config = mImsService.getConfigInterface(); 430 if (config == null) { 431 throw new ImsException("getConfigInterface()", 432 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); 433 } 434 mConfig = new ImsConfig(config); 435 } catch (RemoteException e) { 436 throw new ImsException("getConfigInterface()", e, 437 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 438 } 439 } 440 if (DBG) log("getConfigInterface(), mConfig= " + mConfig); 441 return mConfig; 442 } 443 444 /** 445 * Gets the call ID from the specified incoming call broadcast intent. 446 * 447 * @param incomingCallIntent the incoming call broadcast intent 448 * @return the call ID or null if the intent does not contain it 449 */ 450 private static String getCallId(Intent incomingCallIntent) { 451 if (incomingCallIntent == null) { 452 return null; 453 } 454 455 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID); 456 } 457 458 /** 459 * Gets the service type from the specified incoming call broadcast intent. 460 * 461 * @param incomingCallIntent the incoming call broadcast intent 462 * @return the service identifier or -1 if the intent does not contain it 463 */ 464 private static int getServiceId(Intent incomingCallIntent) { 465 if (incomingCallIntent == null) { 466 return (-1); 467 } 468 469 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1); 470 } 471 472 /** 473 * Binds the IMS service only if the service is not created. 474 */ 475 private void checkAndThrowExceptionIfServiceUnavailable() 476 throws ImsException { 477 if (mImsService == null) { 478 createImsService(true); 479 480 if (mImsService == null) { 481 throw new ImsException("Service is unavailable", 482 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 483 } 484 } 485 } 486 487 /** 488 * Binds the IMS service to make/receive the call. 489 */ 490 private void createImsService(boolean checkService) { 491 if (checkService) { 492 IBinder binder = ServiceManager.checkService(IMS_SERVICE); 493 494 if (binder == null) { 495 return; 496 } 497 } 498 499 IBinder b = ServiceManager.getService(IMS_SERVICE); 500 501 if (b != null) { 502 try { 503 b.linkToDeath(mDeathRecipient, 0); 504 } catch (RemoteException e) { 505 } 506 } 507 508 mImsService = IImsService.Stub.asInterface(b); 509 } 510 511 /** 512 * Creates a {@link ImsCallSession} with the specified call profile. 513 * Use other methods, if applicable, instead of interacting with 514 * {@link ImsCallSession} directly. 515 * 516 * @param serviceId a service id which is obtained from {@link ImsManager#open} 517 * @param profile a call profile to make the call 518 */ 519 private ImsCallSession createCallSession(int serviceId, 520 ImsCallProfile profile) throws ImsException { 521 try { 522 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null)); 523 } catch (RemoteException e) { 524 return null; 525 } 526 } 527 528 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass, 529 ImsConnectionStateListener listener) { 530 ImsRegistrationListenerProxy proxy = 531 new ImsRegistrationListenerProxy(serviceClass, listener); 532 return proxy; 533 } 534 535 private void log(String s) { 536 Rlog.d(TAG, s); 537 } 538 539 private void loge(String s) { 540 Rlog.e(TAG, s); 541 } 542 543 private void loge(String s, Throwable t) { 544 Rlog.e(TAG, s, t); 545 } 546 547 /** 548 * Death recipient class for monitoring IMS service. 549 */ 550 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient { 551 @Override 552 public void binderDied() { 553 mImsService = null; 554 mUt = null; 555 mConfig = null; 556 557 if (mContext != null) { 558 mContext.sendBroadcast(new Intent(ACTION_IMS_SERVICE_DOWN)); 559 } 560 } 561 } 562 563 /** 564 * Adapter class for {@link IImsRegistrationListener}. 565 */ 566 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub { 567 private int mServiceClass; 568 private ImsConnectionStateListener mListener; 569 570 public ImsRegistrationListenerProxy(int serviceClass, 571 ImsConnectionStateListener listener) { 572 mServiceClass = serviceClass; 573 mListener = listener; 574 } 575 576 public boolean isSameProxy(int serviceClass) { 577 return (mServiceClass == serviceClass); 578 } 579 580 @Override 581 public void registrationConnected() { 582 if (DBG) { 583 log("registrationConnected ::"); 584 } 585 586 if (mListener != null) { 587 mListener.onImsConnected(); 588 } 589 } 590 591 @Override 592 public void registrationDisconnected() { 593 if (DBG) { 594 log("registrationDisconnected ::"); 595 } 596 597 if (mListener != null) { 598 mListener.onImsDisconnected(); 599 } 600 } 601 602 @Override 603 public void registrationResumed() { 604 if (DBG) { 605 log("registrationResumed ::"); 606 } 607 608 if (mListener != null) { 609 mListener.onImsResumed(); 610 } 611 } 612 613 @Override 614 public void registrationSuspended() { 615 if (DBG) { 616 log("registrationSuspended ::"); 617 } 618 619 if (mListener != null) { 620 mListener.onImsSuspended(); 621 } 622 } 623 624 @Override 625 public void registrationServiceCapabilityChanged(int serviceClass, int event) { 626 log("registrationServiceCapabilityChanged :: serviceClass=" + 627 serviceClass + ", event=" + event); 628 629 if (mListener != null) { 630 mListener.onImsConnected(); 631 } 632 } 633 } 634} 635