(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
This commit is contained in:
virkumar
2021-11-23 14:44:42 +00:00
parent de761c9fc9
commit b9f6576753
11 changed files with 1049 additions and 229 deletions

View File

@@ -13506,6 +13506,7 @@ package android.telephony.ims {
method public void disableIms(int); method public void disableIms(int);
method public void enableIms(int); method public void enableIms(int);
method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int); method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
method @NonNull public java.util.concurrent.Executor getExecutor();
method public long getImsServiceCapabilities(); method public long getImsServiceCapabilities();
method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int); method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(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 { public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
ctor public MmTelFeature(); 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 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 public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int); 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 { public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
ctor @Deprecated public RcsFeature(); ctor public RcsFeature();
ctor public RcsFeature(@NonNull java.util.concurrent.Executor); 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 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); 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 { public class ImsConfigImplBase {
ctor public ImsConfigImplBase(@NonNull java.util.concurrent.Executor);
ctor public ImsConfigImplBase(); ctor public ImsConfigImplBase();
method public int getConfigInt(int); method public int getConfigInt(int);
method public String getConfigString(int); method public String getConfigString(int);
@@ -14310,6 +14313,7 @@ package android.telephony.ims.stub {
public class ImsRegistrationImplBase { public class ImsRegistrationImplBase {
ctor public 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 onDeregistered(android.telephony.ims.ImsReasonInfo);
method public final void onRegistered(int); method public final void onRegistered(int);
method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes); method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
@@ -14422,6 +14426,7 @@ package android.telephony.ims.stub {
} }
public class SipTransportImplBase { public class SipTransportImplBase {
ctor public SipTransportImplBase();
ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor); 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 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); method public void destroySipDelegate(@NonNull android.telephony.ims.stub.SipDelegate, int);

View File

@@ -17,6 +17,7 @@
package android.telephony.ims; package android.telephony.ims;
import android.annotation.LongDef; import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.SystemApi; import android.annotation.SystemApi;
@@ -44,11 +45,18 @@ import android.util.SparseArray;
import com.android.ims.internal.IImsFeatureStatusCallback; import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; 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 * 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 final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
private IImsServiceControllerListener mListener; 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. * Listener that notifies the framework of ImsService changes.
@@ -201,78 +223,132 @@ public class ImsService extends Service {
@Override @Override
public IImsMmTelFeature createMmTelFeature(int slotId) { public IImsMmTelFeature createMmTelFeature(int slotId) {
return createMmTelFeatureInternal(slotId); return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId),
"createMmTelFeature");
} }
@Override @Override
public IImsRcsFeature createRcsFeature(int slotId) { public IImsRcsFeature createRcsFeature(int slotId) {
return createRcsFeatureInternal(slotId); return executeMethodAsyncForResult(() -> createRcsFeatureInternal(slotId),
"createRcsFeature");
} }
@Override @Override
public void addFeatureStatusCallback(int slotId, int featureType, public void addFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) { IImsFeatureStatusCallback c) {
ImsService.this.addImsFeatureStatusCallback(slotId, featureType, c); executeMethodAsync(() -> ImsService.this.addImsFeatureStatusCallback(
slotId, featureType, c), "addFeatureStatusCallback");
} }
@Override @Override
public void removeFeatureStatusCallback(int slotId, int featureType, public void removeFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) { IImsFeatureStatusCallback c) {
ImsService.this.removeImsFeatureStatusCallback(slotId, featureType, c); executeMethodAsync(() -> ImsService.this.removeImsFeatureStatusCallback(
slotId, featureType, c), "removeFeatureStatusCallback");
} }
@Override @Override
public void removeImsFeature(int slotId, int featureType) { public void removeImsFeature(int slotId, int featureType) {
ImsService.this.removeImsFeature(slotId, featureType); executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType),
"removeImsFeature");
} }
@Override @Override
public ImsFeatureConfiguration querySupportedImsFeatures() { public ImsFeatureConfiguration querySupportedImsFeatures() {
return ImsService.this.querySupportedImsFeatures(); return executeMethodAsyncForResult(() -> ImsService.this.querySupportedImsFeatures(),
"ImsFeatureConfiguration");
} }
@Override @Override
public long getImsServiceCapabilities() { public long getImsServiceCapabilities() {
long caps = ImsService.this.getImsServiceCapabilities(); return executeMethodAsyncForResult(() -> {
long sanitizedCaps = sanitizeCapabilities(caps); long caps = ImsService.this.getImsServiceCapabilities();
if (caps != sanitizedCaps) { long sanitizedCaps = sanitizeCapabilities(caps);
Log.w(LOG_TAG, "removing invalid bits from field: 0x" if (caps != sanitizedCaps) {
+ Long.toHexString(caps ^ sanitizedCaps)); Log.w(LOG_TAG, "removing invalid bits from field: 0x"
} + Long.toHexString(caps ^ sanitizedCaps));
return sanitizedCaps; }
return sanitizedCaps;
}, "getImsServiceCapabilities");
} }
@Override @Override
public void notifyImsServiceReadyForFeatureCreation() { public void notifyImsServiceReadyForFeatureCreation() {
ImsService.this.readyForFeatureCreation(); executeMethodAsync(() -> ImsService.this.readyForFeatureCreation(),
"notifyImsServiceReadyForFeatureCreation");
} }
@Override @Override
public IImsConfig getConfig(int slotId) { public IImsConfig getConfig(int slotId) {
ImsConfigImplBase c = ImsService.this.getConfig(slotId); return executeMethodAsyncForResult(() -> {
return c != null ? c.getIImsConfig() : null; ImsConfigImplBase c = ImsService.this.getConfig(slotId);
if (c != null) {
c.setDefaultExecutor(mExecutor);
return c.getIImsConfig();
} else {
return null;
}
}, "getConfig");
} }
@Override @Override
public IImsRegistration getRegistration(int slotId) { public IImsRegistration getRegistration(int slotId) {
ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId); return executeMethodAsyncForResult(() -> {
return r != null ? r.getBinder() : null; ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
if (r != null) {
r.setDefaultExecutor(mExecutor);
return r.getBinder();
} else {
return null;
}
}, "getRegistration");
} }
@Override @Override
public ISipTransport getSipTransport(int slotId) { public ISipTransport getSipTransport(int slotId) {
SipTransportImplBase s = ImsService.this.getSipTransport(slotId); return executeMethodAsyncForResult(() -> {
return s != null ? s.getBinder() : null; SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
if (s != null) {
s.setDefaultExecutor(mExecutor);
return s.getBinder();
} else {
return null;
}
}, "getSipTransport");
} }
@Override @Override
public void enableIms(int slotId) { public void enableIms(int slotId) {
ImsService.this.enableIms(slotId); executeMethodAsync(() -> ImsService.this.enableIms(slotId), "enableIms");
} }
@Override @Override
public void disableIms(int slotId) { 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); MmTelFeature f = createMmTelFeature(slotId);
if (f != null) { if (f != null) {
setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL); setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
f.setDefaultExecutor(mExecutor);
return f.getBinder(); return f.getBinder();
} else { } else {
Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned."); Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
@@ -310,6 +387,7 @@ public class ImsService extends Service {
private IImsRcsFeature createRcsFeatureInternal(int slotId) { private IImsRcsFeature createRcsFeatureInternal(int slotId) {
RcsFeature f = createRcsFeature(slotId); RcsFeature f = createRcsFeature(slotId);
if (f != null) { if (f != null) {
f.setDefaultExecutor(mExecutor);
setupFeature(f, slotId, ImsFeature.FEATURE_RCS); setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
return f.getBinder(); return f.getBinder();
} else { } else {
@@ -562,4 +640,15 @@ public class ImsService extends Service {
result.append("}"); result.append("}");
return result.toString(); 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;
}
} }

View File

@@ -40,16 +40,25 @@ import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase; import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase; import android.telephony.ims.stub.ImsUtImplBase;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log;
import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint; import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsUt; import com.android.ims.internal.IImsUt;
import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.List; import java.util.List;
import java.util.Set; 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. * 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 { public class MmTelFeature extends ImsFeature {
private static final String LOG_TAG = "MmTelFeature"; private static final String LOG_TAG = "MmTelFeature";
private Executor mExecutor;
/** /**
* @hide * @hide
@@ -68,160 +78,261 @@ public class MmTelFeature extends ImsFeature {
public MmTelFeature() { 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() { private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
@Override @Override
public void setListener(IImsMmTelListener l) { public void setListener(IImsMmTelListener l) {
MmTelFeature.this.setListener(l); executeMethodAsyncNoException(() -> MmTelFeature.this.setListener(l), "setListener");
} }
@Override @Override
public int getFeatureState() throws RemoteException { public int getFeatureState() throws RemoteException {
try { return executeMethodAsyncForResult(() -> MmTelFeature.this.getFeatureState(),
return MmTelFeature.this.getFeatureState(); "getFeatureState");
} catch (Exception e) {
throw new RemoteException(e.getMessage());
}
} }
@Override @Override
public ImsCallProfile createCallProfile(int callSessionType, int callType) public ImsCallProfile createCallProfile(int callSessionType, int callType)
throws RemoteException { throws RemoteException {
synchronized (mLock) { return executeMethodAsyncForResult(() -> MmTelFeature.this.createCallProfile(
try { callSessionType, callType), "createCallProfile");
return MmTelFeature.this.createCallProfile(callSessionType, callType);
} catch (Exception e) {
throw new RemoteException(e.getMessage());
}
}
} }
@Override @Override
public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types) public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types)
throws RemoteException { throws RemoteException {
synchronized (mLock) { executeMethodAsync(() -> MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(
try { new ArraySet<>(types)), "changeOfferedRtpHeaderExtensionTypes");
MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(new ArraySet<>(types));
} catch (Exception e) {
throw new RemoteException(e.getMessage());
}
}
} }
@Override @Override
public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException { public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
synchronized (mLock) { AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
return createCallSessionInterface(profile); 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 @Override
public int shouldProcessCall(String[] numbers) { public int shouldProcessCall(String[] numbers) {
synchronized (mLock) { Integer result = executeMethodAsyncForResultNoException(() ->
return MmTelFeature.this.shouldProcessCall(numbers); MmTelFeature.this.shouldProcessCall(numbers), "shouldProcessCall");
if (result != null) {
return result.intValue();
} else {
return PROCESS_CALL_CSFB;
} }
} }
@Override @Override
public IImsUt getUtInterface() throws RemoteException { public IImsUt getUtInterface() throws RemoteException {
synchronized (mLock) { AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
return MmTelFeature.this.getUtInterface(); 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 @Override
public IImsEcbm getEcbmInterface() throws RemoteException { public IImsEcbm getEcbmInterface() throws RemoteException {
synchronized (mLock) { AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
return MmTelFeature.this.getEcbmInterface(); 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 @Override
public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException { public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
synchronized (mLock) { executeMethodAsync(() -> MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage),
try { "setUiTtyMode");
MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
} catch (Exception e) {
throw new RemoteException(e.getMessage());
}
}
} }
@Override @Override
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
synchronized (mLock) { AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
return MmTelFeature.this.getMultiEndpointInterface(); 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 @Override
public int queryCapabilityStatus() { 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 @Override
public void addCapabilityCallback(IImsCapabilityCallback c) { public void addCapabilityCallback(IImsCapabilityCallback c) {
// no need to lock, structure already handles multithreading. executeMethodAsyncNoException(() -> MmTelFeature.this
MmTelFeature.this.addCapabilityCallback(c); .addCapabilityCallback(c), "addCapabilityCallback");
} }
@Override @Override
public void removeCapabilityCallback(IImsCapabilityCallback c) { public void removeCapabilityCallback(IImsCapabilityCallback c) {
// no need to lock, structure already handles multithreading. executeMethodAsyncNoException(() -> MmTelFeature.this
MmTelFeature.this.removeCapabilityCallback(c); .removeCapabilityCallback(c), "removeCapabilityCallback");
} }
@Override @Override
public void changeCapabilitiesConfiguration(CapabilityChangeRequest request, public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
IImsCapabilityCallback c) { IImsCapabilityCallback c) {
MmTelFeature.this.requestChangeEnabledCapabilities(request, c); executeMethodAsyncNoException(() -> MmTelFeature.this
.requestChangeEnabledCapabilities(request, c),
"changeCapabilitiesConfiguration");
} }
@Override @Override
public void queryCapabilityConfiguration(int capability, int radioTech, public void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c) { IImsCapabilityCallback c) {
queryCapabilityConfigurationInternal(capability, radioTech, c); executeMethodAsyncNoException(() -> queryCapabilityConfigurationInternal(
capability, radioTech, c), "queryCapabilityConfiguration");
} }
@Override @Override
public void setSmsListener(IImsSmsListener l) { public void setSmsListener(IImsSmsListener l) {
MmTelFeature.this.setSmsListener(l); executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l),
"setSmsListener");
} }
@Override @Override
public void sendSms(int token, int messageRef, String format, String smsc, boolean retry, public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
byte[] pdu) { byte[] pdu) {
synchronized (mLock) { executeMethodAsyncNoException(() -> MmTelFeature.this
MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu); .sendSms(token, messageRef, format, smsc, retry, pdu), "sendSms");
}
} }
@Override @Override
public void acknowledgeSms(int token, int messageRef, int result) { public void acknowledgeSms(int token, int messageRef, int result) {
synchronized (mLock) { executeMethodAsyncNoException(() -> MmTelFeature.this
MmTelFeature.this.acknowledgeSms(token, messageRef, result); .acknowledgeSms(token, messageRef, result), "acknowledgeSms");
}
} }
@Override @Override
public void acknowledgeSmsReport(int token, int messageRef, int result) { public void acknowledgeSmsReport(int token, int messageRef, int result) {
synchronized (mLock) { executeMethodAsyncNoException(() -> MmTelFeature.this
MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result); .acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport");
}
} }
@Override @Override
public String getSmsFormat() { public String getSmsFormat() {
synchronized (mLock) { return executeMethodAsyncForResultNoException(() -> MmTelFeature.this
return MmTelFeature.this.getSmsFormat(); .getSmsFormat(), "getSmsFormat");
}
} }
@Override @Override
public void onSmsReady() { public void onSmsReady() {
synchronized (mLock) { executeMethodAsyncNoException(() -> MmTelFeature.this.onSmsReady(),
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) public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
throws RemoteException { throws RemoteException {
ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile); 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 { protected IImsUt getUtInterface() throws RemoteException {
ImsUtImplBase utImpl = getUt(); 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 { protected IImsEcbm getEcbmInterface() throws RemoteException {
ImsEcbmImplBase ecbmImpl = getEcbm(); 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 { public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint(); 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() { public final IImsMmTelFeature getBinder() {
return mImsMMTelBinder; 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;
}
}
} }

View File

@@ -70,7 +70,7 @@ public class RcsFeature extends ImsFeature {
// Reference the outer class in order to have better test coverage metrics instead of // Reference the outer class in order to have better test coverage metrics instead of
// creating a inner class referencing the outer class directly. // creating a inner class referencing the outer class directly.
private final RcsFeature mReference; private final RcsFeature mReference;
private final Executor mExecutor; private Executor mExecutor;
RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) { RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
mReference = classRef; mReference = classRef;
@@ -259,7 +259,7 @@ public class RcsFeature extends ImsFeature {
} }
} }
private final Executor mExecutor; private Executor mExecutor;
private final RcsFeatureBinder mImsRcsBinder; private final RcsFeatureBinder mImsRcsBinder;
private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl; private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl;
private CapabilityExchangeEventListener mCapExchangeEventListener; 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 * Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use * {@link Executor} that the methods stubs will be called, use
* {@link RcsFeature#RcsFeature(Executor)} instead. * {@link RcsFeature#RcsFeature(Executor)} instead.
*
* @deprecated Use {@link #RcsFeature(Executor)} to create the RcsFeature.
*/ */
@Deprecated
public RcsFeature() { public RcsFeature() {
super(); super();
mExecutor = Runnable::run;
// Run on the Binder threads that call them. // Run on the Binder threads that call them.
mImsRcsBinder = new RcsFeatureBinder(this, mExecutor); mImsRcsBinder = new RcsFeatureBinder(this, mExecutor);
} }
@@ -477,4 +473,17 @@ public class RcsFeature extends ImsFeature {
return mCapabilityExchangeImpl; 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;
}
}
} }

View File

@@ -30,12 +30,20 @@ import android.telephony.ims.RtpHeaderExtension;
import android.telephony.ims.RtpHeaderExtensionType; import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.aidl.IImsCallSessionListener; import android.telephony.ims.aidl.IImsCallSessionListener;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log;
import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider; import com.android.ims.internal.IImsVideoCallProvider;
import com.android.internal.telephony.util.TelephonyUtils;
import java.util.List; import java.util.List;
import java.util.Set; 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. * 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 // 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. // will break other implementations of ImsCallSession maintained by other ImsServices.
public class ImsCallSessionImplBase implements AutoCloseable { public class ImsCallSessionImplBase implements AutoCloseable {
private static final String LOG_TAG = "ImsCallSessionImplBase";
/** /**
* Notify USSD Mode. * Notify USSD Mode.
*/ */
@@ -110,185 +120,235 @@ public class ImsCallSessionImplBase implements AutoCloseable {
} }
} }
private Executor mExecutor = Runnable::run;
// Non-final for injection by tests // Non-final for injection by tests
private IImsCallSession mServiceImpl = new IImsCallSession.Stub() { private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
@Override @Override
public void close() { public void close() {
ImsCallSessionImplBase.this.close(); executeMethodAsync(() -> ImsCallSessionImplBase.this.close(), "close");
} }
@Override @Override
public String getCallId() { public String getCallId() {
return ImsCallSessionImplBase.this.getCallId(); return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallId(),
"getCallId");
} }
@Override @Override
public ImsCallProfile getCallProfile() { public ImsCallProfile getCallProfile() {
return ImsCallSessionImplBase.this.getCallProfile(); return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallProfile(),
"getCallProfile");
} }
@Override @Override
public ImsCallProfile getLocalCallProfile() { public ImsCallProfile getLocalCallProfile() {
return ImsCallSessionImplBase.this.getLocalCallProfile(); return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
.getLocalCallProfile(), "getLocalCallProfile");
} }
@Override @Override
public ImsCallProfile getRemoteCallProfile() { public ImsCallProfile getRemoteCallProfile() {
return ImsCallSessionImplBase.this.getRemoteCallProfile(); return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
.getRemoteCallProfile(), "getRemoteCallProfile");
} }
@Override @Override
public String getProperty(String name) { public String getProperty(String name) {
return ImsCallSessionImplBase.this.getProperty(name); return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getProperty(name),
"getProperty");
} }
@Override @Override
public int getState() { public int getState() {
return ImsCallSessionImplBase.this.getState(); return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getState(),
"getState");
} }
@Override @Override
public boolean isInCall() { public boolean isInCall() {
return ImsCallSessionImplBase.this.isInCall(); return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isInCall(),
"isInCall");
} }
@Override @Override
public void setListener(IImsCallSessionListener listener) { public void setListener(IImsCallSessionListener listener) {
ImsCallSessionImplBase.this.setListener(new ImsCallSessionListener(listener)); executeMethodAsync(() -> ImsCallSessionImplBase.this.setListener(
new ImsCallSessionListener(listener)), "setListener");
} }
@Override @Override
public void setMute(boolean muted) { public void setMute(boolean muted) {
ImsCallSessionImplBase.this.setMute(muted); executeMethodAsync(() -> ImsCallSessionImplBase.this.setMute(muted), "setMute");
} }
@Override @Override
public void start(String callee, ImsCallProfile profile) { public void start(String callee, ImsCallProfile profile) {
ImsCallSessionImplBase.this.start(callee, profile); executeMethodAsync(() -> ImsCallSessionImplBase.this.start(callee, profile), "start");
} }
@Override @Override
public void startConference(String[] participants, ImsCallProfile profile) throws public void startConference(String[] participants, ImsCallProfile profile) throws
RemoteException { RemoteException {
ImsCallSessionImplBase.this.startConference(participants, profile); executeMethodAsync(() -> ImsCallSessionImplBase.this.startConference(participants,
profile), "startConference");
} }
@Override @Override
public void accept(int callType, ImsStreamMediaProfile profile) { public void accept(int callType, ImsStreamMediaProfile profile) {
ImsCallSessionImplBase.this.accept(callType, profile); executeMethodAsync(() -> ImsCallSessionImplBase.this.accept(callType, profile),
"accept");
} }
@Override @Override
public void deflect(String deflectNumber) { public void deflect(String deflectNumber) {
ImsCallSessionImplBase.this.deflect(deflectNumber); executeMethodAsync(() -> ImsCallSessionImplBase.this.deflect(deflectNumber),
"deflect");
} }
@Override @Override
public void reject(int reason) { public void reject(int reason) {
ImsCallSessionImplBase.this.reject(reason); executeMethodAsync(() -> ImsCallSessionImplBase.this.reject(reason), "reject");
} }
@Override @Override
public void transfer(@NonNull String number, boolean isConfirmationRequired) { public void transfer(@NonNull String number, boolean isConfirmationRequired) {
ImsCallSessionImplBase.this.transfer(number, isConfirmationRequired); executeMethodAsync(() -> ImsCallSessionImplBase.this.transfer(number,
isConfirmationRequired), "transfer");
} }
@Override @Override
public void consultativeTransfer(@NonNull IImsCallSession transferToSession) { public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase(); executeMethodAsync(() -> {
otherSession.setServiceImpl(transferToSession); ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
ImsCallSessionImplBase.this.transfer(otherSession); otherSession.setServiceImpl(transferToSession);
ImsCallSessionImplBase.this.transfer(otherSession);
}, "consultativeTransfer");
} }
@Override @Override
public void terminate(int reason) { public void terminate(int reason) {
ImsCallSessionImplBase.this.terminate(reason); executeMethodAsync(() -> ImsCallSessionImplBase.this.terminate(reason), "terminate");
} }
@Override @Override
public void hold(ImsStreamMediaProfile profile) { public void hold(ImsStreamMediaProfile profile) {
ImsCallSessionImplBase.this.hold(profile); executeMethodAsync(() -> ImsCallSessionImplBase.this.hold(profile), "hold");
} }
@Override @Override
public void resume(ImsStreamMediaProfile profile) { public void resume(ImsStreamMediaProfile profile) {
ImsCallSessionImplBase.this.resume(profile); executeMethodAsync(() -> ImsCallSessionImplBase.this.resume(profile), "resume");
} }
@Override @Override
public void merge() { public void merge() {
ImsCallSessionImplBase.this.merge(); executeMethodAsync(() -> ImsCallSessionImplBase.this.merge(), "merge");
} }
@Override @Override
public void update(int callType, ImsStreamMediaProfile profile) { public void update(int callType, ImsStreamMediaProfile profile) {
ImsCallSessionImplBase.this.update(callType, profile); executeMethodAsync(() -> ImsCallSessionImplBase.this.update(callType, profile),
"update");
} }
@Override @Override
public void extendToConference(String[] participants) { public void extendToConference(String[] participants) {
ImsCallSessionImplBase.this.extendToConference(participants); executeMethodAsync(() -> ImsCallSessionImplBase.this.extendToConference(participants),
"extendToConference");
} }
@Override @Override
public void inviteParticipants(String[] participants) { public void inviteParticipants(String[] participants) {
ImsCallSessionImplBase.this.inviteParticipants(participants); executeMethodAsync(() -> ImsCallSessionImplBase.this.inviteParticipants(participants),
"inviteParticipants");
} }
@Override @Override
public void removeParticipants(String[] participants) { public void removeParticipants(String[] participants) {
ImsCallSessionImplBase.this.removeParticipants(participants); executeMethodAsync(() -> ImsCallSessionImplBase.this.removeParticipants(participants),
"removeParticipants");
} }
@Override @Override
public void sendDtmf(char c, Message result) { public void sendDtmf(char c, Message result) {
ImsCallSessionImplBase.this.sendDtmf(c, result); executeMethodAsync(() -> ImsCallSessionImplBase.this.sendDtmf(c, result), "sendDtmf");
} }
@Override @Override
public void startDtmf(char c) { public void startDtmf(char c) {
ImsCallSessionImplBase.this.startDtmf(c); executeMethodAsync(() -> ImsCallSessionImplBase.this.startDtmf(c), "startDtmf");
} }
@Override @Override
public void stopDtmf() { public void stopDtmf() {
ImsCallSessionImplBase.this.stopDtmf(); executeMethodAsync(() -> ImsCallSessionImplBase.this.stopDtmf(), "stopDtmf");
} }
@Override @Override
public void sendUssd(String ussdMessage) { public void sendUssd(String ussdMessage) {
ImsCallSessionImplBase.this.sendUssd(ussdMessage); executeMethodAsync(() -> ImsCallSessionImplBase.this.sendUssd(ussdMessage), "sendUssd");
} }
@Override @Override
public IImsVideoCallProvider getVideoCallProvider() { public IImsVideoCallProvider getVideoCallProvider() {
return ImsCallSessionImplBase.this.getVideoCallProvider(); return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
.getVideoCallProvider(), "getVideoCallProvider");
} }
@Override @Override
public boolean isMultiparty() { public boolean isMultiparty() {
return ImsCallSessionImplBase.this.isMultiparty(); return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isMultiparty(),
"isMultiparty");
} }
@Override @Override
public void sendRttModifyRequest(ImsCallProfile toProfile) { public void sendRttModifyRequest(ImsCallProfile toProfile) {
ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile); executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile),
"sendRttModifyRequest");
} }
@Override @Override
public void sendRttModifyResponse(boolean status) { public void sendRttModifyResponse(boolean status) {
ImsCallSessionImplBase.this.sendRttModifyResponse(status); executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyResponse(status),
"sendRttModifyResponse");
} }
@Override @Override
public void sendRttMessage(String rttMessage) { public void sendRttMessage(String rttMessage) {
ImsCallSessionImplBase.this.sendRttMessage(rttMessage); executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttMessage(rttMessage),
"sendRttMessage");
} }
@Override @Override
public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> extensions) { public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> extensions) {
ImsCallSessionImplBase.this.sendRtpHeaderExtensions( executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRtpHeaderExtensions(
new ArraySet<RtpHeaderExtension>(extensions)); 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) { public void setServiceImpl(IImsCallSession serviceImpl) {
mServiceImpl = 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;
}
} }

