1033 lines
44 KiB
Java
1033 lines
44 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;
|
||
|
||
import android.Manifest;
|
||
import android.annotation.CallbackExecutor;
|
||
import android.annotation.IntDef;
|
||
import android.annotation.NonNull;
|
||
import android.annotation.Nullable;
|
||
import android.annotation.RequiresFeature;
|
||
import android.annotation.RequiresPermission;
|
||
import android.annotation.SystemApi;
|
||
import android.content.Context;
|
||
import android.content.pm.PackageManager;
|
||
import android.net.Uri;
|
||
import android.os.Binder;
|
||
import android.os.IBinder;
|
||
import android.os.RemoteException;
|
||
import android.os.ServiceSpecificException;
|
||
import android.telephony.TelephonyFrameworkInitializer;
|
||
import android.telephony.ims.aidl.IImsRcsController;
|
||
import android.telephony.ims.aidl.IRcsUceControllerCallback;
|
||
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
|
||
import android.util.Log;
|
||
|
||
import java.lang.annotation.Retention;
|
||
import java.lang.annotation.RetentionPolicy;
|
||
import java.util.ArrayList;
|
||
import java.util.Collection;
|
||
import java.util.HashMap;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.concurrent.Executor;
|
||
|
||
/**
|
||
* Manages RCS User Capability Exchange for the subscription specified.
|
||
*
|
||
* @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
|
||
*/
|
||
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
|
||
public class RcsUceAdapter {
|
||
private static final String TAG = "RcsUceAdapter";
|
||
|
||
/**
|
||
* This carrier supports User Capability Exchange as, defined by the framework using
|
||
* SIP OPTIONS. If set, the RcsFeature should support capability exchange. If not set, this
|
||
* RcsFeature should not publish capabilities or service capability requests.
|
||
* @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_OPTIONS_UCE} instead.
|
||
* @hide
|
||
*/
|
||
@Deprecated
|
||
public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
|
||
|
||
/**
|
||
* This carrier supports User Capability Exchange as, defined by the framework using a
|
||
* presence server. If set, the RcsFeature should support capability exchange. If not set, this
|
||
* RcsFeature should not publish capabilities or service capability requests.
|
||
* @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_PRESENCE_UCE} instead.
|
||
* @hide
|
||
*/
|
||
@Deprecated
|
||
@SystemApi
|
||
public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
|
||
|
||
/**
|
||
* @deprecated Use {@link ImsRcsManager.RcsImsCapabilityFlag} instead.
|
||
* @hide
|
||
*/
|
||
@Deprecated
|
||
@Retention(RetentionPolicy.SOURCE)
|
||
@IntDef(prefix = "CAPABILITY_TYPE_", value = {
|
||
CAPABILITY_TYPE_OPTIONS_UCE,
|
||
CAPABILITY_TYPE_PRESENCE_UCE
|
||
})
|
||
public @interface RcsImsCapabilityFlag {}
|
||
|
||
/**
|
||
* An unknown error has caused the request to fail.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_GENERIC_FAILURE = 1;
|
||
|
||
/**
|
||
* The carrier network does not have UCE support enabled for this subscriber.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_NOT_ENABLED = 2;
|
||
|
||
/**
|
||
* The data network that the device is connected to does not support UCE currently (e.g. it is
|
||
* 1x only currently).
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_NOT_AVAILABLE = 3;
|
||
|
||
/**
|
||
* The network has responded with SIP 403 error and a reason "User not registered."
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_NOT_REGISTERED = 4;
|
||
|
||
/**
|
||
* The network has responded to this request with a SIP 403 error and reason "not authorized for
|
||
* presence" for this subscriber.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_NOT_AUTHORIZED = 5;
|
||
|
||
/**
|
||
* The network has responded to this request with a SIP 403 error and no reason.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_FORBIDDEN = 6;
|
||
|
||
/**
|
||
* The contact URI requested is not provisioned for voice or it is not known as an IMS
|
||
* subscriber to the carrier network.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_NOT_FOUND = 7;
|
||
|
||
/**
|
||
* The capabilities request contained too many URIs for the carrier network to handle. Retry
|
||
* with a lower number of contact numbers. The number varies per carrier.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
// TODO: Try to integrate this into the API so that the service will split based on carrier.
|
||
public static final int ERROR_REQUEST_TOO_LARGE = 8;
|
||
|
||
/**
|
||
* The network did not respond to the capabilities request before the request timed out.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_REQUEST_TIMEOUT = 9;
|
||
|
||
/**
|
||
* The request failed due to the service having insufficient memory.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_INSUFFICIENT_MEMORY = 10;
|
||
|
||
/**
|
||
* The network was lost while trying to complete the request.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_LOST_NETWORK = 11;
|
||
|
||
/**
|
||
* The network is temporarily unavailable or busy. Retries should only be done after the retry
|
||
* time returned in {@link CapabilitiesCallback#onError} has elapsed.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int ERROR_SERVER_UNAVAILABLE = 12;
|
||
|
||
/**@hide*/
|
||
@Retention(RetentionPolicy.SOURCE)
|
||
@IntDef(prefix = "ERROR_", value = {
|
||
ERROR_GENERIC_FAILURE,
|
||
ERROR_NOT_ENABLED,
|
||
ERROR_NOT_AVAILABLE,
|
||
ERROR_NOT_REGISTERED,
|
||
ERROR_NOT_AUTHORIZED,
|
||
ERROR_FORBIDDEN,
|
||
ERROR_NOT_FOUND,
|
||
ERROR_REQUEST_TOO_LARGE,
|
||
ERROR_REQUEST_TIMEOUT,
|
||
ERROR_INSUFFICIENT_MEMORY,
|
||
ERROR_LOST_NETWORK,
|
||
ERROR_SERVER_UNAVAILABLE
|
||
})
|
||
public @interface ErrorCode {}
|
||
|
||
/**
|
||
* A capability update has been requested but the reason is unknown.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0;
|
||
|
||
/**
|
||
* A capability update has been requested due to the Entity Tag (ETag) expiring.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1;
|
||
|
||
/**
|
||
* A capability update has been requested due to moving to LTE with VoPS disabled.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2;
|
||
|
||
/**
|
||
* A capability update has been requested due to moving to LTE with VoPS enabled.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3;
|
||
|
||
/**
|
||
* A capability update has been requested due to moving to eHRPD.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4;
|
||
|
||
/**
|
||
* A capability update has been requested due to moving to HSPA+.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5;
|
||
|
||
/**
|
||
* A capability update has been requested due to moving to 3G.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6;
|
||
|
||
/**
|
||
* A capability update has been requested due to moving to 2G.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7;
|
||
|
||
/**
|
||
* A capability update has been requested due to moving to WLAN
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8;
|
||
|
||
/**
|
||
* A capability update has been requested due to moving to IWLAN
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9;
|
||
|
||
/**
|
||
* A capability update has been requested due to moving to 5G NR with VoPS disabled.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10;
|
||
|
||
/**
|
||
* A capability update has been requested due to moving to 5G NR with VoPS enabled.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11;
|
||
|
||
/**
|
||
* A capability update has been requested due to IMS being registered over INTERNET PDN.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN = 12;
|
||
|
||
/**@hide*/
|
||
@Retention(RetentionPolicy.SOURCE)
|
||
@IntDef(prefix = "ERROR_", value = {
|
||
CAPABILITY_UPDATE_TRIGGER_UNKNOWN,
|
||
CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED,
|
||
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN
|
||
})
|
||
public @interface StackPublishTriggerType {}
|
||
|
||
/**
|
||
* The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for
|
||
* UCE.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int PUBLISH_STATE_OK = 1;
|
||
|
||
/**
|
||
* The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int PUBLISH_STATE_NOT_PUBLISHED = 2;
|
||
|
||
/**
|
||
* The device has tried to publish its capabilities, which has resulted in an error. This error
|
||
* is related to the fact that the device is not provisioned for voice.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3;
|
||
|
||
/**
|
||
* The device has tried to publish its capabilities, which has resulted in an error. This error
|
||
* is related to the fact that the device is not RCS or UCE provisioned.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
|
||
|
||
/**
|
||
* The last publish resulted in a "408 Request Timeout" response.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
|
||
|
||
/**
|
||
* The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable"
|
||
* or SIP 423 - "Interval too short".
|
||
* <p>
|
||
* Device shall retry with exponential back-off.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int PUBLISH_STATE_OTHER_ERROR = 6;
|
||
|
||
/**
|
||
* The device is currently trying to publish its capabilities to the network.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public static final int PUBLISH_STATE_PUBLISHING = 7;
|
||
|
||
|
||
/**@hide*/
|
||
@Retention(RetentionPolicy.SOURCE)
|
||
@IntDef(prefix = "PUBLISH_STATE_", value = {
|
||
PUBLISH_STATE_OK,
|
||
PUBLISH_STATE_NOT_PUBLISHED,
|
||
PUBLISH_STATE_VOICE_PROVISION_ERROR,
|
||
PUBLISH_STATE_RCS_PROVISION_ERROR,
|
||
PUBLISH_STATE_REQUEST_TIMEOUT,
|
||
PUBLISH_STATE_OTHER_ERROR,
|
||
PUBLISH_STATE_PUBLISHING
|
||
})
|
||
public @interface PublishState {}
|
||
|
||
/**
|
||
* An application can use {@link #addOnPublishStateChangedListener} to register a
|
||
* {@link OnPublishStateChangedListener ), which will notify the user when the publish state to
|
||
* the network changes.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public interface OnPublishStateChangedListener {
|
||
/**
|
||
* Notifies the callback when the publish state has changed.
|
||
* @param publishState The latest update to the publish state.
|
||
*
|
||
* @deprecated Replaced by {@link #onPublishStateChange}, deprecated for
|
||
* sip information.
|
||
*/
|
||
@Deprecated
|
||
void onPublishStateChange(@PublishState int publishState);
|
||
|
||
/**
|
||
* Notifies the callback when the publish state has changed or the publish operation is
|
||
* done.
|
||
* @param attributes The latest information related to the publish.
|
||
*/
|
||
default void onPublishStateChange(@NonNull PublishAttributes attributes) {
|
||
onPublishStateChange(attributes.getPublishState());
|
||
};
|
||
}
|
||
|
||
/**
|
||
* An application can use {@link #addOnPublishStateChangedListener} to register a
|
||
* {@link OnPublishStateChangedListener ), which will notify the user when the publish state to
|
||
* the network changes.
|
||
* @hide
|
||
*/
|
||
public static class PublishStateCallbackAdapter {
|
||
|
||
private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub {
|
||
private final OnPublishStateChangedListener mPublishStateChangeListener;
|
||
private final Executor mExecutor;
|
||
|
||
PublishStateBinder(Executor executor, OnPublishStateChangedListener listener) {
|
||
mExecutor = executor;
|
||
mPublishStateChangeListener = listener;
|
||
}
|
||
|
||
@Override
|
||
public void onPublishUpdated(@NonNull PublishAttributes attributes) {
|
||
if (mPublishStateChangeListener == null) return;
|
||
|
||
final long callingIdentity = Binder.clearCallingIdentity();
|
||
try {
|
||
mExecutor.execute(() ->
|
||
mPublishStateChangeListener.onPublishStateChange(attributes));
|
||
} finally {
|
||
restoreCallingIdentity(callingIdentity);
|
||
}
|
||
}
|
||
}
|
||
|
||
private final PublishStateBinder mBinder;
|
||
|
||
public PublishStateCallbackAdapter(@NonNull Executor executor,
|
||
@NonNull OnPublishStateChangedListener listener) {
|
||
mBinder = new PublishStateBinder(executor, listener);
|
||
}
|
||
|
||
/**@hide*/
|
||
public final IRcsUcePublishStateCallback getBinder() {
|
||
return mBinder;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* A callback for the response to a UCE request. The method
|
||
* {@link CapabilitiesCallback#onCapabilitiesReceived} will be called zero or more times as the
|
||
* capabilities are fetched from multiple sources, both cached on the device and on the network.
|
||
* <p>
|
||
* This request will take a varying amount of time depending on if the contacts requested are
|
||
* cached or if it requires a network query. The timeout time of these requests can vary
|
||
* depending on the network, however in poor cases it could take up to a minute for a request
|
||
* to timeout. In that time, only a subset of capabilities may have been retrieved.
|
||
* <p>
|
||
* After {@link CapabilitiesCallback#onComplete} or {@link CapabilitiesCallback#onError} has
|
||
* been called, the reference to this callback will be discarded on the service side.
|
||
* @see #requestCapabilities(Collection, Executor, CapabilitiesCallback)
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
public interface CapabilitiesCallback {
|
||
|
||
/**
|
||
* The pending capability request has completed successfully for one or more of the
|
||
* requested contacts.
|
||
* This may be called one or more times before the request is fully completed, as
|
||
* capabilities may need to be fetched from multiple sources both on device and on the
|
||
* network. Once the capabilities of all the requested contacts have been received,
|
||
* {@link #onComplete()} will be called. If there was an error during the capability
|
||
* exchange process, {@link #onError(int, long)} will be called instead.
|
||
* @param contactCapabilities List of capabilities associated with each contact requested.
|
||
*/
|
||
void onCapabilitiesReceived(@NonNull List<RcsContactUceCapability> contactCapabilities);
|
||
|
||
/**
|
||
* Called when the pending request has completed successfully due to all requested contacts
|
||
* information being delivered. The callback {@link #onCapabilitiesReceived(List)} will be
|
||
* called one or more times and will contain the contacts in the request that the device has
|
||
* received capabilities for.
|
||
*
|
||
* @see #onComplete(SipDetails) onComplete(SipDetails) provides more information related to
|
||
* the underlying SIP transaction used to perform the capabilities exchange. Either this
|
||
* method or the alternate method should be implemented to determine when the request has
|
||
* completed successfully.
|
||
*/
|
||
default void onComplete() {}
|
||
|
||
/**
|
||
* The pending request has resulted in an error and may need to be retried, depending on the
|
||
* error code.
|
||
* @param errorCode The reason for the framework being unable to process the request.
|
||
* @param retryIntervalMillis The time in milliseconds the requesting application should
|
||
* wait before retrying, if non-zero.
|
||
*
|
||
* @see #onError(int, long, SipDetails) onError(int, long, SipDetails) provides more
|
||
* information related to the underlying SIP transaction that resulted in an error. Either
|
||
* this method or the alternative method should be implemented to determine when the
|
||
* request has completed with an error.
|
||
*/
|
||
default void onError(@ErrorCode int errorCode, long retryIntervalMillis) {}
|
||
|
||
/**
|
||
* Called when the pending request has completed successfully due to all requested contacts
|
||
* information being delivered. The callback {@link #onCapabilitiesReceived(List)} will be
|
||
* called one or more times and will contain the contacts in the request that the device has
|
||
* received capabilities for.
|
||
*
|
||
* This method contains more information about the underlying SIP transaction if it exists.
|
||
* If this information is not needed, {@link #onComplete()} can be implemented
|
||
* instead.
|
||
*
|
||
* @param details The SIP information related to this request if the device supports
|
||
* supplying this information. This parameter will be {@code null} if this
|
||
* information is not available.
|
||
*/
|
||
default void onComplete(@Nullable SipDetails details) {
|
||
onComplete();
|
||
};
|
||
|
||
/**
|
||
* The pending request has resulted in an error and may need to be retried, depending on the
|
||
* error code.
|
||
*
|
||
* This method contains more information about the underlying SIP transaction if it exists.
|
||
* If this information is not needed, {@link #onError(int, long)} can be implemented
|
||
* instead.
|
||
* @param errorCode The reason for the framework being unable to process the request.
|
||
* @param retryIntervalMillis The time in milliseconds the requesting application should
|
||
* wait before retrying, if non-zero.
|
||
* @param details The SIP information related to this request if the device supports
|
||
* supplying this information. This parameter will be {@code null} if this
|
||
* information is not available.
|
||
*/
|
||
default void onError(@ErrorCode int errorCode, long retryIntervalMillis,
|
||
@Nullable SipDetails details) {
|
||
onError(errorCode, retryIntervalMillis);
|
||
};
|
||
}
|
||
|
||
private final Context mContext;
|
||
private final int mSubId;
|
||
private final Map<OnPublishStateChangedListener, PublishStateCallbackAdapter>
|
||
mPublishStateCallbacks;
|
||
|
||
/**
|
||
* Not to be instantiated directly, use {@link ImsRcsManager#getUceAdapter()} to instantiate
|
||
* this manager class.
|
||
* @hide
|
||
*/
|
||
RcsUceAdapter(Context context, int subId) {
|
||
mContext = context;
|
||
mSubId = subId;
|
||
mPublishStateCallbacks = new HashMap<>();
|
||
}
|
||
|
||
/**
|
||
* Request the RCS capabilities for one or more contacts using RCS User Capability Exchange.
|
||
* <p>
|
||
* This API will first check a local cache for the requested numbers and return the cached
|
||
* RCS capabilities of each number if the cache exists and is not stale. If the cache for a
|
||
* number is stale or there is no cached information about the requested number, the device will
|
||
* then perform a query to the carrier's network to request the RCS capabilities of the
|
||
* requested numbers.
|
||
* <p>
|
||
* Depending on the number of requests being sent, this API may throttled internally as the
|
||
* operations are queued to be executed by the carrier's network.
|
||
* <p>
|
||
* Be sure to check the availability of this feature using
|
||
* {@link ImsRcsManager#isAvailable(int, int)} and ensuring
|
||
* {@link
|
||
* android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
|
||
* {@link
|
||
* android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is
|
||
* enabled or else this operation will fail with {@link #ERROR_NOT_AVAILABLE} or
|
||
* {@link #ERROR_NOT_ENABLED}.
|
||
*
|
||
* @param contactNumbers A list of numbers that the capabilities are being requested for.
|
||
* @param executor The executor that will be used when the request is completed and the
|
||
* {@link CapabilitiesCallback} is called.
|
||
* @param c A one-time callback for when the request for capabilities completes or there is an
|
||
* error processing the request.
|
||
* @throws ImsException if the subscription associated with this instance of
|
||
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
|
||
* available. This can happen if the ImsService has crashed, for example, or if the subscription
|
||
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
|
||
* @throws UnsupportedOperationException If the device does not have
|
||
* {@link PackageManager#FEATURE_TELEPHONY_IMS}.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
@RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
|
||
Manifest.permission.READ_CONTACTS})
|
||
public void requestCapabilities(@NonNull Collection<Uri> contactNumbers,
|
||
@NonNull @CallbackExecutor Executor executor,
|
||
@NonNull CapabilitiesCallback c) throws ImsException {
|
||
if (c == null) {
|
||
throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
|
||
}
|
||
if (executor == null) {
|
||
throw new IllegalArgumentException("Must include a non-null Executor.");
|
||
}
|
||
if (contactNumbers == null) {
|
||
throw new IllegalArgumentException("Must include non-null contact number list.");
|
||
}
|
||
|
||
IImsRcsController imsRcsController = getIImsRcsController();
|
||
if (imsRcsController == null) {
|
||
Log.e(TAG, "requestCapabilities: IImsRcsController is null");
|
||
throw new ImsException("Can not find remote IMS service",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
|
||
IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
|
||
@Override
|
||
public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
|
||
final long callingIdentity = Binder.clearCallingIdentity();
|
||
try {
|
||
executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
|
||
} finally {
|
||
restoreCallingIdentity(callingIdentity);
|
||
}
|
||
}
|
||
@Override
|
||
public void onComplete(@Nullable SipDetails details) {
|
||
final long callingIdentity = Binder.clearCallingIdentity();
|
||
try {
|
||
executor.execute(() -> c.onComplete(details));
|
||
} finally {
|
||
restoreCallingIdentity(callingIdentity);
|
||
}
|
||
}
|
||
@Override
|
||
public void onError(int errorCode, long retryAfterMilliseconds,
|
||
@Nullable SipDetails details) {
|
||
final long callingIdentity = Binder.clearCallingIdentity();
|
||
try {
|
||
executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds, details));
|
||
} finally {
|
||
restoreCallingIdentity(callingIdentity);
|
||
}
|
||
}
|
||
};
|
||
|
||
try {
|
||
imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
|
||
mContext.getAttributionTag(), new ArrayList(contactNumbers), internalCallback);
|
||
} catch (ServiceSpecificException e) {
|
||
throw new ImsException(e.toString(), e.errorCode);
|
||
} catch (RemoteException e) {
|
||
Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e);
|
||
throw new ImsException("Remote IMS Service is not available",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Request the RCS capabilities for a phone number using User Capability Exchange.
|
||
* <p>
|
||
* Unlike {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)}, which caches
|
||
* the result received from the network for a certain amount of time and uses that cached result
|
||
* for subsequent requests for RCS capabilities of the same phone number, this API will always
|
||
* request the RCS capabilities of a contact from the carrier's network.
|
||
* <p>
|
||
* Depending on the number of requests, this API may throttled internally as the operations are
|
||
* queued to be executed by the carrier's network.
|
||
* <p>
|
||
* Be sure to check the availability of this feature using
|
||
* {@link ImsRcsManager#isAvailable(int, int)} and ensuring
|
||
* {@link
|
||
* android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
|
||
* {@link
|
||
* android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is
|
||
* enabled or else this operation will fail with
|
||
* {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
|
||
*
|
||
* @param contactNumber The contact of the capabilities is being requested for.
|
||
* @param executor The executor that will be used when the request is completed and the
|
||
* {@link CapabilitiesCallback} is called.
|
||
* @param c A one-time callback for when the request for capabilities completes or there is
|
||
* an error processing the request.
|
||
* @throws ImsException if the subscription associated with this instance of
|
||
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
|
||
* available. This can happen if the ImsService has crashed, for example, or if the subscription
|
||
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
|
||
* @throws UnsupportedOperationException If the device does not have
|
||
* {@link PackageManager#FEATURE_TELEPHONY_IMS}.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
@RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
|
||
Manifest.permission.READ_CONTACTS})
|
||
public void requestAvailability(@NonNull Uri contactNumber,
|
||
@NonNull @CallbackExecutor Executor executor,
|
||
@NonNull CapabilitiesCallback c) throws ImsException {
|
||
if (executor == null) {
|
||
throw new IllegalArgumentException("Must include a non-null Executor.");
|
||
}
|
||
if (contactNumber == null) {
|
||
throw new IllegalArgumentException("Must include non-null contact number.");
|
||
}
|
||
if (c == null) {
|
||
throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
|
||
}
|
||
|
||
IImsRcsController imsRcsController = getIImsRcsController();
|
||
if (imsRcsController == null) {
|
||
Log.e(TAG, "requestAvailability: IImsRcsController is null");
|
||
throw new ImsException("Cannot find remote IMS service",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
|
||
IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
|
||
@Override
|
||
public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
|
||
final long callingIdentity = Binder.clearCallingIdentity();
|
||
try {
|
||
executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
|
||
} finally {
|
||
restoreCallingIdentity(callingIdentity);
|
||
}
|
||
}
|
||
@Override
|
||
public void onComplete(@Nullable SipDetails details) {
|
||
final long callingIdentity = Binder.clearCallingIdentity();
|
||
try {
|
||
executor.execute(() -> c.onComplete(details));
|
||
} finally {
|
||
restoreCallingIdentity(callingIdentity);
|
||
}
|
||
}
|
||
@Override
|
||
public void onError(int errorCode, long retryAfterMilliseconds,
|
||
@Nullable SipDetails details) {
|
||
final long callingIdentity = Binder.clearCallingIdentity();
|
||
try {
|
||
executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds, details));
|
||
} finally {
|
||
restoreCallingIdentity(callingIdentity);
|
||
}
|
||
}
|
||
};
|
||
|
||
try {
|
||
imsRcsController.requestAvailability(mSubId, mContext.getOpPackageName(),
|
||
mContext.getAttributionTag(), contactNumber, internalCallback);
|
||
} catch (ServiceSpecificException e) {
|
||
throw new ImsException(e.toString(), e.errorCode);
|
||
} catch (RemoteException e) {
|
||
Log.e(TAG, "Error calling IImsRcsController#requestAvailability", e);
|
||
throw new ImsException("Remote IMS Service is not available",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Gets the last publish result from the UCE service if the device is using an RCS presence
|
||
* server.
|
||
* @return The last publish result from the UCE service. If the device is using SIP OPTIONS,
|
||
* this method will return {@link #PUBLISH_STATE_OK} as well.
|
||
* @throws ImsException if the subscription associated with this instance of
|
||
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
|
||
* available. This can happen if the ImsService has crashed, for example, or if the subscription
|
||
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
|
||
* @throws UnsupportedOperationException If the device does not have
|
||
* {@link PackageManager#FEATURE_TELEPHONY_IMS}.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
|
||
public @PublishState int getUcePublishState() throws ImsException {
|
||
IImsRcsController imsRcsController = getIImsRcsController();
|
||
if (imsRcsController == null) {
|
||
Log.e(TAG, "getUcePublishState: IImsRcsController is null");
|
||
throw new ImsException("Can not find remote IMS service",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
|
||
try {
|
||
return imsRcsController.getUcePublishState(mSubId);
|
||
} catch (ServiceSpecificException e) {
|
||
throw new ImsException(e.getMessage(), e.errorCode);
|
||
} catch (RemoteException e) {
|
||
Log.e(TAG, "Error calling IImsRcsController#getUcePublishState", e);
|
||
throw new ImsException("Remote IMS Service is not available",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Registers a {@link OnPublishStateChangedListener} with the system, which will provide publish
|
||
* state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
|
||
* <p>
|
||
* Use {@link android.telephony.SubscriptionManager.OnSubscriptionsChangedListener} to listen
|
||
* to subscription
|
||
* changed events and call
|
||
* {@link #removeOnPublishStateChangedListener(OnPublishStateChangedListener)} to clean up.
|
||
* <p>
|
||
* The registered {@link OnPublishStateChangedListener} will also receive a callback when it is
|
||
* registered with the current publish state.
|
||
*
|
||
* @param executor The executor the listener callback events should be run on.
|
||
* @param listener The {@link OnPublishStateChangedListener} to be added.
|
||
* @throws ImsException if the subscription associated with this callback is valid, but
|
||
* the {@link ImsService} associated with the subscription is not available. This can happen if
|
||
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
|
||
* reason.
|
||
* @throws UnsupportedOperationException If the device does not have
|
||
* {@link PackageManager#FEATURE_TELEPHONY_IMS}.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
|
||
public void addOnPublishStateChangedListener(@NonNull @CallbackExecutor Executor executor,
|
||
@NonNull OnPublishStateChangedListener listener) throws ImsException {
|
||
if (executor == null) {
|
||
throw new IllegalArgumentException("Must include a non-null Executor.");
|
||
}
|
||
if (listener == null) {
|
||
throw new IllegalArgumentException(
|
||
"Must include a non-null OnPublishStateChangedListener.");
|
||
}
|
||
|
||
IImsRcsController imsRcsController = getIImsRcsController();
|
||
if (imsRcsController == null) {
|
||
Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null");
|
||
throw new ImsException("Cannot find remote IMS service",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
|
||
PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener);
|
||
try {
|
||
imsRcsController.registerUcePublishStateCallback(mSubId, stateCallback.getBinder());
|
||
} catch (ServiceSpecificException e) {
|
||
throw new ImsException(e.getMessage(), e.errorCode);
|
||
} catch (RemoteException e) {
|
||
Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e);
|
||
throw new ImsException("Remote IMS Service is not available",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Removes an existing {@link OnPublishStateChangedListener}.
|
||
* <p>
|
||
* When the subscription associated with this callback is removed
|
||
* (SIM removed, ESIM swap,etc...), this callback will automatically be removed. If this method
|
||
* is called for an inactive subscription, it will result in a no-op.
|
||
*
|
||
* @param listener The callback to be unregistered.
|
||
* @throws ImsException if the subscription associated with this callback is valid, but
|
||
* the {@link ImsService} associated with the subscription is not available. This can happen if
|
||
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
|
||
* reason.
|
||
* @throws UnsupportedOperationException If the device does not have
|
||
* {@link PackageManager#FEATURE_TELEPHONY_IMS}.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
|
||
public void removeOnPublishStateChangedListener(
|
||
@NonNull OnPublishStateChangedListener listener) throws ImsException {
|
||
if (listener == null) {
|
||
throw new IllegalArgumentException(
|
||
"Must include a non-null OnPublishStateChangedListener.");
|
||
}
|
||
IImsRcsController imsRcsController = getIImsRcsController();
|
||
if (imsRcsController == null) {
|
||
Log.e(TAG, "removeOnPublishStateChangedListener: IImsRcsController is null");
|
||
throw new ImsException("Cannot find remote IMS service",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
|
||
PublishStateCallbackAdapter callback = removePublishStateCallback(listener);
|
||
if (callback == null) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
imsRcsController.unregisterUcePublishStateCallback(mSubId, callback.getBinder());
|
||
} catch (android.os.ServiceSpecificException e) {
|
||
throw new ImsException(e.getMessage(), e.errorCode);
|
||
} catch (RemoteException e) {
|
||
Log.e(TAG, "Error calling IImsRcsController#unregisterUcePublishStateCallback", e);
|
||
throw new ImsException("Remote IMS Service is not available",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* The setting for whether or not the user has opted in to the automatic refresh of the RCS
|
||
* capabilities associated with the contacts in the user's contact address book. By default,
|
||
* this setting is disabled and must be enabled after the user has seen the opt-in dialog shown
|
||
* by {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}.
|
||
* <p>
|
||
* If this feature is enabled, the device will periodically share the phone numbers of all of
|
||
* the contacts in the user's address book with the carrier to refresh the RCS capabilities
|
||
* cache associated with those contacts as the local cache becomes stale.
|
||
* <p>
|
||
* This setting will only enable this feature if
|
||
* {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is
|
||
* also enabled.
|
||
* <p>
|
||
* Note: This setting does not affect whether or not the device publishes its service
|
||
* capabilities if the subscription supports presence publication.
|
||
*
|
||
* @return true if the user has opted in for automatic refresh of the RCS capabilities of their
|
||
* contacts, false otherwise.
|
||
* @throws ImsException if the subscription associated with this instance of
|
||
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
|
||
* available. This can happen if the ImsService has crashed, for example, or if the subscription
|
||
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
|
||
* @throws UnsupportedOperationException If the device does not have
|
||
* {@link PackageManager#FEATURE_TELEPHONY_IMS}.
|
||
*/
|
||
@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
|
||
public boolean isUceSettingEnabled() throws ImsException {
|
||
IImsRcsController imsRcsController = getIImsRcsController();
|
||
if (imsRcsController == null) {
|
||
Log.e(TAG, "isUceSettingEnabled: IImsRcsController is null");
|
||
throw new ImsException("Can not find remote IMS service",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
try {
|
||
// Telephony.SimInfo#IMS_RCS_UCE_ENABLED can also be used to listen to changes to this.
|
||
return imsRcsController.isUceSettingEnabled(mSubId, mContext.getOpPackageName(),
|
||
mContext.getAttributionTag());
|
||
} catch (RemoteException e) {
|
||
Log.e(TAG, "Error calling IImsRcsController#isUceSettingEnabled", e);
|
||
throw new ImsException("Remote IMS Service is not available",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Change the user’s setting for whether or not the user has opted in to the automatic
|
||
* refresh of the RCS capabilities associated with the contacts in the user's contact address
|
||
* book. By default, this setting is disabled and must be enabled using this method after the
|
||
* user has seen the opt-in dialog shown by
|
||
* {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}.
|
||
* <p>
|
||
* If an application wishes to request that the user enable this feature, they must launch an
|
||
* Activity using the Intent {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN},
|
||
* which will ask the user if they wish to enable this feature. This setting must only be
|
||
* enabled after the user has opted-in to this feature.
|
||
* <p>
|
||
* This must not affect the
|
||
* {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)} or
|
||
* {@link #requestAvailability(Uri, Executor, CapabilitiesCallback)} API,
|
||
* as those APIs are still required for per-contact RCS capability queries of phone numbers
|
||
* required for operations such as placing a Video Telephony call or starting an RCS chat
|
||
* session.
|
||
* <p>
|
||
* This setting will only enable this feature if
|
||
* {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is
|
||
* also enabled.
|
||
* <p>
|
||
* Note: This setting does not affect whether or not the device publishes its service
|
||
* capabilities if the subscription supports presence publication.
|
||
*
|
||
* @param isEnabled true if the user has opted in for automatic refresh of the RCS capabilities
|
||
* of their contacts, or false if they have chosen to opt-out. By default this
|
||
* setting is disabled.
|
||
* @throws ImsException if the subscription associated with this instance of
|
||
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
|
||
* available. This can happen if the ImsService has crashed, for example, or if the subscription
|
||
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
|
||
* @throws UnsupportedOperationException If the device does not have
|
||
* {@link PackageManager#FEATURE_TELEPHONY_IMS}.
|
||
* @hide
|
||
*/
|
||
@SystemApi
|
||
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
|
||
public void setUceSettingEnabled(boolean isEnabled) throws ImsException {
|
||
IImsRcsController imsRcsController = getIImsRcsController();
|
||
if (imsRcsController == null) {
|
||
Log.e(TAG, "setUceSettingEnabled: IImsRcsController is null");
|
||
throw new ImsException("Can not find remote IMS service",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
|
||
try {
|
||
imsRcsController.setUceSettingEnabled(mSubId, isEnabled);
|
||
} catch (RemoteException e) {
|
||
Log.e(TAG, "Error calling IImsRcsController#setUceSettingEnabled", e);
|
||
throw new ImsException("Remote IMS Service is not available",
|
||
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Add the {@link OnPublishStateChangedListener} to collection for tracking.
|
||
* @param executor The executor that will be used when the publish state is changed and the
|
||
* {@link OnPublishStateChangedListener} is called.
|
||
* @param listener The {@link OnPublishStateChangedListener} to call the publish state changed.
|
||
* @return The {@link PublishStateCallbackAdapter} to wrapper the
|
||
* {@link OnPublishStateChangedListener}
|
||
*/
|
||
private PublishStateCallbackAdapter addPublishStateCallback(@NonNull Executor executor,
|
||
@NonNull OnPublishStateChangedListener listener) {
|
||
PublishStateCallbackAdapter adapter = new PublishStateCallbackAdapter(executor, listener);
|
||
synchronized (mPublishStateCallbacks) {
|
||
mPublishStateCallbacks.put(listener, adapter);
|
||
}
|
||
return adapter;
|
||
}
|
||
|
||
/**
|
||
* Remove the existing {@link OnPublishStateChangedListener}.
|
||
* @param listener The {@link OnPublishStateChangedListener} to remove from the collection.
|
||
* @return The wrapper class {@link PublishStateCallbackAdapter} associated with the
|
||
* {@link OnPublishStateChangedListener}.
|
||
*/
|
||
private PublishStateCallbackAdapter removePublishStateCallback(
|
||
@NonNull OnPublishStateChangedListener listener) {
|
||
synchronized (mPublishStateCallbacks) {
|
||
return mPublishStateCallbacks.remove(listener);
|
||
}
|
||
}
|
||
|
||
private IImsRcsController getIImsRcsController() {
|
||
IBinder binder = TelephonyFrameworkInitializer
|
||
.getTelephonyServiceManager()
|
||
.getTelephonyImsServiceRegisterer()
|
||
.get();
|
||
return IImsRcsController.Stub.asInterface(binder);
|
||
}
|
||
}
|