444 lines
18 KiB
Java
444 lines
18 KiB
Java
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License
|
|
*/
|
|
|
|
package android.telephony.ims.feature;
|
|
|
|
import android.annotation.CallbackExecutor;
|
|
import android.annotation.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.SystemApi;
|
|
import android.annotation.TestApi;
|
|
import android.net.Uri;
|
|
import android.os.Binder;
|
|
import android.os.RemoteException;
|
|
import android.telephony.ims.RcsContactUceCapability;
|
|
import android.telephony.ims.aidl.IImsCapabilityCallback;
|
|
import android.telephony.ims.aidl.IImsRcsFeature;
|
|
import android.telephony.ims.aidl.IRcsFeatureListener;
|
|
import android.telephony.ims.stub.ImsRegistrationImplBase;
|
|
import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
|
|
import android.telephony.ims.stub.RcsSipOptionsImplBase;
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.util.FunctionalUtils;
|
|
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.util.List;
|
|
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;
|
|
|
|
/**
|
|
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
|
|
* this class and provide implementations of the RcsFeature methods that they support.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@TestApi
|
|
public class RcsFeature extends ImsFeature {
|
|
|
|
private static final String LOG_TAG = "RcsFeature";
|
|
|
|
private static final class RcsFeatureBinder extends IImsRcsFeature.Stub {
|
|
// Reference the outer class in order to have better test coverage metrics instead of
|
|
// creating a inner class referencing the outer class directly.
|
|
private final RcsFeature mReference;
|
|
private final Executor mExecutor;
|
|
|
|
RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
|
|
mReference = classRef;
|
|
mExecutor = executor;
|
|
}
|
|
|
|
@Override
|
|
public void setListener(IRcsFeatureListener listener) {
|
|
mReference.setListener(listener);
|
|
}
|
|
|
|
@Override
|
|
public int queryCapabilityStatus() throws RemoteException {
|
|
return executeMethodAsyncForResult(
|
|
() -> mReference.queryCapabilityStatus().mCapabilities,
|
|
"queryCapabilityStatus");
|
|
}
|
|
|
|
@Override
|
|
public void addCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
|
|
executeMethodAsync(() -> mReference.addCapabilityCallback(c), "addCapabilityCallback");
|
|
}
|
|
|
|
@Override
|
|
public void removeCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
|
|
executeMethodAsync(() -> mReference.removeCapabilityCallback(c),
|
|
"removeCapabilityCallback");
|
|
}
|
|
|
|
@Override
|
|
public void changeCapabilitiesConfiguration(CapabilityChangeRequest r,
|
|
IImsCapabilityCallback c) throws RemoteException {
|
|
executeMethodAsync(() -> mReference.requestChangeEnabledCapabilities(r, c),
|
|
"changeCapabilitiesConfiguration");
|
|
}
|
|
|
|
@Override
|
|
public void queryCapabilityConfiguration(int capability, int radioTech,
|
|
IImsCapabilityCallback c) throws RemoteException {
|
|
executeMethodAsync(() -> mReference.queryCapabilityConfigurationInternal(capability,
|
|
radioTech, c), "queryCapabilityConfiguration");
|
|
}
|
|
|
|
@Override
|
|
public int getFeatureState() throws RemoteException {
|
|
return executeMethodAsyncForResult(mReference::getFeatureState, "getFeatureState");
|
|
}
|
|
|
|
// RcsPresenceExchangeImplBase specific APIS
|
|
@Override
|
|
public void requestCapabilities(List<Uri> uris, int operationToken) throws RemoteException {
|
|
executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
|
|
.requestCapabilities(uris, operationToken), "requestCapabilities");
|
|
}
|
|
@Override
|
|
public void updateCapabilities(RcsContactUceCapability capabilities, int operationToken)
|
|
throws RemoteException {
|
|
executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
|
|
.updateCapabilities(capabilities, operationToken),
|
|
"updateCapabilities");
|
|
|
|
}
|
|
// RcsSipOptionsImplBase specific APIS
|
|
@Override
|
|
public void sendCapabilityRequest(Uri contactUri, RcsContactUceCapability capabilities,
|
|
int operationToken) throws RemoteException {
|
|
executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
|
|
.sendCapabilityRequest(contactUri, capabilities, operationToken),
|
|
"sendCapabilityRequest");
|
|
|
|
}
|
|
@Override
|
|
public void respondToCapabilityRequest(String contactUri,
|
|
RcsContactUceCapability ownCapabilities, int operationToken)
|
|
throws RemoteException {
|
|
executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
|
|
.respondToCapabilityRequest(contactUri, ownCapabilities,
|
|
operationToken), "respondToCapabilityRequest");
|
|
|
|
}
|
|
@Override
|
|
public void respondToCapabilityRequestWithError(Uri contactUri, int code, String reason,
|
|
int operationToken) throws RemoteException {
|
|
executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
|
|
.respondToCapabilityRequestWithError(contactUri, code, reason,
|
|
operationToken), "respondToCapabilityRequestWithError");
|
|
}
|
|
|
|
// Call the methods with a clean calling identity on the executor and wait indefinitely for
|
|
// the future to return.
|
|
private void executeMethodAsync(FunctionalUtils.ThrowingRunnable r, String errorLogName)
|
|
throws RemoteException {
|
|
// call with a clean calling identity on the executor and wait indefinitely for the
|
|
// future to return.
|
|
try {
|
|
CompletableFuture.runAsync(
|
|
() -> Binder.withCleanCallingIdentity(r), mExecutor).join();
|
|
} catch (CancellationException | CompletionException e) {
|
|
Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
|
|
+ e.getMessage());
|
|
throw new RemoteException(e.getMessage());
|
|
}
|
|
}
|
|
|
|
private <T> T executeMethodAsyncForResult(FunctionalUtils.ThrowingSupplier<T> r,
|
|
String errorLogName) throws RemoteException {
|
|
// call with a clean calling identity on the executor and wait indefinitely for the
|
|
// future to return.
|
|
CompletableFuture<T> future = CompletableFuture.supplyAsync(
|
|
() -> Binder.withCleanCallingIdentity(r), mExecutor);
|
|
try {
|
|
return future.get();
|
|
} catch (ExecutionException | InterruptedException e) {
|
|
Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
|
|
+ e.getMessage());
|
|
throw new RemoteException(e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Contains the capabilities defined and supported by a {@link RcsFeature} in the
|
|
* form of a bitmask. The capabilities that are used in the RcsFeature are
|
|
* defined as:
|
|
* {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
|
|
* {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
|
|
*
|
|
* The enabled capabilities of this RcsFeature will be set by the framework
|
|
* using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
|
|
* After the capabilities have been set, the RcsFeature may then perform the necessary bring up
|
|
* of the capability and notify the capability status as true using
|
|
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
|
|
* framework that the capability is available for usage.
|
|
* @hide
|
|
*/
|
|
public static class RcsImsCapabilities extends Capabilities {
|
|
/** @hide*/
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
|
|
CAPABILITY_TYPE_NONE,
|
|
CAPABILITY_TYPE_OPTIONS_UCE,
|
|
CAPABILITY_TYPE_PRESENCE_UCE
|
|
})
|
|
public @interface RcsImsCapabilityFlag {}
|
|
|
|
/**
|
|
* Undefined capability type for initialization
|
|
* @hide
|
|
*/
|
|
public static final int CAPABILITY_TYPE_NONE = 0;
|
|
|
|
/**
|
|
* This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
|
|
* framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
|
|
* If not set, this RcsFeature should not service capability requests.
|
|
* @hide
|
|
*/
|
|
public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
|
|
|
|
/**
|
|
* This carrier supports User Capability Exchange using a presence server as defined by the
|
|
* framework. If set, the RcsFeature should support capability exchange using a presence
|
|
* server. If not set, this RcsFeature should not publish capabilities or service capability
|
|
* requests using presence.
|
|
* @hide
|
|
*/
|
|
public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
|
|
|
|
/**@hide*/
|
|
public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) {
|
|
super(capabilities);
|
|
}
|
|
|
|
/**@hide*/
|
|
private RcsImsCapabilities(Capabilities c) {
|
|
super(c.getMask());
|
|
}
|
|
|
|
/**@hide*/
|
|
@Override
|
|
public void addCapabilities(@RcsImsCapabilityFlag int capabilities) {
|
|
super.addCapabilities(capabilities);
|
|
}
|
|
|
|
/**@hide*/
|
|
@Override
|
|
public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) {
|
|
super.removeCapabilities(capabilities);
|
|
}
|
|
|
|
/**@hide*/
|
|
@Override
|
|
public boolean isCapable(@RcsImsCapabilityFlag int capabilities) {
|
|
return super.isCapable(capabilities);
|
|
}
|
|
}
|
|
|
|
private final RcsFeatureBinder mImsRcsBinder;
|
|
private IRcsFeatureListener mListenerBinder;
|
|
private RcsPresenceExchangeImplBase mPresExchange;
|
|
private RcsSipOptionsImplBase mSipOptions;
|
|
|
|
/**
|
|
* Create a new RcsFeature.
|
|
* <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 RcsFeature#RcsFeature(Executor)} instead.
|
|
*/
|
|
public RcsFeature() {
|
|
super();
|
|
// Run on the Binder threads that call them.
|
|
mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run);
|
|
}
|
|
|
|
/**
|
|
* Create a new RcsFeature using the Executor specified for methods being called by the
|
|
* framework.
|
|
* @param executor The executor for the framework to use when making calls to this service.
|
|
* @hide
|
|
*/
|
|
public RcsFeature(@NonNull Executor executor) {
|
|
super();
|
|
if (executor == null) {
|
|
throw new IllegalArgumentException("executor can not be null.");
|
|
}
|
|
// Run on the Binder thread by default.
|
|
mImsRcsBinder = new RcsFeatureBinder(this, executor);
|
|
}
|
|
|
|
/**
|
|
* Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is
|
|
* set, the {@link RcsFeature} has brought up the capability and is ready for framework
|
|
* requests. To change the status of the capabilities
|
|
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
|
|
* @hide
|
|
*/
|
|
@Override
|
|
public final RcsImsCapabilities queryCapabilityStatus() {
|
|
return new RcsImsCapabilities(super.queryCapabilityStatus());
|
|
}
|
|
|
|
/**
|
|
* Notify the framework that the capabilities status has changed. If a capability is enabled,
|
|
* this signals to the framework that the capability has been initialized and is ready.
|
|
* Call {@link #queryCapabilityStatus()} to return the current capability status.
|
|
* @hide
|
|
*/
|
|
public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
|
|
if (c == null) {
|
|
throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
|
|
}
|
|
super.notifyCapabilitiesStatusChanged(c);
|
|
}
|
|
|
|
/**
|
|
* Provides the RcsFeature with the ability to return the framework capability configuration set
|
|
* by the framework. When the framework calls
|
|
* {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
|
|
* enable or disable capability A, this method should return the correct configuration for
|
|
* capability A afterwards (until it has changed).
|
|
* @hide
|
|
*/
|
|
public boolean queryCapabilityConfiguration(
|
|
@RcsImsCapabilities.RcsImsCapabilityFlag int capability,
|
|
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
|
|
// Base Implementation - Override to provide functionality
|
|
return false;
|
|
}
|
|
/**
|
|
* Called from the framework when the {@link RcsImsCapabilities} that have been configured for
|
|
* this {@link RcsFeature} has changed.
|
|
* <p>
|
|
* For each newly enabled capability flag, the corresponding capability should be brought up in
|
|
* the {@link RcsFeature} and registered on the network. For each newly disabled capability
|
|
* flag, the corresponding capability should be brought down, and deregistered. Once a new
|
|
* capability has been initialized and is ready for usage, the status of that capability should
|
|
* also be set to true using {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This
|
|
* will notify the framework that the capability is ready.
|
|
* <p>
|
|
* If for some reason one or more of these capabilities can not be enabled/disabled,
|
|
* {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
|
|
* be called for each capability change that resulted in an error.
|
|
* @hide
|
|
*/
|
|
@Override
|
|
public void changeEnabledCapabilities(CapabilityChangeRequest request,
|
|
CapabilityCallbackProxy c) {
|
|
// Base Implementation - Override to provide functionality
|
|
}
|
|
|
|
/**
|
|
* Retrieve the implementation of SIP OPTIONS for this {@link RcsFeature}.
|
|
* <p>
|
|
* Will only be requested by the framework if capability exchange via SIP OPTIONS is
|
|
* configured as capable during a
|
|
* {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
|
|
* operation and the RcsFeature sets the status of the capability to true using
|
|
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
|
|
*
|
|
* @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if
|
|
* it is supported by the device.
|
|
* @hide
|
|
*/
|
|
public RcsSipOptionsImplBase getOptionsExchangeImpl() {
|
|
// Base Implementation, override to implement functionality
|
|
return new RcsSipOptionsImplBase();
|
|
}
|
|
|
|
/**
|
|
* Retrieve the implementation of UCE presence for this {@link RcsFeature}.
|
|
* Will only be requested by the framework if presence exchang is configured as capable during
|
|
* a {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
|
|
* operation and the RcsFeature sets the status of the capability to true using
|
|
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
|
|
*
|
|
* @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence
|
|
* exchange if it is supported by the device.
|
|
* @hide
|
|
*/
|
|
public RcsPresenceExchangeImplBase getPresenceExchangeImpl() {
|
|
// Base Implementation, override to implement functionality.
|
|
return new RcsPresenceExchangeImplBase();
|
|
}
|
|
|
|
/**{@inheritDoc}*/
|
|
@Override
|
|
public void onFeatureRemoved() {
|
|
|
|
}
|
|
|
|
/**{@inheritDoc}*/
|
|
@Override
|
|
public void onFeatureReady() {
|
|
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
@Override
|
|
public final IImsRcsFeature getBinder() {
|
|
return mImsRcsBinder;
|
|
}
|
|
|
|
/**@hide*/
|
|
public IRcsFeatureListener getListener() {
|
|
synchronized (mLock) {
|
|
return mListenerBinder;
|
|
}
|
|
}
|
|
|
|
private void setListener(IRcsFeatureListener listener) {
|
|
synchronized (mLock) {
|
|
mListenerBinder = listener;
|
|
if (mListenerBinder != null) {
|
|
onFeatureReady();
|
|
}
|
|
}
|
|
}
|
|
|
|
private RcsPresenceExchangeImplBase getPresenceExchangeInternal() {
|
|
synchronized (mLock) {
|
|
if (mPresExchange == null) {
|
|
mPresExchange = getPresenceExchangeImpl();
|
|
mPresExchange.initialize(this);
|
|
}
|
|
return mPresExchange;
|
|
}
|
|
}
|
|
|
|
private RcsSipOptionsImplBase getOptionsExchangeInternal() {
|
|
synchronized (mLock) {
|
|
if (mSipOptions == null) {
|
|
mSipOptions = getOptionsExchangeImpl();
|
|
mSipOptions.initialize(this);
|
|
}
|
|
return mSipOptions;
|
|
}
|
|
}
|
|
}
|