Merge "(ImsService API changes for Better IMS Threading) ImsService to execute binder calls in Executor." am: 984a31a9fb

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1958681

Change-Id: I341279de701c64c9a769d11ede6ba8f22d05b350
This commit is contained in:
Virkumar Karavate
2022-02-08 18:47:59 +00:00
committed by Automerger Merge Worker
11 changed files with 1049 additions and 229 deletions

View File

@@ -12926,6 +12926,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);
@@ -13546,6 +13547,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);
@@ -13579,7 +13581,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);
@@ -13684,6 +13686,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);
@@ -13736,6 +13739,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);
@@ -13848,6 +13852,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;
}
}
} }