View File

@@ -33,12 +33,21 @@ import android.util.Log;
import com.android.ims.ImsConfig; import com.android.ims.ImsConfig;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.RemoteCallbackListExt; import com.android.internal.telephony.util.RemoteCallbackListExt;
import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; 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 * Controls the modification of IMS specific configurations. For more information on the supported
@@ -81,21 +90,48 @@ public class ImsConfigImplBase {
WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference; WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>(); private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>(); private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
private final Object mLock = new Object();
private Executor mExecutor;
@VisibleForTesting @VisibleForTesting
public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) { public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Executor executor) {
mExecutor = executor;
mImsConfigImplBaseWeakReference = mImsConfigImplBaseWeakReference =
new WeakReference<ImsConfigImplBase>(imsConfigImplBase); new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
} }
@Override @Override
public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException { 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 @Override
public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException { 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. * unavailable.
*/ */
@Override @Override
public synchronized int getConfigInt(int item) throws RemoteException { public int getConfigInt(int item) throws RemoteException {
if (mProvisionedIntValue.containsKey(item)) { AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
return mProvisionedIntValue.get(item); int retVal = executeMethodAsyncForResult(()-> {
} else { int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
int retVal = getImsConfigImpl().getConfigInt(item); synchronized (mLock) {
if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) { if (mProvisionedIntValue.containsKey(item)) {
updateCachedValue(item, retVal, false); 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. * @return value in String format.
*/ */
@Override @Override
public synchronized String getConfigString(int item) throws RemoteException { public String getConfigString(int item) throws RemoteException {
if (mProvisionedStringValue.containsKey(item)) { AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
return mProvisionedStringValue.get(item); String retVal = executeMethodAsyncForResult(()-> {
} else { String returnVal = null;
String retVal = getImsConfigImpl().getConfigString(item); synchronized (mLock) {
if (retVal != null) { if (mProvisionedStringValue.containsKey(item)) {
updateCachedValue(item, retVal, false); 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}. * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
*/ */
@Override @Override
public synchronized int setConfigInt(int item, int value) throws RemoteException { public int setConfigInt(int item, int value) throws RemoteException {
mProvisionedIntValue.remove(item); AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
int retVal = getImsConfigImpl().setConfig(item, value); int retVal = executeMethodAsyncForResult(()-> {
if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) { int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
updateCachedValue(item, value, true); try {
} else { synchronized (mLock) {
Log.d(TAG, "Set provision value of " + item + mProvisionedIntValue.remove(item);
" to " + value + " failed with error code " + retVal); 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; return retVal;
@@ -178,12 +268,30 @@ public class ImsConfigImplBase {
* {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}. * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
*/ */
@Override @Override
public synchronized int setConfigString(int item, String value) public int setConfigString(int item, String value)
throws RemoteException { throws RemoteException {
mProvisionedStringValue.remove(item); AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
int retVal = getImsConfigImpl().setConfig(item, value); int retVal = executeMethodAsyncForResult(()-> {
if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) { int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
updateCachedValue(item, value, true); 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; return retVal;
@@ -191,7 +299,19 @@ public class ImsConfigImplBase {
@Override @Override
public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException { 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 { private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
@@ -206,13 +326,37 @@ public class ImsConfigImplBase {
@Override @Override
public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
throws RemoteException { 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 @Override
public void notifyRcsAutoConfigurationRemoved() public void notifyRcsAutoConfigurationRemoved()
throws RemoteException { 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 { private void notifyImsConfigChanged(int item, int value) throws RemoteException {
@@ -223,50 +367,144 @@ public class ImsConfigImplBase {
getImsConfigImpl().notifyConfigChanged(item, value); getImsConfigImpl().notifyConfigChanged(item, value);
} }
protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) protected void updateCachedValue(int item, int value) {
throws RemoteException { synchronized (mLock) {
mProvisionedIntValue.put(item, value); mProvisionedIntValue.put(item, value);
if (notifyChange) {
notifyImsConfigChanged(item, value);
} }
} }
protected synchronized void updateCachedValue(int item, String value, protected void updateCachedValue(int item, String value) {
boolean notifyChange) throws RemoteException { synchronized (mLock) {
mProvisionedStringValue.put(item, value); mProvisionedStringValue.put(item, value);
if (notifyChange) {
notifyImsConfigChanged(item, value);
} }
} }
@Override @Override
public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { 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 @Override
public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { 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 @Override
public void triggerRcsReconfiguration() throws RemoteException { 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 @Override
public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException { 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 @Override
public void notifyIntImsConfigChanged(int item, int value) throws RemoteException { 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 @Override
public void notifyStringImsConfigChanged(int item, String value) throws RemoteException { 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; 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 * @hide
*/ */
public ImsConfigImplBase(Context context) { public ImsConfigImplBase(@NonNull Context context) {
mImsConfigStub = new ImsConfigStub(this); mImsConfigStub = new ImsConfigStub(this, null);
} }
public ImsConfigImplBase() { public ImsConfigImplBase() {
mImsConfigStub = new ImsConfigStub(this); mImsConfigStub = new ImsConfigStub(this, null);
} }
/** /**
@@ -427,8 +674,10 @@ public class ImsConfigImplBase {
* @param value in Integer format. * @param value in Integer format.
*/ */
public final void notifyProvisionedValueChanged(int item, int value) { public final void notifyProvisionedValueChanged(int item, int value) {
mImsConfigStub.updateCachedValue(item, value);
try { try {
mImsConfigStub.updateCachedValue(item, value, true); mImsConfigStub.notifyImsConfigChanged(item, value);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead."); Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead.");
} }
@@ -443,8 +692,10 @@ public class ImsConfigImplBase {
* @param value in String format. * @param value in String format.
*/ */
public final void notifyProvisionedValueChanged(int item, String value) { public final void notifyProvisionedValueChanged(int item, String value) {
mImsConfigStub.updateCachedValue(item, value);
try { try {
mImsConfigStub.updateCachedValue(item, value, true); mImsConfigStub.notifyImsConfigChanged(item, value);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead."); 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;
}
}
} }

View File

@@ -16,14 +16,21 @@
package android.telephony.ims.stub; package android.telephony.ims.stub;
import android.annotation.NonNull;
import android.annotation.SystemApi; import android.annotation.SystemApi;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsEcbmListener; import com.android.ims.internal.IImsEcbmListener;
import com.android.internal.telephony.util.TelephonyUtils;
import java.util.Objects; 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 * 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 final Object mLock = new Object();
private IImsEcbmListener mListener; private IImsEcbmListener mListener;
private Executor mExecutor = Runnable::run;
private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() { private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
@Override @Override
public void setListener(IImsEcbmListener listener) { public void setListener(IImsEcbmListener listener) {
synchronized (mLock) { executeMethodAsync(() -> {
if (mListener != null && !mListener.asBinder().isBinderAlive()) { if (mListener != null && !mListener.asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder"); Log.w(TAG, "setListener: discarding dead Binder");
mListener = null; mListener = null;
@@ -62,12 +71,25 @@ public class ImsEcbmImplBase {
+ "listener"); + "listener");
mListener = listener; mListener = listener;
} }
} }, "setListener");
} }
@Override @Override
public void exitEmergencyCallbackMode() { 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;
}
} }

View File

@@ -16,6 +16,7 @@
package android.telephony.ims.stub; package android.telephony.ims.stub;
import android.annotation.NonNull;
import android.annotation.SystemApi; import android.annotation.SystemApi;
import android.os.RemoteException; import android.os.RemoteException;
import android.telephony.ims.ImsExternalCallState; import android.telephony.ims.ImsExternalCallState;
@@ -23,9 +24,14 @@ import android.util.Log;
import com.android.ims.internal.IImsExternalCallStateListener; import com.android.ims.internal.IImsExternalCallStateListener;
import com.android.ims.internal.IImsMultiEndpoint; import com.android.ims.internal.IImsMultiEndpoint;
import com.android.internal.telephony.util.TelephonyUtils;
import java.util.List; import java.util.List;
import java.util.Objects; 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 * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
@@ -43,11 +49,13 @@ public class ImsMultiEndpointImplBase {
private IImsExternalCallStateListener mListener; private IImsExternalCallStateListener mListener;
private final Object mLock = new Object(); private final Object mLock = new Object();
private Executor mExecutor = Runnable::run;
private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() { private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
@Override @Override
public void setListener(IImsExternalCallStateListener listener) throws RemoteException { public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
synchronized (mLock) { executeMethodAsync(() -> {
if (mListener != null && !mListener.asBinder().isBinderAlive()) { if (mListener != null && !mListener.asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder"); Log.w(TAG, "setListener: discarding dead Binder");
mListener = null; mListener = null;
@@ -67,12 +75,25 @@ public class ImsMultiEndpointImplBase {
+ "listener"); + "listener");
mListener = listener; mListener = listener;
} }
} }, "setListener");
} }
@Override @Override
public void requestImsExternalCallStateInfo() throws RemoteException { 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() { public void requestImsExternalCallStateInfo() {
Log.d(TAG, "requestImsExternalCallStateInfo() not implemented"); 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;
}
} }

View File

@@ -31,10 +31,19 @@ import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log; import android.util.Log;
import com.android.internal.telephony.util.RemoteCallbackListExt; import com.android.internal.telephony.util.RemoteCallbackListExt;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; 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 * Controls IMS registration for this ImsService and notifies the framework when the IMS
@@ -92,39 +101,114 @@ public class ImsRegistrationImplBase {
// yet. // yet.
private static final int REGISTRATION_STATE_UNKNOWN = -1; 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() { private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@Override @Override
public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
synchronized (mLock) { return executeMethodAsyncForResult(() -> (mRegistrationAttributes == null)
return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE ? REGISTRATION_TECH_NONE : mRegistrationAttributes.getRegistrationTechnology(),
: mRegistrationAttributes.getRegistrationTechnology(); "getRegistrationTechnology");
}
} }
@Override @Override
public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 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 @Override
public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
ImsRegistrationImplBase.this.removeRegistrationCallback(c); executeMethodAsync(() -> ImsRegistrationImplBase.this.removeRegistrationCallback(c),
"removeRegistrationCallback");
} }
@Override @Override
public void triggerFullNetworkRegistration(int sipCode, String sipReason) { public void triggerFullNetworkRegistration(int sipCode, String sipReason) {
ImsRegistrationImplBase.this.triggerFullNetworkRegistration(sipCode, sipReason); executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
.triggerFullNetworkRegistration(sipCode, sipReason),
"triggerFullNetworkRegistration");
} }
@Override @Override
public void triggerUpdateSipDelegateRegistration() { public void triggerUpdateSipDelegateRegistration() {
ImsRegistrationImplBase.this.updateSipDelegateRegistration(); executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
.updateSipDelegateRegistration(), "triggerUpdateSipDelegateRegistration");
} }
@Override @Override
public void triggerSipDelegateDeregistration() { 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); 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;
}
}
} }

View File

@@ -27,10 +27,17 @@ import android.util.Log;
import com.android.ims.internal.IImsUt; import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsUtListener; import com.android.ims.internal.IImsUtListener;
import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Objects; 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 * 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; public static final int INVALID_RESULT = -1;
private Executor mExecutor = Runnable::run;
private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() { private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
private final Object mLock = new Object(); private final Object mLock = new Object();
private ImsUtListener mUtListener; private ImsUtListener mUtListener;
@Override @Override
public void close() throws RemoteException { public void close() throws RemoteException {
ImsUtImplBase.this.close(); executeMethodAsync(() ->ImsUtImplBase.this.close(), "close");
} }
@Override @Override
public int queryCallBarring(int cbType) throws RemoteException { public int queryCallBarring(int cbType) throws RemoteException {
return ImsUtImplBase.this.queryCallBarring(cbType); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallBarring(cbType),
"queryCallBarring");
} }
@Override @Override
public int queryCallForward(int condition, String number) throws RemoteException { public int queryCallForward(int condition, String number) throws RemoteException {
return ImsUtImplBase.this.queryCallForward(condition, number); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallForward(
condition, number), "queryCallForward");
} }
@Override @Override
public int queryCallWaiting() throws RemoteException { public int queryCallWaiting() throws RemoteException {
return ImsUtImplBase.this.queryCallWaiting(); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallWaiting(),
"queryCallWaiting");
} }
@Override @Override
public int queryCLIR() throws RemoteException { public int queryCLIR() throws RemoteException {
return ImsUtImplBase.this.queryCLIR(); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIR(), "queryCLIR");
} }
@Override @Override
public int queryCLIP() throws RemoteException { public int queryCLIP() throws RemoteException {
return ImsUtImplBase.this.queryCLIP(); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIP(), "queryCLIP");
} }
@Override @Override
public int queryCOLR() throws RemoteException { public int queryCOLR() throws RemoteException {
return ImsUtImplBase.this.queryCOLR(); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLR(), "queryCOLR");
} }
@Override @Override
public int queryCOLP() throws RemoteException { public int queryCOLP() throws RemoteException {
return ImsUtImplBase.this.queryCOLP(); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLP(), "queryCOLP");
} }
@Override @Override
public int transact(Bundle ssInfo) throws RemoteException { public int transact(Bundle ssInfo) throws RemoteException {
return ImsUtImplBase.this.transact(ssInfo); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.transact(ssInfo),
"transact");
} }
@Override @Override
public int updateCallBarring(int cbType, int action, String[] barrList) throws public int updateCallBarring(int cbType, int action, String[] barrList) throws
RemoteException { RemoteException {
return ImsUtImplBase.this.updateCallBarring(cbType, action, barrList); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallBarring(
cbType, action, barrList), "updateCallBarring");
} }
@Override @Override
public int updateCallForward(int action, int condition, String number, int serviceClass, public int updateCallForward(int action, int condition, String number, int serviceClass,
int timeSeconds) throws RemoteException { int timeSeconds) throws RemoteException {
return ImsUtImplBase.this.updateCallForward(action, condition, number, serviceClass, return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallForward(
timeSeconds); action, condition, number, serviceClass, timeSeconds), "updateCallForward");
} }
@Override @Override
public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException { public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
return ImsUtImplBase.this.updateCallWaiting(enable, serviceClass); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallWaiting(
enable, serviceClass), "updateCallWaiting");
} }
@Override @Override
public int updateCLIR(int clirMode) throws RemoteException { public int updateCLIR(int clirMode) throws RemoteException {
return ImsUtImplBase.this.updateCLIR(clirMode); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIR(clirMode),
"updateCLIR");
} }
@Override @Override
public int updateCLIP(boolean enable) throws RemoteException { public int updateCLIP(boolean enable) throws RemoteException {
return ImsUtImplBase.this.updateCLIP(enable); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIP(enable),
"updateCLIP");
} }
@Override @Override
public int updateCOLR(int presentation) throws RemoteException { public int updateCOLR(int presentation) throws RemoteException {
return ImsUtImplBase.this.updateCOLR(presentation); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLR(presentation),
"updateCOLR");
} }
@Override @Override
public int updateCOLP(boolean enable) throws RemoteException { public int updateCOLP(boolean enable) throws RemoteException {
return ImsUtImplBase.this.updateCOLP(enable); return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLP(enable),
"updateCOLP");
} }
@Override @Override
public void setListener(IImsUtListener listener) throws RemoteException { public void setListener(IImsUtListener listener) throws RemoteException {
synchronized (mLock) { executeMethodAsync(() -> {
if (mUtListener != null if (mUtListener != null
&& !mUtListener.getListenerInterface().asBinder().isBinderAlive()) { && !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder"); Log.w(TAG, "setListener: discarding dead Binder");
@@ -229,29 +248,59 @@ public class ImsUtImplBase {
+ "listener"); + "listener");
mUtListener = new ImsUtListener(listener); mUtListener = new ImsUtListener(listener);
} }
}
ImsUtImplBase.this.setListener(mUtListener); ImsUtImplBase.this.setListener(mUtListener);
}, "setListener");
} }
@Override @Override
public int queryCallBarringForServiceClass(int cbType, int serviceClass) public int queryCallBarringForServiceClass(int cbType, int serviceClass)
throws RemoteException { throws RemoteException {
return ImsUtImplBase.this.queryCallBarringForServiceClass(cbType, serviceClass); return executeMethodAsyncForResult(() -> ImsUtImplBase.this
.queryCallBarringForServiceClass(cbType, serviceClass),
"queryCallBarringForServiceClass");
} }
@Override @Override
public int updateCallBarringForServiceClass(int cbType, int action, public int updateCallBarringForServiceClass(int cbType, int action,
String[] barrList, int serviceClass) throws RemoteException { String[] barrList, int serviceClass) throws RemoteException {
return ImsUtImplBase.this.updateCallBarringForServiceClass( return executeMethodAsyncForResult(() -> ImsUtImplBase.this
cbType, action, barrList, serviceClass); .updateCallBarringForServiceClass(cbType, action, barrList, serviceClass),
"updateCallBarringForServiceClass");
} }
@Override @Override
public int updateCallBarringWithPassword(int cbType, int action, String[] barrList, public int updateCallBarringWithPassword(int cbType, int action, String[] barrList,
int serviceClass, String password) throws RemoteException { int serviceClass, String password) throws RemoteException {
return ImsUtImplBase.this.updateCallBarringWithPassword( return executeMethodAsyncForResult(() -> ImsUtImplBase.this
cbType, action, barrList, serviceClass, password); .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() { public IImsUt getInterface() {
return mServiceImpl; 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;
}
} }

View File

@@ -86,9 +86,20 @@ public class SipTransportImplBase {
} }
}; };
private final Executor mBinderExecutor; private Executor mBinderExecutor;
private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>(); 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. * Create an implementation of SipTransportImplBase.
* *
@@ -212,4 +223,16 @@ public class SipTransportImplBase {
public ISipTransport getBinder() { public ISipTransport getBinder() {
return mSipTransportImpl; 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;
}
}
} }