1 /* 2 * Copyright (C) 2018 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 android.telephony.ims.feature; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemApi; 23 import android.os.Bundle; 24 import android.os.Message; 25 import android.os.RemoteException; 26 import android.telecom.TelecomManager; 27 import android.telephony.ims.ImsCallProfile; 28 import android.telephony.ims.ImsCallSession; 29 import android.telephony.ims.ImsReasonInfo; 30 import android.telephony.ims.ImsService; 31 import android.telephony.ims.RtpHeaderExtensionType; 32 import android.telephony.ims.aidl.IImsCapabilityCallback; 33 import android.telephony.ims.aidl.IImsMmTelFeature; 34 import android.telephony.ims.aidl.IImsMmTelListener; 35 import android.telephony.ims.aidl.IImsSmsListener; 36 import android.telephony.ims.stub.ImsCallSessionImplBase; 37 import android.telephony.ims.stub.ImsEcbmImplBase; 38 import android.telephony.ims.stub.ImsMultiEndpointImplBase; 39 import android.telephony.ims.stub.ImsRegistrationImplBase; 40 import android.telephony.ims.stub.ImsSmsImplBase; 41 import android.telephony.ims.stub.ImsUtImplBase; 42 import android.util.ArraySet; 43 import android.util.Log; 44 45 import com.android.ims.internal.IImsCallSession; 46 import com.android.ims.internal.IImsEcbm; 47 import com.android.ims.internal.IImsMultiEndpoint; 48 import com.android.ims.internal.IImsUt; 49 import com.android.internal.telephony.util.TelephonyUtils; 50 51 import java.lang.annotation.Retention; 52 import java.lang.annotation.RetentionPolicy; 53 import java.util.List; 54 import java.util.Set; 55 import java.util.concurrent.CancellationException; 56 import java.util.concurrent.CompletableFuture; 57 import java.util.concurrent.CompletionException; 58 import java.util.concurrent.ExecutionException; 59 import java.util.concurrent.Executor; 60 import java.util.concurrent.atomic.AtomicReference; 61 import java.util.function.Supplier; 62 63 /** 64 * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support. 65 * 66 * Any class wishing to use MmTelFeature should extend this class and implement all methods that the 67 * service supports. 68 */ 69 public class MmTelFeature extends ImsFeature { 70 71 private static final String LOG_TAG = "MmTelFeature"; 72 private Executor mExecutor; 73 74 /** 75 * @hide 76 */ 77 @SystemApi MmTelFeature()78 public MmTelFeature() { 79 } 80 81 /** 82 * Create a new MmTelFeature using the Executor specified for methods being called by the 83 * framework. 84 * @param executor The executor for the framework to use when executing the methods overridden 85 * by the implementation of MmTelFeature. 86 * @hide 87 */ 88 @SystemApi MmTelFeature(@onNull Executor executor)89 public MmTelFeature(@NonNull Executor executor) { 90 super(); 91 mExecutor = executor; 92 } 93 94 private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() { 95 96 @Override 97 public void setListener(IImsMmTelListener l) { 98 executeMethodAsyncNoException(() -> MmTelFeature.this.setListener(l), "setListener"); 99 } 100 101 @Override 102 public int getFeatureState() throws RemoteException { 103 return executeMethodAsyncForResult(() -> MmTelFeature.this.getFeatureState(), 104 "getFeatureState"); 105 } 106 107 @Override 108 public ImsCallProfile createCallProfile(int callSessionType, int callType) 109 throws RemoteException { 110 return executeMethodAsyncForResult(() -> MmTelFeature.this.createCallProfile( 111 callSessionType, callType), "createCallProfile"); 112 } 113 114 @Override 115 public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types) 116 throws RemoteException { 117 executeMethodAsync(() -> MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes( 118 new ArraySet<>(types)), "changeOfferedRtpHeaderExtensionTypes"); 119 } 120 121 @Override 122 public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException { 123 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 124 IImsCallSession result = executeMethodAsyncForResult(() -> { 125 try { 126 return createCallSessionInterface(profile); 127 } catch (RemoteException e) { 128 exceptionRef.set(e); 129 return null; 130 } 131 }, "createCallSession"); 132 133 if (exceptionRef.get() != null) { 134 throw exceptionRef.get(); 135 } 136 137 return result; 138 } 139 140 @Override 141 public int shouldProcessCall(String[] numbers) { 142 Integer result = executeMethodAsyncForResultNoException(() -> 143 MmTelFeature.this.shouldProcessCall(numbers), "shouldProcessCall"); 144 if (result != null) { 145 return result.intValue(); 146 } else { 147 return PROCESS_CALL_CSFB; 148 } 149 } 150 151 @Override 152 public IImsUt getUtInterface() throws RemoteException { 153 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 154 IImsUt result = executeMethodAsyncForResult(() -> { 155 try { 156 return MmTelFeature.this.getUtInterface(); 157 } catch (RemoteException e) { 158 exceptionRef.set(e); 159 return null; 160 } 161 }, "getUtInterface"); 162 163 if (exceptionRef.get() != null) { 164 throw exceptionRef.get(); 165 } 166 167 return result; 168 } 169 170 @Override 171 public IImsEcbm getEcbmInterface() throws RemoteException { 172 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 173 IImsEcbm result = executeMethodAsyncForResult(() -> { 174 try { 175 return MmTelFeature.this.getEcbmInterface(); 176 } catch (RemoteException e) { 177 exceptionRef.set(e); 178 return null; 179 } 180 }, "getEcbmInterface"); 181 182 if (exceptionRef.get() != null) { 183 throw exceptionRef.get(); 184 } 185 186 return result; 187 } 188 189 @Override 190 public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException { 191 executeMethodAsync(() -> MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage), 192 "setUiTtyMode"); 193 } 194 195 @Override 196 public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { 197 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 198 IImsMultiEndpoint result = executeMethodAsyncForResult(() -> { 199 try { 200 return MmTelFeature.this.getMultiEndpointInterface(); 201 } catch (RemoteException e) { 202 exceptionRef.set(e); 203 return null; 204 } 205 }, "getMultiEndpointInterface"); 206 207 if (exceptionRef.get() != null) { 208 throw exceptionRef.get(); 209 } 210 211 return result; 212 } 213 214 @Override 215 public int queryCapabilityStatus() { 216 Integer result = executeMethodAsyncForResultNoException(() -> MmTelFeature.this 217 .queryCapabilityStatus().mCapabilities, "queryCapabilityStatus"); 218 219 if (result != null) { 220 return result.intValue(); 221 } else { 222 return 0; 223 } 224 } 225 226 @Override 227 public void addCapabilityCallback(IImsCapabilityCallback c) { 228 executeMethodAsyncNoException(() -> MmTelFeature.this 229 .addCapabilityCallback(c), "addCapabilityCallback"); 230 } 231 232 @Override 233 public void removeCapabilityCallback(IImsCapabilityCallback c) { 234 executeMethodAsyncNoException(() -> MmTelFeature.this 235 .removeCapabilityCallback(c), "removeCapabilityCallback"); 236 } 237 238 @Override 239 public void changeCapabilitiesConfiguration(CapabilityChangeRequest request, 240 IImsCapabilityCallback c) { 241 executeMethodAsyncNoException(() -> MmTelFeature.this 242 .requestChangeEnabledCapabilities(request, c), 243 "changeCapabilitiesConfiguration"); 244 } 245 246 @Override 247 public void queryCapabilityConfiguration(int capability, int radioTech, 248 IImsCapabilityCallback c) { 249 executeMethodAsyncNoException(() -> queryCapabilityConfigurationInternal( 250 capability, radioTech, c), "queryCapabilityConfiguration"); 251 } 252 253 @Override 254 public void setSmsListener(IImsSmsListener l) { 255 executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l), 256 "setSmsListener"); 257 } 258 259 @Override 260 public void sendSms(int token, int messageRef, String format, String smsc, boolean retry, 261 byte[] pdu) { 262 executeMethodAsyncNoException(() -> MmTelFeature.this 263 .sendSms(token, messageRef, format, smsc, retry, pdu), "sendSms"); 264 } 265 266 @Override 267 public void acknowledgeSms(int token, int messageRef, int result) { 268 executeMethodAsyncNoException(() -> MmTelFeature.this 269 .acknowledgeSms(token, messageRef, result), "acknowledgeSms"); 270 } 271 272 @Override 273 public void acknowledgeSmsReport(int token, int messageRef, int result) { 274 executeMethodAsyncNoException(() -> MmTelFeature.this 275 .acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport"); 276 } 277 278 @Override 279 public String getSmsFormat() { 280 return executeMethodAsyncForResultNoException(() -> MmTelFeature.this 281 .getSmsFormat(), "getSmsFormat"); 282 } 283 284 @Override 285 public void onSmsReady() { 286 executeMethodAsyncNoException(() -> MmTelFeature.this.onSmsReady(), 287 "onSmsReady"); 288 } 289 290 // Call the methods with a clean calling identity on the executor and wait indefinitely for 291 // the future to return. 292 private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { 293 try { 294 CompletableFuture.runAsync( 295 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); 296 } catch (CancellationException | CompletionException e) { 297 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: " 298 + e.getMessage()); 299 throw new RemoteException(e.getMessage()); 300 } 301 } 302 303 private void executeMethodAsyncNoException(Runnable r, String errorLogName) { 304 try { 305 CompletableFuture.runAsync( 306 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); 307 } catch (CancellationException | CompletionException e) { 308 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: " 309 + e.getMessage()); 310 } 311 } 312 313 private <T> T executeMethodAsyncForResult(Supplier<T> r, 314 String errorLogName) throws RemoteException { 315 CompletableFuture<T> future = CompletableFuture.supplyAsync( 316 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); 317 try { 318 return future.get(); 319 } catch (ExecutionException | InterruptedException e) { 320 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: " 321 + e.getMessage()); 322 throw new RemoteException(e.getMessage()); 323 } 324 } 325 326 private <T> T executeMethodAsyncForResultNoException(Supplier<T> r, 327 String errorLogName) { 328 CompletableFuture<T> future = CompletableFuture.supplyAsync( 329 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); 330 try { 331 return future.get(); 332 } catch (ExecutionException | InterruptedException e) { 333 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: " 334 + e.getMessage()); 335 return null; 336 } 337 } 338 }; 339 340 /** 341 * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask. 342 * The capabilities that are used in MmTelFeature are defined as 343 * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE}, 344 * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, 345 * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, 346 * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}, and 347 * {@link MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER}. 348 * 349 * The capabilities of this MmTelFeature will be set by the framework. 350 */ 351 public static class MmTelCapabilities extends Capabilities { 352 353 /** 354 * Create a new empty {@link MmTelCapabilities} instance. 355 * @see #addCapabilities(int) 356 * @see #removeCapabilities(int) 357 * @hide 358 */ 359 @SystemApi MmTelCapabilities()360 public MmTelCapabilities() { 361 super(); 362 } 363 364 /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead. 365 * @hide 366 */ 367 @Deprecated 368 @SystemApi MmTelCapabilities(Capabilities c)369 public MmTelCapabilities(Capabilities c) { 370 mCapabilities = c.mCapabilities; 371 } 372 373 /** 374 * Create a new {link @MmTelCapabilities} instance with the provided capabilities. 375 * @param capabilities The capabilities that are supported for MmTel in the form of a 376 * bitfield. 377 * @hide 378 */ 379 @SystemApi MmTelCapabilities(@mTelCapability int capabilities)380 public MmTelCapabilities(@MmTelCapability int capabilities) { 381 super(capabilities); 382 } 383 384 /** @hide */ 385 @IntDef(flag = true, 386 value = { 387 CAPABILITY_TYPE_VOICE, 388 CAPABILITY_TYPE_VIDEO, 389 CAPABILITY_TYPE_UT, 390 CAPABILITY_TYPE_SMS, 391 CAPABILITY_TYPE_CALL_COMPOSER 392 }) 393 @Retention(RetentionPolicy.SOURCE) 394 public @interface MmTelCapability {} 395 396 /** 397 * Undefined capability type for initialization 398 * This is used to check the upper range of MmTel capability 399 * @hide 400 */ 401 public static final int CAPABILITY_TYPE_NONE = 0; 402 403 /** 404 * This MmTelFeature supports Voice calling (IR.92) 405 */ 406 public static final int CAPABILITY_TYPE_VOICE = 1 << 0; 407 408 /** 409 * This MmTelFeature supports Video (IR.94) 410 */ 411 public static final int CAPABILITY_TYPE_VIDEO = 1 << 1; 412 413 /** 414 * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92) 415 */ 416 public static final int CAPABILITY_TYPE_UT = 1 << 2; 417 418 /** 419 * This MmTelFeature supports SMS (IR.92) 420 */ 421 public static final int CAPABILITY_TYPE_SMS = 1 << 3; 422 423 /** 424 * This MmTelFeature supports Call Composer (section 2.4 of RC.20) 425 */ 426 public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4; 427 428 /** 429 * This is used to check the upper range of MmTel capability 430 * @hide 431 */ 432 public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_CALL_COMPOSER + 1; 433 434 /** 435 * @hide 436 */ 437 @Override 438 @SystemApi addCapabilities(@mTelCapability int capabilities)439 public final void addCapabilities(@MmTelCapability int capabilities) { 440 super.addCapabilities(capabilities); 441 } 442 443 /** 444 * @hide 445 */ 446 @Override 447 @SystemApi removeCapabilities(@mTelCapability int capability)448 public final void removeCapabilities(@MmTelCapability int capability) { 449 super.removeCapabilities(capability); 450 } 451 452 /** 453 * @param capabilities a bitmask of one or more capabilities. 454 * 455 * @return true if all queried capabilities are true, otherwise false. 456 */ 457 @Override isCapable(@mTelCapability int capabilities)458 public final boolean isCapable(@MmTelCapability int capabilities) { 459 return super.isCapable(capabilities); 460 } 461 462 /** 463 * @hide 464 */ 465 @NonNull 466 @Override toString()467 public String toString() { 468 StringBuilder builder = new StringBuilder("MmTel Capabilities - ["); 469 builder.append("Voice: "); 470 builder.append(isCapable(CAPABILITY_TYPE_VOICE)); 471 builder.append(" Video: "); 472 builder.append(isCapable(CAPABILITY_TYPE_VIDEO)); 473 builder.append(" UT: "); 474 builder.append(isCapable(CAPABILITY_TYPE_UT)); 475 builder.append(" SMS: "); 476 builder.append(isCapable(CAPABILITY_TYPE_SMS)); 477 builder.append(" CALL_COMPOSER: "); 478 builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER)); 479 builder.append("]"); 480 return builder.toString(); 481 } 482 } 483 484 /** 485 * Listener that the framework implements for communication from the MmTelFeature. 486 * @hide 487 */ 488 public static class Listener extends IImsMmTelListener.Stub { 489 490 /** 491 * Called when the IMS provider receives an incoming call. 492 * @param c The {@link ImsCallSession} associated with the new call. 493 * @hide 494 */ 495 @Override onIncomingCall(IImsCallSession c, Bundle extras)496 public void onIncomingCall(IImsCallSession c, Bundle extras) { 497 498 } 499 500 /** 501 * Called when the IMS provider implicitly rejects an incoming call during setup. 502 * @param callProfile An {@link ImsCallProfile} with the call details. 503 * @param reason The {@link ImsReasonInfo} reason for call rejection. 504 * @hide 505 */ 506 @Override onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason)507 public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) { 508 509 } 510 511 /** 512 * Updates the Listener when the voice message count for IMS has changed. 513 * @param count an integer representing the new message count. 514 * @hide 515 */ 516 @Override onVoiceMessageCountUpdate(int count)517 public void onVoiceMessageCountUpdate(int count) { 518 519 } 520 } 521 522 /** 523 * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the 524 * outgoing call as IMS. 525 * @hide 526 */ 527 @SystemApi 528 public static final int PROCESS_CALL_IMS = 0; 529 /** 530 * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should 531 * not process the outgoing call as IMS and should instead use circuit switch. 532 * @hide 533 */ 534 @SystemApi 535 public static final int PROCESS_CALL_CSFB = 1; 536 537 /** @hide */ 538 @IntDef(flag = true, 539 value = { 540 PROCESS_CALL_IMS, 541 PROCESS_CALL_CSFB 542 }) 543 @Retention(RetentionPolicy.SOURCE) 544 public @interface ProcessCallResult {} 545 546 /** 547 * If the flag is present and true, it indicates that the incoming call is for USSD. 548 * <p> 549 * This is an optional boolean flag. 550 * @hide 551 */ 552 @SystemApi 553 public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD"; 554 555 /** 556 * If this flag is present and true, this call is marked as an unknown dialing call instead 557 * of an incoming call. An example of such a call is a call that is originated by sending 558 * commands (like AT commands) directly to the modem without Android involvement or dialing 559 * calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in 560 * certain situations. 561 * <p> 562 * This is an optional boolean flag. 563 * @hide 564 */ 565 @SystemApi 566 public static final String EXTRA_IS_UNKNOWN_CALL = 567 "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL"; 568 569 private IImsMmTelListener mListener; 570 571 /** 572 * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and 573 * notifies the framework. 574 */ setListener(IImsMmTelListener listener)575 private void setListener(IImsMmTelListener listener) { 576 synchronized (mLock) { 577 mListener = listener; 578 if (mListener != null) { 579 onFeatureReady(); 580 } 581 } 582 } 583 584 /** 585 * @return the listener associated with this MmTelFeature. May be null if it has not been set 586 * by the framework yet. 587 */ getListener()588 private IImsMmTelListener getListener() { 589 synchronized (mLock) { 590 return mListener; 591 } 592 } 593 594 /** 595 * The current capability status that this MmTelFeature has defined is available. This 596 * configuration will be used by the platform to figure out which capabilities are CURRENTLY 597 * available to be used. 598 * 599 * Should be a subset of the capabilities that are enabled by the framework in 600 * {@link #changeEnabledCapabilities}. 601 * @return A copy of the current MmTelFeature capability status. 602 * @hide 603 */ 604 @Override 605 @SystemApi queryCapabilityStatus()606 public @NonNull final MmTelCapabilities queryCapabilityStatus() { 607 return new MmTelCapabilities(super.queryCapabilityStatus()); 608 } 609 610 /** 611 * Notify the framework that the status of the Capabilities has changed. Even though the 612 * MmTelFeature capability may be enabled by the framework, the status may be disabled due to 613 * the feature being unavailable from the network. 614 * @param c The current capability status of the MmTelFeature. If a capability is disabled, then 615 * the status of that capability is disabled. This can happen if the network does not currently 616 * support the capability that is enabled. A capability that is disabled by the framework (via 617 * {@link #changeEnabledCapabilities}) should also show the status as disabled. 618 * @hide 619 */ 620 @SystemApi notifyCapabilitiesStatusChanged(@onNull MmTelCapabilities c)621 public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) { 622 if (c == null) { 623 throw new IllegalArgumentException("MmTelCapabilities must be non-null!"); 624 } 625 super.notifyCapabilitiesStatusChanged(c); 626 } 627 628 /** 629 * Notify the framework of an incoming call. 630 * @param c The {@link ImsCallSessionImplBase} of the new incoming call. 631 * @param extras A bundle containing extra parameters related to the call. See 632 * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above. 633 * @hide 634 */ 635 @SystemApi notifyIncomingCall(@onNull ImsCallSessionImplBase c, @NonNull Bundle extras)636 public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c, 637 @NonNull Bundle extras) { 638 if (c == null || extras == null) { 639 throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be " 640 + "null."); 641 } 642 IImsMmTelListener listener = getListener(); 643 if (listener == null) { 644 throw new IllegalStateException("Session is not available."); 645 } 646 try { 647 listener.onIncomingCall(c.getServiceImpl(), extras); 648 } catch (RemoteException e) { 649 throw new RuntimeException(e); 650 } 651 } 652 653 /** 654 * Notify the framework that a call has been implicitly rejected by this MmTelFeature 655 * during call setup. 656 * @param callProfile The {@link ImsCallProfile} IMS call profile with details. 657 * This can be null if no call information is available for the rejected call. 658 * @param reason The {@link ImsReasonInfo} call rejection reason. 659 * @hide 660 */ 661 @SystemApi notifyRejectedCall(@onNull ImsCallProfile callProfile, @NonNull ImsReasonInfo reason)662 public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile, 663 @NonNull ImsReasonInfo reason) { 664 if (callProfile == null || reason == null) { 665 throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be " 666 + "null."); 667 } 668 IImsMmTelListener listener = getListener(); 669 if (listener == null) { 670 throw new IllegalStateException("Session is not available."); 671 } 672 try { 673 listener.onRejectedCall(callProfile, reason); 674 } catch (RemoteException e) { 675 throw new RuntimeException(e); 676 } 677 } 678 679 /** 680 * 681 * @hide 682 */ notifyIncomingCallSession(IImsCallSession c, Bundle extras)683 public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) { 684 IImsMmTelListener listener = getListener(); 685 if (listener == null) { 686 throw new IllegalStateException("Session is not available."); 687 } 688 try { 689 listener.onIncomingCall(c, extras); 690 } catch (RemoteException e) { 691 throw new RuntimeException(e); 692 } 693 } 694 695 /** 696 * Notify the framework of a change in the Voice Message count. 697 * @link count the new Voice Message count. 698 * @hide 699 */ 700 @SystemApi notifyVoiceMessageCountUpdate(int count)701 public final void notifyVoiceMessageCountUpdate(int count) { 702 IImsMmTelListener listener = getListener(); 703 if (listener == null) { 704 throw new IllegalStateException("Session is not available."); 705 } 706 try { 707 listener.onVoiceMessageCountUpdate(count); 708 } catch (RemoteException e) { 709 throw new RuntimeException(e); 710 } 711 } 712 713 /** 714 * Provides the MmTelFeature with the ability to return the framework Capability Configuration 715 * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and 716 * includes a capability A to enable or disable, this method should return the correct enabled 717 * status for capability A. 718 * @param capability The capability that we are querying the configuration for. 719 * @return true if the capability is enabled, false otherwise. 720 * @hide 721 */ 722 @Override 723 @SystemApi queryCapabilityConfiguration(@mTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)724 public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability, 725 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { 726 // Base implementation - Override to provide functionality 727 return false; 728 } 729 730 /** 731 * The MmTelFeature should override this method to handle the enabling/disabling of 732 * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes 733 * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities 734 * could not be set to their new values, 735 * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called 736 * individually for each capability whose processing resulted in an error. 737 * 738 * Enabling/Disabling a capability here indicates that the capability should be registered or 739 * deregistered (depending on the capability change) and become available or unavailable to 740 * the framework. 741 * @hide 742 */ 743 @Override 744 @SystemApi changeEnabledCapabilities(@onNull CapabilityChangeRequest request, @NonNull CapabilityCallbackProxy c)745 public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request, 746 @NonNull CapabilityCallbackProxy c) { 747 // Base implementation, no-op 748 } 749 750 /** 751 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. 752 * 753 * @param callSessionType a service type that is specified in {@link ImsCallProfile} 754 * {@link ImsCallProfile#SERVICE_TYPE_NONE} 755 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} 756 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} 757 * @param callType a call type that is specified in {@link ImsCallProfile} 758 * {@link ImsCallProfile#CALL_TYPE_VOICE} 759 * {@link ImsCallProfile#CALL_TYPE_VT} 760 * {@link ImsCallProfile#CALL_TYPE_VT_TX} 761 * {@link ImsCallProfile#CALL_TYPE_VT_RX} 762 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR} 763 * {@link ImsCallProfile#CALL_TYPE_VS} 764 * {@link ImsCallProfile#CALL_TYPE_VS_TX} 765 * {@link ImsCallProfile#CALL_TYPE_VS_RX} 766 * @return a {@link ImsCallProfile} object 767 * @hide 768 */ 769 @SystemApi createCallProfile(int callSessionType, int callType)770 public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) { 771 // Base Implementation - Should be overridden 772 return null; 773 } 774 775 /** 776 * Called by the framework to report a change to the RTP header extension types which should be 777 * offered during SDP negotiation (see RFC8285 for more information). 778 * <p> 779 * The {@link ImsService} should report the RTP header extensions which were accepted during 780 * SDP negotiation using {@link ImsCallProfile#setAcceptedRtpHeaderExtensionTypes(Set)}. 781 * 782 * @param extensionTypes The RTP header extensions the framework wishes to offer during 783 * outgoing and incoming call setup. An empty list indicates that there 784 * are no framework defined RTP header extension types to offer. 785 * @hide 786 */ 787 @SystemApi changeOfferedRtpHeaderExtensionTypes( @onNull Set<RtpHeaderExtensionType> extensionTypes)788 public void changeOfferedRtpHeaderExtensionTypes( 789 @NonNull Set<RtpHeaderExtensionType> extensionTypes) { 790 // Base implementation - should be overridden if RTP header extension handling is supported. 791 } 792 793 /** 794 * @hide 795 */ createCallSessionInterface(ImsCallProfile profile)796 public IImsCallSession createCallSessionInterface(ImsCallProfile profile) 797 throws RemoteException { 798 ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile); 799 if (s != null) { 800 s.setDefaultExecutor(mExecutor); 801 return s.getServiceImpl(); 802 } else { 803 return null; 804 } 805 } 806 807 /** 808 * Creates an {@link ImsCallSession} with the specified call profile. 809 * Use other methods, if applicable, instead of interacting with 810 * {@link ImsCallSession} directly. 811 * 812 * @param profile a call profile to make the call 813 * @hide 814 */ 815 @SystemApi createCallSession(@onNull ImsCallProfile profile)816 public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) { 817 // Base Implementation - Should be overridden 818 return null; 819 } 820 821 /** 822 * Called by the framework to determine if the outgoing call, designated by the outgoing 823 * {@link String}s, should be processed as an IMS call or CSFB call. If this method's 824 * functionality is not overridden, the platform will process every call as IMS as long as the 825 * MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is 826 * available. 827 * @param numbers An array of {@link String}s that will be used for placing the call. There can 828 * be multiple {@link String}s listed in the case when we want to place an outgoing 829 * call as a conference. 830 * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the 831 * call will be placed over IMS or via CSFB. 832 * @hide 833 */ 834 @SystemApi shouldProcessCall(@onNull String[] numbers)835 public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) { 836 return PROCESS_CALL_IMS; 837 } 838 839 /** 840 * 841 * @hide 842 */ getUtInterface()843 protected IImsUt getUtInterface() throws RemoteException { 844 ImsUtImplBase utImpl = getUt(); 845 if (utImpl != null) { 846 utImpl.setDefaultExecutor(mExecutor); 847 return utImpl.getInterface(); 848 } else { 849 return null; 850 } 851 } 852 853 /** 854 * @hide 855 */ getEcbmInterface()856 protected IImsEcbm getEcbmInterface() throws RemoteException { 857 ImsEcbmImplBase ecbmImpl = getEcbm(); 858 if (ecbmImpl != null) { 859 ecbmImpl.setDefaultExecutor(mExecutor); 860 return ecbmImpl.getImsEcbm(); 861 } else { 862 return null; 863 } 864 } 865 866 /** 867 * @hide 868 */ getMultiEndpointInterface()869 public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { 870 ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint(); 871 if (multiendpointImpl != null) { 872 multiendpointImpl.setDefaultExecutor(mExecutor); 873 return multiendpointImpl.getIImsMultiEndpoint(); 874 } else { 875 return null; 876 } 877 } 878 879 /** 880 * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service 881 * configuration. 882 * @hide 883 */ 884 @SystemApi getUt()885 public @NonNull ImsUtImplBase getUt() { 886 // Base Implementation - Should be overridden 887 return new ImsUtImplBase(); 888 } 889 890 /** 891 * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE 892 * calls that support it. 893 * @hide 894 */ 895 @SystemApi getEcbm()896 public @NonNull ImsEcbmImplBase getEcbm() { 897 // Base Implementation - Should be overridden 898 return new ImsEcbmImplBase(); 899 } 900 901 /** 902 * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event 903 * package processing for multi-endpoint. 904 * @hide 905 */ 906 @SystemApi getMultiEndpoint()907 public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() { 908 // Base Implementation - Should be overridden 909 return new ImsMultiEndpointImplBase(); 910 } 911 912 /** 913 * Sets the current UI TTY mode for the MmTelFeature. 914 * @param mode An integer containing the new UI TTY Mode, can consist of 915 * {@link TelecomManager#TTY_MODE_OFF}, 916 * {@link TelecomManager#TTY_MODE_FULL}, 917 * {@link TelecomManager#TTY_MODE_HCO}, 918 * {@link TelecomManager#TTY_MODE_VCO} 919 * @param onCompleteMessage If non-null, this MmTelFeature should call this {@link Message} when 920 * the operation is complete by using the associated {@link android.os.Messenger} in 921 * {@link Message#replyTo}. For example: 922 * {@code 923 * // Set UI TTY Mode and other operations... 924 * try { 925 * // Notify framework that the mode was changed. 926 * Messenger uiMessenger = onCompleteMessage.replyTo; 927 * uiMessenger.send(onCompleteMessage); 928 * } catch (RemoteException e) { 929 * // Remote side is dead 930 * } 931 * } 932 * @hide 933 */ 934 @SystemApi setUiTtyMode(int mode, @Nullable Message onCompleteMessage)935 public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) { 936 // Base Implementation - Should be overridden 937 } 938 setSmsListener(IImsSmsListener listener)939 private void setSmsListener(IImsSmsListener listener) { 940 getSmsImplementation().registerSmsListener(listener); 941 } 942 sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)943 private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, 944 byte[] pdu) { 945 getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu); 946 } 947 acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.DeliverStatusResult int result)948 private void acknowledgeSms(int token, int messageRef, 949 @ImsSmsImplBase.DeliverStatusResult int result) { 950 getSmsImplementation().acknowledgeSms(token, messageRef, result); 951 } 952 acknowledgeSmsReport(int token, int messageRef, @ImsSmsImplBase.StatusReportResult int result)953 private void acknowledgeSmsReport(int token, int messageRef, 954 @ImsSmsImplBase.StatusReportResult int result) { 955 getSmsImplementation().acknowledgeSmsReport(token, messageRef, result); 956 } 957 onSmsReady()958 private void onSmsReady() { 959 getSmsImplementation().onReady(); 960 } 961 962 /** 963 * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default 964 * non-functional implementation is returned. 965 * 966 * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS 967 * Provider. 968 * @hide 969 */ 970 @SystemApi getSmsImplementation()971 public @NonNull ImsSmsImplBase getSmsImplementation() { 972 return new ImsSmsImplBase(); 973 } 974 getSmsFormat()975 private String getSmsFormat() { 976 return getSmsImplementation().getSmsFormat(); 977 } 978 979 /** 980 * {@inheritDoc} 981 * @hide 982 */ 983 @Override 984 @SystemApi onFeatureRemoved()985 public void onFeatureRemoved() { 986 // Base Implementation - Should be overridden 987 } 988 989 /** 990 * {@inheritDoc} 991 * @hide 992 */ 993 @Override 994 @SystemApi onFeatureReady()995 public void onFeatureReady() { 996 // Base Implementation - Should be overridden 997 } 998 999 /** 1000 * @hide 1001 */ 1002 @Override getBinder()1003 public final IImsMmTelFeature getBinder() { 1004 return mImsMMTelBinder; 1005 } 1006 1007 /** 1008 * Set default Executor from ImsService. 1009 * @param executor The default executor for the framework to use when executing the methods 1010 * overridden by the implementation of MmTelFeature. 1011 * @hide 1012 */ setDefaultExecutor(@onNull Executor executor)1013 public final void setDefaultExecutor(@NonNull Executor executor) { 1014 if (mExecutor == null) { 1015 mExecutor = executor; 1016 } 1017 } 1018 } 1019