diff options
| author | 2021-11-23 14:44:42 +0000 | |
|---|---|---|
| committer | 2021-12-14 19:15:04 +0000 | |
| commit | b9f657675317d03c4ae53d4120ff9b5d346e03c5 (patch) | |
| tree | d05751bfa86d53431b512229dd5398ce06a0b729 | |
| parent | de761c9fc97b3fb59321ee00f788d7127d7480ab (diff) | |
(ImsService API changes for Better IMS Threading) ImsService to execute binder calls in Executor.
In order to avoid undefined behavior when the framework calls a method through IPC, the ImsService
will now be able to define an Executor that the ImsService can be used to execute the methods
By default all ImsService level method calls will use this Executor.
Sub-classes will also use this Executor unless specified via constuctor which takes executor as an argument.
Test: atest CtsTelephonyTestCases:ImsCallingTest
Bug: 171037053
Change-Id: I10621f9a767ba5bc55373f49caf426e66adbec77
11 files changed, 1049 insertions, 229 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index fb325a2c81d5..bddc2ab9c395 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -13506,6 +13506,7 @@ package android.telephony.ims { method public void disableIms(int); method public void enableIms(int); method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int); + method @NonNull public java.util.concurrent.Executor getExecutor(); method public long getImsServiceCapabilities(); method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int); method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int); @@ -14121,6 +14122,7 @@ package android.telephony.ims.feature { public class MmTelFeature extends android.telephony.ims.feature.ImsFeature { ctor public MmTelFeature(); + ctor public MmTelFeature(@NonNull java.util.concurrent.Executor); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); method public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>); method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int); @@ -14154,7 +14156,7 @@ package android.telephony.ims.feature { } public class RcsFeature extends android.telephony.ims.feature.ImsFeature { - ctor @Deprecated public RcsFeature(); + ctor public RcsFeature(); ctor public RcsFeature(@NonNull java.util.concurrent.Executor); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.CapabilityExchangeEventListener); @@ -14258,6 +14260,7 @@ package android.telephony.ims.stub { } public class ImsConfigImplBase { + ctor public ImsConfigImplBase(@NonNull java.util.concurrent.Executor); ctor public ImsConfigImplBase(); method public int getConfigInt(int); method public String getConfigString(int); @@ -14310,6 +14313,7 @@ package android.telephony.ims.stub { public class ImsRegistrationImplBase { ctor public ImsRegistrationImplBase(); + ctor public ImsRegistrationImplBase(@NonNull java.util.concurrent.Executor); method public final void onDeregistered(android.telephony.ims.ImsReasonInfo); method public final void onRegistered(int); method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes); @@ -14422,6 +14426,7 @@ package android.telephony.ims.stub { } public class SipTransportImplBase { + ctor public SipTransportImplBase(); ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor); method public void createSipDelegate(int, @NonNull android.telephony.ims.DelegateRequest, @NonNull android.telephony.ims.DelegateStateCallback, @NonNull android.telephony.ims.DelegateMessageCallback); method public void destroySipDelegate(@NonNull android.telephony.ims.stub.SipDelegate, int); diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index 9ab5aeb9c34c..53dff545b0ce 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -17,6 +17,7 @@ package android.telephony.ims; import android.annotation.LongDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -44,11 +45,18 @@ import android.util.SparseArray; import com.android.ims.internal.IImsFeatureStatusCallback; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.function.Supplier; /** * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend @@ -173,7 +181,21 @@ public class ImsService extends Service { private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>(); private IImsServiceControllerListener mListener; + private Executor mExecutor; + /** + * Create a new ImsService. + * <p> + * Method stubs called from the framework will be called asynchronously. Vendor specifies the + * {@link Executor} that the methods stubs will be called. If mExecutor is set to null by + * vendor use Runnable::run. + */ + public ImsService() { + mExecutor = ImsService.this.getExecutor(); + if (mExecutor == null) { + mExecutor = Runnable::run; + } + } /** * Listener that notifies the framework of ImsService changes. @@ -201,78 +223,132 @@ public class ImsService extends Service { @Override public IImsMmTelFeature createMmTelFeature(int slotId) { - return createMmTelFeatureInternal(slotId); + return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId), + "createMmTelFeature"); } @Override public IImsRcsFeature createRcsFeature(int slotId) { - return createRcsFeatureInternal(slotId); + return executeMethodAsyncForResult(() -> createRcsFeatureInternal(slotId), + "createRcsFeature"); } @Override public void addFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c) { - ImsService.this.addImsFeatureStatusCallback(slotId, featureType, c); + executeMethodAsync(() -> ImsService.this.addImsFeatureStatusCallback( + slotId, featureType, c), "addFeatureStatusCallback"); } @Override public void removeFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c) { - ImsService.this.removeImsFeatureStatusCallback(slotId, featureType, c); + executeMethodAsync(() -> ImsService.this.removeImsFeatureStatusCallback( + slotId, featureType, c), "removeFeatureStatusCallback"); } @Override public void removeImsFeature(int slotId, int featureType) { - ImsService.this.removeImsFeature(slotId, featureType); + executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType), + "removeImsFeature"); } @Override public ImsFeatureConfiguration querySupportedImsFeatures() { - return ImsService.this.querySupportedImsFeatures(); + return executeMethodAsyncForResult(() -> ImsService.this.querySupportedImsFeatures(), + "ImsFeatureConfiguration"); } @Override public long getImsServiceCapabilities() { - long caps = ImsService.this.getImsServiceCapabilities(); - long sanitizedCaps = sanitizeCapabilities(caps); - if (caps != sanitizedCaps) { - Log.w(LOG_TAG, "removing invalid bits from field: 0x" - + Long.toHexString(caps ^ sanitizedCaps)); - } - return sanitizedCaps; + return executeMethodAsyncForResult(() -> { + long caps = ImsService.this.getImsServiceCapabilities(); + long sanitizedCaps = sanitizeCapabilities(caps); + if (caps != sanitizedCaps) { + Log.w(LOG_TAG, "removing invalid bits from field: 0x" + + Long.toHexString(caps ^ sanitizedCaps)); + } + return sanitizedCaps; + }, "getImsServiceCapabilities"); } @Override public void notifyImsServiceReadyForFeatureCreation() { - ImsService.this.readyForFeatureCreation(); + executeMethodAsync(() -> ImsService.this.readyForFeatureCreation(), + "notifyImsServiceReadyForFeatureCreation"); } @Override public IImsConfig getConfig(int slotId) { - ImsConfigImplBase c = ImsService.this.getConfig(slotId); - return c != null ? c.getIImsConfig() : null; + return executeMethodAsyncForResult(() -> { + ImsConfigImplBase c = ImsService.this.getConfig(slotId); + if (c != null) { + c.setDefaultExecutor(mExecutor); + return c.getIImsConfig(); + } else { + return null; + } + }, "getConfig"); } @Override public IImsRegistration getRegistration(int slotId) { - ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId); - return r != null ? r.getBinder() : null; + return executeMethodAsyncForResult(() -> { + ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId); + if (r != null) { + r.setDefaultExecutor(mExecutor); + return r.getBinder(); + } else { + return null; + } + }, "getRegistration"); } @Override public ISipTransport getSipTransport(int slotId) { - SipTransportImplBase s = ImsService.this.getSipTransport(slotId); - return s != null ? s.getBinder() : null; + return executeMethodAsyncForResult(() -> { + SipTransportImplBase s = ImsService.this.getSipTransport(slotId); + if (s != null) { + s.setDefaultExecutor(mExecutor); + return s.getBinder(); + } else { + return null; + } + }, "getSipTransport"); } @Override public void enableIms(int slotId) { - ImsService.this.enableIms(slotId); + executeMethodAsync(() -> ImsService.this.enableIms(slotId), "enableIms"); } @Override public void disableIms(int slotId) { - ImsService.this.disableIms(slotId); + executeMethodAsync(() -> ImsService.this.disableIms(slotId), "disableIms"); + } + + // Call the methods with a clean calling identity on the executor and wait indefinitely for + // the future to return. + private void executeMethodAsync(Runnable r, String errorLogName) { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: " + + e.getMessage()); + } + } + + private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) { + CompletableFuture<T> future = CompletableFuture.supplyAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); + try { + return future.get(); + } catch (ExecutionException | InterruptedException e) { + Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: " + + e.getMessage()); + return null; + } } }; @@ -300,6 +376,7 @@ public class ImsService extends Service { MmTelFeature f = createMmTelFeature(slotId); if (f != null) { setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL); + f.setDefaultExecutor(mExecutor); return f.getBinder(); } else { Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned."); @@ -310,6 +387,7 @@ public class ImsService extends Service { private IImsRcsFeature createRcsFeatureInternal(int slotId) { RcsFeature f = createRcsFeature(slotId); if (f != null) { + f.setDefaultExecutor(mExecutor); setupFeature(f, slotId, ImsFeature.FEATURE_RCS); return f.getBinder(); } else { @@ -562,4 +640,15 @@ public class ImsService extends Service { result.append("}"); return result.toString(); } + + /** + * The ImsService will now be able to define an Executor that the ImsService can be used to + * execute the methods. By default all ImsService level method calls will use this Executor. + * The ImsService has set the default executor as Runnable::run, + * Should be override or default executor will be used. + * @return an Executor used to execute methods called remotely by the framework. + */ + public @NonNull Executor getExecutor() { + return Runnable::run; + } } diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index 9a3f592480d1..7fdf21b3e5ff 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -40,16 +40,25 @@ import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.ImsSmsImplBase; import android.telephony.ims.stub.ImsUtImplBase; import android.util.ArraySet; +import android.util.Log; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsMultiEndpoint; import com.android.ims.internal.IImsUt; +import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; /** * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support. @@ -60,6 +69,7 @@ import java.util.Set; public class MmTelFeature extends ImsFeature { private static final String LOG_TAG = "MmTelFeature"; + private Executor mExecutor; /** * @hide @@ -68,160 +78,261 @@ public class MmTelFeature extends ImsFeature { public MmTelFeature() { } + /** + * Create a new MmTelFeature using the Executor specified for methods being called by the + * framework. + * @param executor The executor for the framework to use when executing the methods overridden + * by the implementation of MmTelFeature. + * @hide + */ + @SystemApi + public MmTelFeature(@NonNull Executor executor) { + super(); + mExecutor = executor; + } + private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() { @Override public void setListener(IImsMmTelListener l) { - MmTelFeature.this.setListener(l); + executeMethodAsyncNoException(() -> MmTelFeature.this.setListener(l), "setListener"); } @Override public int getFeatureState() throws RemoteException { - try { - return MmTelFeature.this.getFeatureState(); - } catch (Exception e) { - throw new RemoteException(e.getMessage()); - } + return executeMethodAsyncForResult(() -> MmTelFeature.this.getFeatureState(), + "getFeatureState"); } - @Override public ImsCallProfile createCallProfile(int callSessionType, int callType) throws RemoteException { - synchronized (mLock) { - try { - return MmTelFeature.this.createCallProfile(callSessionType, callType); - } catch (Exception e) { - throw new RemoteException(e.getMessage()); - } - } + return executeMethodAsyncForResult(() -> MmTelFeature.this.createCallProfile( + callSessionType, callType), "createCallProfile"); } @Override public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types) throws RemoteException { - synchronized (mLock) { - try { - MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(new ArraySet<>(types)); - } catch (Exception e) { - throw new RemoteException(e.getMessage()); - } - } + executeMethodAsync(() -> MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes( + new ArraySet<>(types)), "changeOfferedRtpHeaderExtensionTypes"); } @Override public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException { - synchronized (mLock) { - return createCallSessionInterface(profile); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + IImsCallSession result = executeMethodAsyncForResult(() -> { + try { + return createCallSessionInterface(profile); + } catch (RemoteException e) { + exceptionRef.set(e); + return null; + } + }, "createCallSession"); + + if (exceptionRef.get() != null) { + throw exceptionRef.get(); } + + return result; } @Override public int shouldProcessCall(String[] numbers) { - synchronized (mLock) { - return MmTelFeature.this.shouldProcessCall(numbers); + Integer result = executeMethodAsyncForResultNoException(() -> + MmTelFeature.this.shouldProcessCall(numbers), "shouldProcessCall"); + if (result != null) { + return result.intValue(); + } else { + return PROCESS_CALL_CSFB; } } @Override public IImsUt getUtInterface() throws RemoteException { - synchronized (mLock) { - return MmTelFeature.this.getUtInterface(); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + IImsUt result = executeMethodAsyncForResult(() -> { + try { + return MmTelFeature.this.getUtInterface(); + } catch (RemoteException e) { + exceptionRef.set(e); + return null; + } + }, "getUtInterface"); + + if (exceptionRef.get() != null) { + throw exceptionRef.get(); } + + return result; } @Override public IImsEcbm getEcbmInterface() throws RemoteException { - synchronized (mLock) { - return MmTelFeature.this.getEcbmInterface(); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + IImsEcbm result = executeMethodAsyncForResult(() -> { + try { + return MmTelFeature.this.getEcbmInterface(); + } catch (RemoteException e) { + exceptionRef.set(e); + return null; + } + }, "getEcbmInterface"); + + if (exceptionRef.get() != null) { + throw exceptionRef.get(); } + + return result; } @Override public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException { - synchronized (mLock) { - try { - MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage); - } catch (Exception e) { - throw new RemoteException(e.getMessage()); - } - } + executeMethodAsync(() -> MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage), + "setUiTtyMode"); } @Override public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { - synchronized (mLock) { - return MmTelFeature.this.getMultiEndpointInterface(); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + IImsMultiEndpoint result = executeMethodAsyncForResult(() -> { + try { + return MmTelFeature.this.getMultiEndpointInterface(); + } catch (RemoteException e) { + exceptionRef.set(e); + return null; + } + }, "getMultiEndpointInterface"); + + if (exceptionRef.get() != null) { + throw exceptionRef.get(); } + + return result; } @Override public int queryCapabilityStatus() { - return MmTelFeature.this.queryCapabilityStatus().mCapabilities; + Integer result = executeMethodAsyncForResultNoException(() -> MmTelFeature.this + .queryCapabilityStatus().mCapabilities, "queryCapabilityStatus"); + + if (result != null) { + return result.intValue(); + } else { + return 0; + } } @Override public void addCapabilityCallback(IImsCapabilityCallback c) { - // no need to lock, structure already handles multithreading. - MmTelFeature.this.addCapabilityCallback(c); + executeMethodAsyncNoException(() -> MmTelFeature.this + .addCapabilityCallback(c), "addCapabilityCallback"); } @Override public void removeCapabilityCallback(IImsCapabilityCallback c) { - // no need to lock, structure already handles multithreading. - MmTelFeature.this.removeCapabilityCallback(c); + executeMethodAsyncNoException(() -> MmTelFeature.this + .removeCapabilityCallback(c), "removeCapabilityCallback"); } @Override public void changeCapabilitiesConfiguration(CapabilityChangeRequest request, IImsCapabilityCallback c) { - MmTelFeature.this.requestChangeEnabledCapabilities(request, c); + executeMethodAsyncNoException(() -> MmTelFeature.this + .requestChangeEnabledCapabilities(request, c), + "changeCapabilitiesConfiguration"); } @Override public void queryCapabilityConfiguration(int capability, int radioTech, IImsCapabilityCallback c) { - queryCapabilityConfigurationInternal(capability, radioTech, c); + executeMethodAsyncNoException(() -> queryCapabilityConfigurationInternal( + capability, radioTech, c), "queryCapabilityConfiguration"); } @Override public void setSmsListener(IImsSmsListener l) { - MmTelFeature.this.setSmsListener(l); + executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l), + "setSmsListener"); } @Override public void sendSms(int token, int messageRef, String format, String smsc, boolean retry, byte[] pdu) { - synchronized (mLock) { - MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu); - } + executeMethodAsyncNoException(() -> MmTelFeature.this + .sendSms(token, messageRef, format, smsc, retry, pdu), "sendSms"); } @Override public void acknowledgeSms(int token, int messageRef, int result) { - synchronized (mLock) { - MmTelFeature.this.acknowledgeSms(token, messageRef, result); - } + executeMethodAsyncNoException(() -> MmTelFeature.this + .acknowledgeSms(token, messageRef, result), "acknowledgeSms"); } @Override public void acknowledgeSmsReport(int token, int messageRef, int result) { - synchronized (mLock) { - MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result); - } + executeMethodAsyncNoException(() -> MmTelFeature.this + .acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport"); } @Override public String getSmsFormat() { - synchronized (mLock) { - return MmTelFeature.this.getSmsFormat(); - } + return executeMethodAsyncForResultNoException(() -> MmTelFeature.this + .getSmsFormat(), "getSmsFormat"); } @Override public void onSmsReady() { - synchronized (mLock) { - MmTelFeature.this.onSmsReady(); + executeMethodAsyncNoException(() -> MmTelFeature.this.onSmsReady(), + "onSmsReady"); + } + + // Call the methods with a clean calling identity on the executor and wait indefinitely for + // the future to return. + private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: " + + e.getMessage()); + throw new RemoteException(e.getMessage()); + } + } + + private void executeMethodAsyncNoException(Runnable r, String errorLogName) { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: " + + e.getMessage()); + } + } + + private <T> T executeMethodAsyncForResult(Supplier<T> r, + String errorLogName) throws RemoteException { + CompletableFuture<T> future = CompletableFuture.supplyAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); + try { + return future.get(); + } catch (ExecutionException | InterruptedException e) { + Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: " + + e.getMessage()); + throw new RemoteException(e.getMessage()); + } + } + + private <T> T executeMethodAsyncForResultNoException(Supplier<T> r, + String errorLogName) { + CompletableFuture<T> future = CompletableFuture.supplyAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); + try { + return future.get(); + } catch (ExecutionException | InterruptedException e) { + Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: " + + e.getMessage()); + return null; } } }; @@ -672,7 +783,12 @@ public class MmTelFeature extends ImsFeature { public IImsCallSession createCallSessionInterface(ImsCallProfile profile) throws RemoteException { ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile); - return s != null ? s.getServiceImpl() : null; + if (s != null) { + s.setDefaultExecutor(mExecutor); + return s.getServiceImpl(); + } else { + return null; + } } /** @@ -713,7 +829,12 @@ public class MmTelFeature extends ImsFeature { */ protected IImsUt getUtInterface() throws RemoteException { ImsUtImplBase utImpl = getUt(); - return utImpl != null ? utImpl.getInterface() : null; + if (utImpl != null) { + utImpl.setDefaultExecutor(mExecutor); + return utImpl.getInterface(); + } else { + return null; + } } /** @@ -721,7 +842,12 @@ public class MmTelFeature extends ImsFeature { */ protected IImsEcbm getEcbmInterface() throws RemoteException { ImsEcbmImplBase ecbmImpl = getEcbm(); - return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null; + if (ecbmImpl != null) { + ecbmImpl.setDefaultExecutor(mExecutor); + return ecbmImpl.getImsEcbm(); + } else { + return null; + } } /** @@ -729,7 +855,12 @@ public class MmTelFeature extends ImsFeature { */ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint(); - return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null; + if (multiendpointImpl != null) { + multiendpointImpl.setDefaultExecutor(mExecutor); + return multiendpointImpl.getIImsMultiEndpoint(); + } else { + return null; + } } /** @@ -859,4 +990,16 @@ public class MmTelFeature extends ImsFeature { public final IImsMmTelFeature getBinder() { return mImsMMTelBinder; } + + /** + * Set default Executor from ImsService. + * @param executor The default executor for the framework to use when executing the methods + * overridden by the implementation of MmTelFeature. + * @hide + */ + public final void setDefaultExecutor(@NonNull Executor executor) { + if (mExecutor == null) { + mExecutor = executor; + } + } } diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index 18cc37d7fbda..11cf0e3f7855 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -70,7 +70,7 @@ public class RcsFeature extends ImsFeature { // Reference the outer class in order to have better test coverage metrics instead of // creating a inner class referencing the outer class directly. private final RcsFeature mReference; - private final Executor mExecutor; + private Executor mExecutor; RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) { mReference = classRef; @@ -259,7 +259,7 @@ public class RcsFeature extends ImsFeature { } } - private final Executor mExecutor; + private Executor mExecutor; private final RcsFeatureBinder mImsRcsBinder; private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl; private CapabilityExchangeEventListener mCapExchangeEventListener; @@ -270,13 +270,9 @@ public class RcsFeature extends ImsFeature { * Method stubs called from the framework will be called asynchronously. To specify the * {@link Executor} that the methods stubs will be called, use * {@link RcsFeature#RcsFeature(Executor)} instead. - * - * @deprecated Use {@link #RcsFeature(Executor)} to create the RcsFeature. */ - @Deprecated public RcsFeature() { super(); - mExecutor = Runnable::run; // Run on the Binder threads that call them. mImsRcsBinder = new RcsFeatureBinder(this, mExecutor); } @@ -477,4 +473,17 @@ public class RcsFeature extends ImsFeature { return mCapabilityExchangeImpl; } } + + /** + * Set default Executor from ImsService. + * @param executor The default executor for the framework to use when executing the methods + * overridden by the implementation of RcsFeature. + * @hide + */ + public final void setDefaultExecutor(@NonNull Executor executor) { + if (mImsRcsBinder.mExecutor == null) { + mExecutor = executor; + mImsRcsBinder.mExecutor = executor; + } + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java index a3a6cb864fa5..e8100957517f 100644 --- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java @@ -30,12 +30,20 @@ import android.telephony.ims.RtpHeaderExtension; import android.telephony.ims.RtpHeaderExtensionType; import android.telephony.ims.aidl.IImsCallSessionListener; import android.util.ArraySet; +import android.util.Log; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsVideoCallProvider; +import com.android.internal.telephony.util.TelephonyUtils; import java.util.List; import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.function.Supplier; /** * Base implementation of IImsCallSession, which implements stub versions of the methods available. @@ -48,6 +56,8 @@ import java.util.Set; // DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you // will break other implementations of ImsCallSession maintained by other ImsServices. public class ImsCallSessionImplBase implements AutoCloseable { + + private static final String LOG_TAG = "ImsCallSessionImplBase"; /** * Notify USSD Mode. */ @@ -110,185 +120,235 @@ public class ImsCallSessionImplBase implements AutoCloseable { } } + private Executor mExecutor = Runnable::run; + // Non-final for injection by tests private IImsCallSession mServiceImpl = new IImsCallSession.Stub() { @Override public void close() { - ImsCallSessionImplBase.this.close(); + executeMethodAsync(() -> ImsCallSessionImplBase.this.close(), "close"); } @Override public String getCallId() { - return ImsCallSessionImplBase.this.getCallId(); + return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallId(), + "getCallId"); } @Override public ImsCallProfile getCallProfile() { - return ImsCallSessionImplBase.this.getCallProfile(); + return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallProfile(), + "getCallProfile"); } @Override public ImsCallProfile getLocalCallProfile() { - return ImsCallSessionImplBase.this.getLocalCallProfile(); + return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this + .getLocalCallProfile(), "getLocalCallProfile"); } @Override public ImsCallProfile getRemoteCallProfile() { - return ImsCallSessionImplBase.this.getRemoteCallProfile(); + return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this + .getRemoteCallProfile(), "getRemoteCallProfile"); } @Override public String getProperty(String name) { - return ImsCallSessionImplBase.this.getProperty(name); + return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getProperty(name), + "getProperty"); } @Override public int getState() { - return ImsCallSessionImplBase.this.getState(); + return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getState(), + "getState"); } @Override public boolean isInCall() { - return ImsCallSessionImplBase.this.isInCall(); + return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isInCall(), + "isInCall"); } @Override public void setListener(IImsCallSessionListener listener) { - ImsCallSessionImplBase.this.setListener(new ImsCallSessionListener(listener)); + executeMethodAsync(() -> ImsCallSessionImplBase.this.setListener( + new ImsCallSessionListener(listener)), "setListener"); } @Override public void setMute(boolean muted) { - ImsCallSessionImplBase.this.setMute(muted); + executeMethodAsync(() -> ImsCallSessionImplBase.this.setMute(muted), "setMute"); } @Override public void start(String callee, ImsCallProfile profile) { - ImsCallSessionImplBase.this.start(callee, profile); + executeMethodAsync(() -> ImsCallSessionImplBase.this.start(callee, profile), "start"); } @Override public void startConference(String[] participants, ImsCallProfile profile) throws RemoteException { - ImsCallSessionImplBase.this.startConference(participants, profile); + executeMethodAsync(() -> ImsCallSessionImplBase.this.startConference(participants, + profile), "startConference"); } @Override public void accept(int callType, ImsStreamMediaProfile profile) { - ImsCallSessionImplBase.this.accept(callType, profile); + executeMethodAsync(() -> ImsCallSessionImplBase.this.accept(callType, profile), + "accept"); } @Override public void deflect(String deflectNumber) { - ImsCallSessionImplBase.this.deflect(deflectNumber); + executeMethodAsync(() -> ImsCallSessionImplBase.this.deflect(deflectNumber), + "deflect"); } @Override public void reject(int reason) { - ImsCallSessionImplBase.this.reject(reason); + executeMethodAsync(() -> ImsCallSessionImplBase.this.reject(reason), "reject"); } @Override public void transfer(@NonNull String number, boolean isConfirmationRequired) { - ImsCallSessionImplBase.this.transfer(number, isConfirmationRequired); + executeMethodAsync(() -> ImsCallSessionImplBase.this.transfer(number, + isConfirmationRequired), "transfer"); } @Override public void consultativeTransfer(@NonNull IImsCallSession transferToSession) { - ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase(); - otherSession.setServiceImpl(transferToSession); - ImsCallSessionImplBase.this.transfer(otherSession); + executeMethodAsync(() -> { + ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase(); + otherSession.setServiceImpl(transferToSession); + ImsCallSessionImplBase.this.transfer(otherSession); + }, "consultativeTransfer"); } @Override public void terminate(int reason) { - ImsCallSessionImplBase.this.terminate(reason); + executeMethodAsync(() -> ImsCallSessionImplBase.this.terminate(reason), "terminate"); } @Override public void hold(ImsStreamMediaProfile profile) { - ImsCallSessionImplBase.this.hold(profile); + executeMethodAsync(() -> ImsCallSessionImplBase.this.hold(profile), "hold"); } @Override public void resume(ImsStreamMediaProfile profile) { - ImsCallSessionImplBase.this.resume(profile); + executeMethodAsync(() -> ImsCallSessionImplBase.this.resume(profile), "resume"); } @Override public void merge() { - ImsCallSessionImplBase.this.merge(); + executeMethodAsync(() -> ImsCallSessionImplBase.this.merge(), "merge"); } @Override public void update(int callType, ImsStreamMediaProfile profile) { - ImsCallSessionImplBase.this.update(callType, profile); + executeMethodAsync(() -> ImsCallSessionImplBase.this.update(callType, profile), + "update"); } @Override public void extendToConference(String[] participants) { - ImsCallSessionImplBase.this.extendToConference(participants); + executeMethodAsync(() -> ImsCallSessionImplBase.this.extendToConference(participants), + "extendToConference"); } @Override public void inviteParticipants(String[] participants) { - ImsCallSessionImplBase.this.inviteParticipants(participants); + executeMethodAsync(() -> ImsCallSessionImplBase.this.inviteParticipants(participants), + "inviteParticipants"); } @Override public void removeParticipants(String[] participants) { - ImsCallSessionImplBase.this.removeParticipants(participants); + executeMethodAsync(() -> ImsCallSessionImplBase.this.removeParticipants(participants), + "removeParticipants"); } @Override public void sendDtmf(char c, Message result) { - ImsCallSessionImplBase.this.sendDtmf(c, result); + executeMethodAsync(() -> ImsCallSessionImplBase.this.sendDtmf(c, result), "sendDtmf"); } @Override public void startDtmf(char c) { - ImsCallSessionImplBase.this.startDtmf(c); + executeMethodAsync(() -> ImsCallSessionImplBase.this.startDtmf(c), "startDtmf"); } @Override public void stopDtmf() { - ImsCallSessionImplBase.this.stopDtmf(); + executeMethodAsync(() -> ImsCallSessionImplBase.this.stopDtmf(), "stopDtmf"); } @Override public void sendUssd(String ussdMessage) { - ImsCallSessionImplBase.this.sendUssd(ussdMessage); + executeMethodAsync(() -> ImsCallSessionImplBase.this.sendUssd(ussdMessage), "sendUssd"); } @Override public IImsVideoCallProvider getVideoCallProvider() { - return ImsCallSessionImplBase.this.getVideoCallProvider(); + return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this + .getVideoCallProvider(), "getVideoCallProvider"); } @Override public boolean isMultiparty() { - return ImsCallSessionImplBase.this.isMultiparty(); + return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isMultiparty(), + "isMultiparty"); } @Override public void sendRttModifyRequest(ImsCallProfile toProfile) { - ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile); + executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile), + "sendRttModifyRequest"); } @Override public void sendRttModifyResponse(boolean status) { - ImsCallSessionImplBase.this.sendRttModifyResponse(status); + executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyResponse(status), + "sendRttModifyResponse"); } @Override public void sendRttMessage(String rttMessage) { - ImsCallSessionImplBase.this.sendRttMessage(rttMessage); + executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttMessage(rttMessage), + "sendRttMessage"); } @Override public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> extensions) { - ImsCallSessionImplBase.this.sendRtpHeaderExtensions( - new ArraySet<RtpHeaderExtension>(extensions)); + executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRtpHeaderExtensions( + new ArraySet<RtpHeaderExtension>(extensions)), "sendRtpHeaderExtensions"); + } + + // Call the methods with a clean calling identity on the executor and wait indefinitely for + // the future to return. + private void executeMethodAsync(Runnable r, String errorLogName) { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + } + } + + private <T> T executeMethodAsyncForResult(Supplier<T> r, + String errorLogName) { + CompletableFuture<T> future = CompletableFuture.supplyAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); + try { + return future.get(); + } catch (ExecutionException | InterruptedException e) { + Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + return null; + } } }; @@ -674,4 +734,14 @@ public class ImsCallSessionImplBase implements AutoCloseable { public void setServiceImpl(IImsCallSession serviceImpl) { mServiceImpl = serviceImpl; } + + /** + * Set default Executor from MmTelFeature. + * @param executor The default executor for the framework to use when executing the methods + * overridden by the implementation of ImsCallSession. + * @hide + */ + public final void setDefaultExecutor(@NonNull Executor executor) { + mExecutor = executor; + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index d75da9035124..11fc328a42c7 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -33,12 +33,21 @@ import android.util.Log; import com.android.ims.ImsConfig; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.util.RemoteCallbackListExt; +import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.HashMap; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + /** * Controls the modification of IMS specific configurations. For more information on the supported @@ -81,21 +90,48 @@ public class ImsConfigImplBase { WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference; private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>(); private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>(); + private final Object mLock = new Object(); + private Executor mExecutor; @VisibleForTesting - public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) { + public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Executor executor) { + mExecutor = executor; mImsConfigImplBaseWeakReference = new WeakReference<ImsConfigImplBase>(imsConfigImplBase); } @Override public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException { - getImsConfigImpl().addImsConfigCallback(c); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + getImsConfigImpl().addImsConfigCallback(c); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "addImsConfigCallback"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception addImsConfigCallback"); + throw exceptionRef.get(); + } } @Override public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException { - getImsConfigImpl().removeImsConfigCallback(c); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + getImsConfigImpl().removeImsConfigCallback(c); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "removeImsConfigCallback"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception removeImsConfigCallback"); + throw exceptionRef.get(); + } } /** @@ -108,16 +144,34 @@ public class ImsConfigImplBase { * unavailable. */ @Override - public synchronized int getConfigInt(int item) throws RemoteException { - if (mProvisionedIntValue.containsKey(item)) { - return mProvisionedIntValue.get(item); - } else { - int retVal = getImsConfigImpl().getConfigInt(item); - if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) { - updateCachedValue(item, retVal, false); + public int getConfigInt(int item) throws RemoteException { + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + int retVal = executeMethodAsyncForResult(()-> { + int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN; + synchronized (mLock) { + if (mProvisionedIntValue.containsKey(item)) { + return mProvisionedIntValue.get(item); + } else { + try { + returnVal = getImsConfigImpl().getConfigInt(item); + if (returnVal != ImsConfig.OperationStatusConstants.UNKNOWN) { + mProvisionedIntValue.put(item, returnVal); + } + } catch (RemoteException e) { + exceptionRef.set(e); + return returnVal; + } + } } - return retVal; + return returnVal; + }, "getConfigInt"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception getConfigString"); + throw exceptionRef.get(); } + + return retVal; } /** @@ -129,16 +183,34 @@ public class ImsConfigImplBase { * @return value in String format. */ @Override - public synchronized String getConfigString(int item) throws RemoteException { - if (mProvisionedStringValue.containsKey(item)) { - return mProvisionedStringValue.get(item); - } else { - String retVal = getImsConfigImpl().getConfigString(item); - if (retVal != null) { - updateCachedValue(item, retVal, false); + public String getConfigString(int item) throws RemoteException { + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + String retVal = executeMethodAsyncForResult(()-> { + String returnVal = null; + synchronized (mLock) { + if (mProvisionedStringValue.containsKey(item)) { + returnVal = mProvisionedStringValue.get(item); + } else { + try { + returnVal = getImsConfigImpl().getConfigString(item); + if (returnVal != null) { + mProvisionedStringValue.put(item, returnVal); + } + } catch (RemoteException e) { + exceptionRef.set(e); + return returnVal; + } + } } - return retVal; + return returnVal; + }, "getConfigString"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception getConfigString"); + throw exceptionRef.get(); } + + return retVal; } /** @@ -153,14 +225,32 @@ public class ImsConfigImplBase { * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}. */ @Override - public synchronized int setConfigInt(int item, int value) throws RemoteException { - mProvisionedIntValue.remove(item); - int retVal = getImsConfigImpl().setConfig(item, value); - if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) { - updateCachedValue(item, value, true); - } else { - Log.d(TAG, "Set provision value of " + item + - " to " + value + " failed with error code " + retVal); + public int setConfigInt(int item, int value) throws RemoteException { + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + int retVal = executeMethodAsyncForResult(()-> { + int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN; + try { + synchronized (mLock) { + mProvisionedIntValue.remove(item); + returnVal = getImsConfigImpl().setConfig(item, value); + if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) { + mProvisionedIntValue.put(item, value); + } else { + Log.d(TAG, "Set provision value of " + item + + " to " + value + " failed with error code " + returnVal); + } + } + notifyImsConfigChanged(item, value); + return returnVal; + } catch (RemoteException e) { + exceptionRef.set(e); + return returnVal; + } + }, "setConfigInt"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception setConfigInt"); + throw exceptionRef.get(); } return retVal; @@ -178,12 +268,30 @@ public class ImsConfigImplBase { * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}. */ @Override - public synchronized int setConfigString(int item, String value) + public int setConfigString(int item, String value) throws RemoteException { - mProvisionedStringValue.remove(item); - int retVal = getImsConfigImpl().setConfig(item, value); - if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) { - updateCachedValue(item, value, true); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + int retVal = executeMethodAsyncForResult(()-> { + int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN; + try { + synchronized (mLock) { + mProvisionedStringValue.remove(item); + returnVal = getImsConfigImpl().setConfig(item, value); + if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) { + mProvisionedStringValue.put(item, value); + } + } + notifyImsConfigChanged(item, value); + return returnVal; + } catch (RemoteException e) { + exceptionRef.set(e); + return returnVal; + } + }, "setConfigString"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception setConfigInt"); + throw exceptionRef.get(); } return retVal; @@ -191,7 +299,19 @@ public class ImsConfigImplBase { @Override public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException { - getImsConfigImpl().updateImsCarrierConfigs(bundle); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + getImsConfigImpl().updateImsCarrierConfigs(bundle); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "updateImsCarrierConfigs"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception updateImsCarrierConfigs"); + throw exceptionRef.get(); + } } private ImsConfigImplBase getImsConfigImpl() throws RemoteException { @@ -206,13 +326,37 @@ public class ImsConfigImplBase { @Override public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) throws RemoteException { - getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "notifyRcsAutoConfigurationReceived"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationReceived"); + throw exceptionRef.get(); + } } @Override public void notifyRcsAutoConfigurationRemoved() throws RemoteException { - getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved(); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved(); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "notifyRcsAutoConfigurationRemoved"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationRemoved"); + throw exceptionRef.get(); + } } private void notifyImsConfigChanged(int item, int value) throws RemoteException { @@ -223,50 +367,144 @@ public class ImsConfigImplBase { getImsConfigImpl().notifyConfigChanged(item, value); } - protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) - throws RemoteException { - mProvisionedIntValue.put(item, value); - if (notifyChange) { - notifyImsConfigChanged(item, value); + protected void updateCachedValue(int item, int value) { + synchronized (mLock) { + mProvisionedIntValue.put(item, value); } } - protected synchronized void updateCachedValue(int item, String value, - boolean notifyChange) throws RemoteException { - mProvisionedStringValue.put(item, value); - if (notifyChange) { - notifyImsConfigChanged(item, value); + protected void updateCachedValue(int item, String value) { + synchronized (mLock) { + mProvisionedStringValue.put(item, value); } } @Override public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { - getImsConfigImpl().addRcsConfigCallback(c); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + getImsConfigImpl().addRcsConfigCallback(c); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "addRcsConfigCallback"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception addRcsConfigCallback"); + throw exceptionRef.get(); + } } @Override public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { - getImsConfigImpl().removeRcsConfigCallback(c); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + getImsConfigImpl().removeRcsConfigCallback(c); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "removeRcsConfigCallback"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception removeRcsConfigCallback"); + throw exceptionRef.get(); + } } @Override public void triggerRcsReconfiguration() throws RemoteException { - getImsConfigImpl().triggerAutoConfiguration(); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + getImsConfigImpl().triggerAutoConfiguration(); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "triggerRcsReconfiguration"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception triggerRcsReconfiguration"); + throw exceptionRef.get(); + } } @Override public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException { - getImsConfigImpl().setRcsClientConfiguration(rcc); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + getImsConfigImpl().setRcsClientConfiguration(rcc); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "setRcsClientConfiguration"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception setRcsClientConfiguration"); + throw exceptionRef.get(); + } } @Override public void notifyIntImsConfigChanged(int item, int value) throws RemoteException { - notifyImsConfigChanged(item, value); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + notifyImsConfigChanged(item, value); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "notifyIntImsConfigChanged"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception notifyIntImsConfigChanged"); + throw exceptionRef.get(); + } } @Override public void notifyStringImsConfigChanged(int item, String value) throws RemoteException { - notifyImsConfigChanged(item, value); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(()-> { + try { + notifyImsConfigChanged(item, value); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "notifyStringImsConfigChanged"); + + if (exceptionRef.get() != null) { + Log.d(TAG, "ImsConfigImplBase Exception notifyStringImsConfigChanged"); + throw exceptionRef.get(); + } + } + + // Call the methods with a clean calling identity on the executor and wait indefinitely for + // the future to return. + private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + throw new RemoteException(e.getMessage()); + } + } + + private <T> T executeMethodAsyncForResult(Supplier<T> r, + String errorLogName) throws RemoteException { + CompletableFuture<T> future = CompletableFuture.supplyAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); + try { + return future.get(); + } catch (ExecutionException | InterruptedException e) { + Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + throw new RemoteException(e.getMessage()); + } } } @@ -303,15 +541,24 @@ public class ImsConfigImplBase { ImsConfigStub mImsConfigStub; /** - * Used for compatibility between older versions of the ImsService. + * Create a ImsConfig using the Executor specified for methods being called by the + * framework. + * @param executor The executor for the framework to use when executing the methods overridden + * by the implementation of ImsConfig. + */ + public ImsConfigImplBase(@NonNull Executor executor) { + mImsConfigStub = new ImsConfigStub(this, executor); + } + + /** * @hide */ - public ImsConfigImplBase(Context context) { - mImsConfigStub = new ImsConfigStub(this); + public ImsConfigImplBase(@NonNull Context context) { + mImsConfigStub = new ImsConfigStub(this, null); } public ImsConfigImplBase() { - mImsConfigStub = new ImsConfigStub(this); + mImsConfigStub = new ImsConfigStub(this, null); } /** @@ -427,8 +674,10 @@ public class ImsConfigImplBase { * @param value in Integer format. */ public final void notifyProvisionedValueChanged(int item, int value) { + mImsConfigStub.updateCachedValue(item, value); + try { - mImsConfigStub.updateCachedValue(item, value, true); + mImsConfigStub.notifyImsConfigChanged(item, value); } catch (RemoteException e) { Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead."); } @@ -443,8 +692,10 @@ public class ImsConfigImplBase { * @param value in String format. */ public final void notifyProvisionedValueChanged(int item, String value) { + mImsConfigStub.updateCachedValue(item, value); + try { - mImsConfigStub.updateCachedValue(item, value, true); + mImsConfigStub.notifyImsConfigChanged(item, value); } catch (RemoteException e) { Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead."); } @@ -582,4 +833,16 @@ public class ImsConfigImplBase { } }); } + + /** + * Set default Executor from ImsService. + * @param executor The default executor for the framework to use when executing the methods + * overridden by the implementation of ImsConfig. + * @hide + */ + public final void setDefaultExecutor(@NonNull Executor executor) { + if (mImsConfigStub.mExecutor == null) { + mImsConfigStub.mExecutor = executor; + } + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java index 8ad40ed1032c..84b2253e1b27 100644 --- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java @@ -16,14 +16,21 @@ package android.telephony.ims.stub; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.RemoteException; import android.util.Log; import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsEcbmListener; +import com.android.internal.telephony.util.TelephonyUtils; import java.util.Objects; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; + /** * Base implementation of ImsEcbm, which implements stub versions of the methods @@ -40,10 +47,12 @@ public class ImsEcbmImplBase { private final Object mLock = new Object(); private IImsEcbmListener mListener; + private Executor mExecutor = Runnable::run; + private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() { @Override public void setListener(IImsEcbmListener listener) { - synchronized (mLock) { + executeMethodAsync(() -> { if (mListener != null && !mListener.asBinder().isBinderAlive()) { Log.w(TAG, "setListener: discarding dead Binder"); mListener = null; @@ -62,12 +71,25 @@ public class ImsEcbmImplBase { + "listener"); mListener = listener; } - } + }, "setListener"); } @Override public void exitEmergencyCallbackMode() { - ImsEcbmImplBase.this.exitEmergencyCallbackMode(); + executeMethodAsync(() -> ImsEcbmImplBase.this.exitEmergencyCallbackMode(), + "exitEmergencyCallbackMode"); + } + + // Call the methods with a clean calling identity on the executor and wait indefinitely for + // the future to return. + private void executeMethodAsync(Runnable r, String errorLogName) { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(TAG, "ImsEcbmImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + } } }; @@ -123,4 +145,14 @@ public class ImsEcbmImplBase { } } } + + /** + * Set default Executor from MmTelFeature. + * @param executor The default executor for the framework to use when executing the methods + * overridden by the implementation of ImsEcbm. + * @hide + */ + public final void setDefaultExecutor(@NonNull Executor executor) { + mExecutor = executor; + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java index ec1c7b3a92a8..a723cd8b118c 100644 --- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java @@ -16,6 +16,7 @@ package android.telephony.ims.stub; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.RemoteException; import android.telephony.ims.ImsExternalCallState; @@ -23,9 +24,14 @@ import android.util.Log; import com.android.ims.internal.IImsExternalCallStateListener; import com.android.ims.internal.IImsMultiEndpoint; +import com.android.internal.telephony.util.TelephonyUtils; import java.util.List; import java.util.Objects; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; /** * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods @@ -43,11 +49,13 @@ public class ImsMultiEndpointImplBase { private IImsExternalCallStateListener mListener; private final Object mLock = new Object(); + private Executor mExecutor = Runnable::run; + private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() { @Override public void setListener(IImsExternalCallStateListener listener) throws RemoteException { - synchronized (mLock) { + executeMethodAsync(() -> { if (mListener != null && !mListener.asBinder().isBinderAlive()) { Log.w(TAG, "setListener: discarding dead Binder"); mListener = null; @@ -67,12 +75,25 @@ public class ImsMultiEndpointImplBase { + "listener"); mListener = listener; } - } + }, "setListener"); } @Override public void requestImsExternalCallStateInfo() throws RemoteException { - ImsMultiEndpointImplBase.this.requestImsExternalCallStateInfo(); + executeMethodAsync(() -> ImsMultiEndpointImplBase.this + .requestImsExternalCallStateInfo(), "requestImsExternalCallStateInfo"); + } + + // Call the methods with a clean calling identity on the executor and wait indefinitely for + // the future to return. + private void executeMethodAsync(Runnable r, String errorLogName) { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(TAG, "ImsMultiEndpointImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + } } }; @@ -108,4 +129,14 @@ public class ImsMultiEndpointImplBase { public void requestImsExternalCallStateInfo() { Log.d(TAG, "requestImsExternalCallStateInfo() not implemented"); } + + /** + * Set default Executor from MmTelFeature. + * @param executor The default executor for the framework to use when executing the methods + * overridden by the implementation of ImsMultiEndpoint. + * @hide + */ + public final void setDefaultExecutor(@NonNull Executor executor) { + mExecutor = executor; + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index 02bcdec621c1..3b151a422b57 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -31,10 +31,19 @@ import android.telephony.ims.aidl.IImsRegistrationCallback; import android.util.Log; import com.android.internal.telephony.util.RemoteCallbackListExt; +import com.android.internal.telephony.util.TelephonyUtils; import com.android.internal.util.ArrayUtils; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; /** * Controls IMS registration for this ImsService and notifies the framework when the IMS @@ -92,39 +101,114 @@ public class ImsRegistrationImplBase { // yet. private static final int REGISTRATION_STATE_UNKNOWN = -1; + private Executor mExecutor; + + /** + * Create a new ImsRegistration. + * <p> + * Method stubs called from the framework will be called asynchronously. To specify the + * {@link Executor} that the methods stubs will be called, use + * {@link ImsRegistrationImplBase#ImsRegistrationImplBase(Executor)} instead. + */ + public ImsRegistrationImplBase() { + super(); + } + + /** + * Create a ImsRegistration using the Executor specified for methods being called by the + * framework. + * @param executor The executor for the framework to use when executing the methods overridden + * by the implementation of ImsRegistration. + */ + public ImsRegistrationImplBase(@NonNull Executor executor) { + super(); + mExecutor = executor; + } + private final IImsRegistration mBinder = new IImsRegistration.Stub() { @Override public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { - synchronized (mLock) { - return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE - : mRegistrationAttributes.getRegistrationTechnology(); - } + return executeMethodAsyncForResult(() -> (mRegistrationAttributes == null) + ? REGISTRATION_TECH_NONE : mRegistrationAttributes.getRegistrationTechnology(), + "getRegistrationTechnology"); } @Override public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { - ImsRegistrationImplBase.this.addRegistrationCallback(c); + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(() -> { + try { + ImsRegistrationImplBase.this.addRegistrationCallback(c); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "addRegistrationCallback"); + + if (exceptionRef.get() != null) { + throw exceptionRef.get(); + } } @Override public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { - ImsRegistrationImplBase.this.removeRegistrationCallback(c); + executeMethodAsync(() -> ImsRegistrationImplBase.this.removeRegistrationCallback(c), + "removeRegistrationCallback"); } @Override public void triggerFullNetworkRegistration(int sipCode, String sipReason) { - ImsRegistrationImplBase.this.triggerFullNetworkRegistration(sipCode, sipReason); + executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this + .triggerFullNetworkRegistration(sipCode, sipReason), + "triggerFullNetworkRegistration"); } @Override public void triggerUpdateSipDelegateRegistration() { - ImsRegistrationImplBase.this.updateSipDelegateRegistration(); + executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this + .updateSipDelegateRegistration(), "triggerUpdateSipDelegateRegistration"); } @Override public void triggerSipDelegateDeregistration() { - ImsRegistrationImplBase.this.triggerSipDelegateDeregistration(); + executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this + .triggerSipDelegateDeregistration(), "triggerSipDelegateDeregistration"); + } + + // Call the methods with a clean calling identity on the executor and wait indefinitely for + // the future to return. + private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + throw new RemoteException(e.getMessage()); + } + } + + private void executeMethodAsyncNoException(Runnable r, String errorLogName) { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + } + } + + private <T> T executeMethodAsyncForResult(Supplier<T> r, + String errorLogName) throws RemoteException { + CompletableFuture<T> future = CompletableFuture.supplyAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); + try { + return future.get(); + } catch (ExecutionException | InterruptedException e) { + Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + throw new RemoteException(e.getMessage()); + } } }; @@ -394,4 +478,16 @@ public class ImsRegistrationImplBase { onSubscriberAssociatedUriChanged(c, uris); } } + + /** + * Set default Executor from ImsService. + * @param executor The default executor for the framework to use when executing the methods + * overridden by the implementation of Registration. + * @hide + */ + public final void setDefaultExecutor(@NonNull Executor executor) { + if (mExecutor == null) { + mExecutor = executor; + } + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java index eb3e8ed5a8e4..11cdeed10c5a 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -27,10 +27,17 @@ import android.util.Log; import com.android.ims.internal.IImsUt; import com.android.ims.internal.IImsUtListener; +import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.function.Supplier; /** * Base implementation of IMS UT interface, which implements stubs. Override these methods to @@ -119,96 +126,108 @@ public class ImsUtImplBase { */ public static final int INVALID_RESULT = -1; + private Executor mExecutor = Runnable::run; + private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() { private final Object mLock = new Object(); private ImsUtListener mUtListener; @Override public void close() throws RemoteException { - ImsUtImplBase.this.close(); + executeMethodAsync(() ->ImsUtImplBase.this.close(), "close"); } @Override public int queryCallBarring(int cbType) throws RemoteException { - return ImsUtImplBase.this.queryCallBarring(cbType); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallBarring(cbType), + "queryCallBarring"); } @Override public int queryCallForward(int condition, String number) throws RemoteException { - return ImsUtImplBase.this.queryCallForward(condition, number); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallForward( + condition, number), "queryCallForward"); } @Override public int queryCallWaiting() throws RemoteException { - return ImsUtImplBase.this.queryCallWaiting(); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallWaiting(), + "queryCallWaiting"); } @Override public int queryCLIR() throws RemoteException { - return ImsUtImplBase.this.queryCLIR(); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIR(), "queryCLIR"); } @Override public int queryCLIP() throws RemoteException { - return ImsUtImplBase.this.queryCLIP(); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIP(), "queryCLIP"); } @Override public int queryCOLR() throws RemoteException { - return ImsUtImplBase.this.queryCOLR(); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLR(), "queryCOLR"); } @Override public int queryCOLP() throws RemoteException { - return ImsUtImplBase.this.queryCOLP(); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLP(), "queryCOLP"); } @Override public int transact(Bundle ssInfo) throws RemoteException { - return ImsUtImplBase.this.transact(ssInfo); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.transact(ssInfo), + "transact"); } @Override public int updateCallBarring(int cbType, int action, String[] barrList) throws RemoteException { - return ImsUtImplBase.this.updateCallBarring(cbType, action, barrList); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallBarring( + cbType, action, barrList), "updateCallBarring"); } @Override public int updateCallForward(int action, int condition, String number, int serviceClass, int timeSeconds) throws RemoteException { - return ImsUtImplBase.this.updateCallForward(action, condition, number, serviceClass, - timeSeconds); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallForward( + action, condition, number, serviceClass, timeSeconds), "updateCallForward"); } @Override public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException { - return ImsUtImplBase.this.updateCallWaiting(enable, serviceClass); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallWaiting( + enable, serviceClass), "updateCallWaiting"); } @Override public int updateCLIR(int clirMode) throws RemoteException { - return ImsUtImplBase.this.updateCLIR(clirMode); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIR(clirMode), + "updateCLIR"); } @Override public int updateCLIP(boolean enable) throws RemoteException { - return ImsUtImplBase.this.updateCLIP(enable); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIP(enable), + "updateCLIP"); } @Override public int updateCOLR(int presentation) throws RemoteException { - return ImsUtImplBase.this.updateCOLR(presentation); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLR(presentation), + "updateCOLR"); } @Override public int updateCOLP(boolean enable) throws RemoteException { - return ImsUtImplBase.this.updateCOLP(enable); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLP(enable), + "updateCOLP"); } @Override public void setListener(IImsUtListener listener) throws RemoteException { - synchronized (mLock) { + executeMethodAsync(() -> { if (mUtListener != null && !mUtListener.getListenerInterface().asBinder().isBinderAlive()) { Log.w(TAG, "setListener: discarding dead Binder"); @@ -229,29 +248,59 @@ public class ImsUtImplBase { + "listener"); mUtListener = new ImsUtListener(listener); } - } - ImsUtImplBase.this.setListener(mUtListener); + ImsUtImplBase.this.setListener(mUtListener); + }, "setListener"); } @Override public int queryCallBarringForServiceClass(int cbType, int serviceClass) throws RemoteException { - return ImsUtImplBase.this.queryCallBarringForServiceClass(cbType, serviceClass); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this + .queryCallBarringForServiceClass(cbType, serviceClass), + "queryCallBarringForServiceClass"); } @Override public int updateCallBarringForServiceClass(int cbType, int action, String[] barrList, int serviceClass) throws RemoteException { - return ImsUtImplBase.this.updateCallBarringForServiceClass( - cbType, action, barrList, serviceClass); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this + .updateCallBarringForServiceClass(cbType, action, barrList, serviceClass), + "updateCallBarringForServiceClass"); } @Override public int updateCallBarringWithPassword(int cbType, int action, String[] barrList, int serviceClass, String password) throws RemoteException { - return ImsUtImplBase.this.updateCallBarringWithPassword( - cbType, action, barrList, serviceClass, password); + return executeMethodAsyncForResult(() -> ImsUtImplBase.this + .updateCallBarringWithPassword(cbType, action, barrList, serviceClass, + password), "updateCallBarringWithPassword"); + } + + // Call the methods with a clean calling identity on the executor and wait indefinitely for + // the future to return. + private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(TAG, "ImsUtImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + throw new RemoteException(e.getMessage()); + } + } + + private <T> T executeMethodAsyncForResult(Supplier<T> r, + String errorLogName) throws RemoteException { + CompletableFuture<T> future = CompletableFuture.supplyAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); + try { + return future.get(); + } catch (ExecutionException | InterruptedException e) { + Log.w(TAG, "ImsUtImplBase Binder - " + errorLogName + " exception: " + + e.getMessage()); + throw new RemoteException(e.getMessage()); + } } }; @@ -470,4 +519,14 @@ public class ImsUtImplBase { public IImsUt getInterface() { return mServiceImpl; } + + /** + * Set default Executor from MmTelFeature. + * @param executor The default executor for the framework to use when executing the methods + * overridden by the implementation of ImsUT. + * @hide + */ + public final void setDefaultExecutor(@NonNull Executor executor) { + mExecutor = executor; + } } diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java index 13ea99735ab4..52538cb4e2df 100644 --- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java +++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java @@ -86,10 +86,21 @@ public class SipTransportImplBase { } }; - private final Executor mBinderExecutor; + private Executor mBinderExecutor; private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>(); /** + * Create a new SipTransport. + * <p> + * Method stubs called from the framework will be called asynchronously. To specify the + * {@link Executor} that the methods stubs will be called, use + * {@link SipTransportImplBase#SipTransportImplBase(Executor)} instead. + */ + public SipTransportImplBase() { + super(); + } + + /** * Create an implementation of SipTransportImplBase. * * @param executor The executor that remote calls from the framework will be called on. This @@ -212,4 +223,16 @@ public class SipTransportImplBase { public ISipTransport getBinder() { return mSipTransportImpl; } + + /** + * Set default Executor from ImsService. + * @param executor The default executor for the framework to use when executing the methods + * overridden by the implementation of SipTransport. + * @hide + */ + public final void setDefaultExecutor(@NonNull Executor executor) { + if (mBinderExecutor == null) { + mBinderExecutor = executor; + } + } } |