diff options
author | Virkumar Karavate <virkumar@google.com> | 2022-02-08 18:08:15 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2022-02-08 18:08:15 +0000 |
commit | 984a31a9fbb1d193199674bbc7431f474231d206 (patch) | |
tree | 913d49bb7c78a352f9d320fd842344b6c01695de | |
parent | af771eb908192409118a36a02d604d68649948ae (diff) | |
parent | e5c38399ec25e383d3454163f54bbf2e49dea168 (diff) | |
download | base-984a31a9fbb1d193199674bbc7431f474231d206.tar.gz |
Merge "(ImsService API changes for Better IMS Threading) ImsService to execute binder calls in Executor."
11 files changed, 1049 insertions, 229 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 4480e041f52c..7fe3703fac0d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -12926,6 +12926,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); @@ -13546,6 +13547,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); @@ -13579,7 +13581,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); @@ -13684,6 +13686,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); @@ -13736,6 +13739,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); @@ -13848,6 +13852,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; + } + } } |