diff options
author | James.cf Lin <jamescflin@google.com> | 2021-02-26 10:27:13 +0800 |
---|---|---|
committer | James.cf Lin <jamescflin@google.com> | 2021-03-08 11:14:25 +0800 |
commit | b476f891042f1c470c060e0037ba77345d2c8d4c (patch) | |
tree | f301994fccbefe9e04622ff81171d4756417e494 /telephony/java/android | |
parent | eccbfe4b721a57723a2a6d349fff3508501d8ffb (diff) |
Update the RCS UCE APIs.
Add the RCS APIs and keep the original APIs.
The original APIs will be removed when the applications call the new APIs.
1) document the ImsException on the API requestAvailability
2) Use Collection instead of List for the contacts
3) Use Instant object to represent the timestamp information
Bug: 180126217
Test: atest ImsServiceTest RcsUceAdapterTest
Merged-In: I56a54cd07e8f785d7db8094b9b8a0191d6e28282
Change-Id: I56a54cd07e8f785d7db8094b9b8a0191d6e28282
Diffstat (limited to 'telephony/java/android')
4 files changed, 194 insertions, 10 deletions
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java index 5eb75e762fc9..cd1f4c5e9253 100644 --- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -23,9 +23,14 @@ import android.annotation.SystemApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; +import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -38,6 +43,8 @@ import java.util.List; @SystemApi public final class RcsContactPresenceTuple implements Parcelable { + private static final String LOG_TAG = "RcsContactPresenceTuple"; + /** * The service ID used to indicate that MMTEL service is available. * <p> @@ -353,7 +360,8 @@ public final class RcsContactPresenceTuple implements Parcelable { } /** - * The optional SIP Contact URI associated with the PIDF tuple element. + * The optional SIP Contact URI associated with the PIDF tuple element if the network + * expects the user to use the URI instead of the contact URI to contact it. */ public @NonNull Builder setContactUri(@NonNull Uri contactUri) { mPresenceTuple.mContactUri = contactUri; @@ -364,8 +372,24 @@ public final class RcsContactPresenceTuple implements Parcelable { * The optional timestamp indicating the data and time of the status change of this tuple. * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format * string per RFC3339. + * @hide */ public @NonNull Builder setTimestamp(@NonNull String timestamp) { + try { + mPresenceTuple.mTimestamp = + DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from); + } catch (DateTimeParseException e) { + Log.d(LOG_TAG, "Parse timestamp failed " + e); + } + return this; + } + + /** + * The optional timestamp indicating the data and time of the status change of this tuple. + * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format + * string per RFC3339. + */ + public @NonNull Builder setTime(@NonNull Instant timestamp) { mPresenceTuple.mTimestamp = timestamp; return this; } @@ -397,7 +421,7 @@ public final class RcsContactPresenceTuple implements Parcelable { } private Uri mContactUri; - private String mTimestamp; + private Instant mTimestamp; private @BasicStatus String mStatus; // The service information in the service-description element. @@ -416,7 +440,7 @@ public final class RcsContactPresenceTuple implements Parcelable { private RcsContactPresenceTuple(Parcel in) { mContactUri = in.readParcelable(Uri.class.getClassLoader()); - mTimestamp = in.readString(); + mTimestamp = convertStringFormatTimeToInstant(in.readString()); mStatus = in.readString(); mServiceId = in.readString(); mServiceVersion = in.readString(); @@ -427,7 +451,7 @@ public final class RcsContactPresenceTuple implements Parcelable { @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeParcelable(mContactUri, flags); - out.writeString(mTimestamp); + out.writeString(convertInstantToStringFormat(mTimestamp)); out.writeString(mStatus); out.writeString(mServiceId); out.writeString(mServiceVersion); @@ -453,6 +477,26 @@ public final class RcsContactPresenceTuple implements Parcelable { } }; + // Convert the Instant to the string format + private String convertInstantToStringFormat(Instant instant) { + if (instant == null) { + return ""; + } + return instant.toString(); + } + + // Convert the time string format to Instant + private @Nullable Instant convertStringFormatTimeToInstant(String timestamp) { + if (TextUtils.isEmpty(timestamp)) { + return null; + } + try { + return DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from); + } catch (DateTimeParseException e) { + return null; + } + } + /** @return the status of the tuple element. */ public @NonNull @BasicStatus String getStatus() { return mStatus; @@ -473,8 +517,16 @@ public final class RcsContactPresenceTuple implements Parcelable { return mContactUri; } - /** @return the timestamp element contained in the tuple if it exists */ + /** + * @return the timestamp element contained in the tuple if it exists + * @hide + */ public @Nullable String getTimestamp() { + return (mTimestamp == null) ? null : mTimestamp.toString(); + } + + /** @return the timestamp element contained in the tuple if it exists */ + public @Nullable Instant getTime() { return mTimestamp; } diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index fe855023f5d0..b28dc27e06e0 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -339,6 +339,7 @@ public final class RcsContactUceCapability implements Parcelable { } /** + * Retrieve the contact URI requested by the applications. * @return the URI representing the contact associated with the capabilities. */ public @NonNull Uri getContactUri() { diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index ce8bd7d8c28e..815c08d120c2 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -36,6 +36,8 @@ 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; @@ -430,13 +432,15 @@ public class RcsUceAdapter { /** * The pending request has completed successfully due to all requested contacts information - * being delivered. + * being delivered. The callback {@link #onCapabilitiesReceived(List)} + * for each contacts is required to be called before {@link #onComplete} is called. */ void onComplete(); /** * The pending request has resulted in an error and may need to be retried, depending on the - * error code. + * error code. The callback {@link #onCapabilitiesReceived(List)} + * for each contacts is required to be called before {@link #onError} is called. * @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. @@ -483,7 +487,6 @@ public class RcsUceAdapter { * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ - @SystemApi @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull List<Uri> contactNumbers, @@ -549,6 +552,94 @@ public class RcsUceAdapter { } /** + * Request the User Capability Exchange capabilities for one or more contacts. + * <p> + * This will return the cached capabilities of the contact and will not perform a capability + * poll on the network unless there are contacts being queried with stale information. + * <p> + * Be sure to check the availability of this feature using + * {@link ImsRcsManager#isAvailable(int, int)} and ensuring + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or + * {@link 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. + * @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() { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> c.onComplete()); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + @Override + public void onError(int errorCode, long retryAfterMilliseconds) { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds)); + } 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); + } + } + + /** * Ignore the device cache and perform a capability discovery for one contact, also called * "availability fetch." * <p> @@ -569,6 +660,10 @@ public class RcsUceAdapter { * {@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. * @hide */ @SystemApi diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index 908869beb607..00c91681d9ea 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -31,6 +31,7 @@ import android.util.Pair; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; @@ -241,7 +242,7 @@ public class RcsCapabilityExchangeImplBase { /** * Notify the framework of the response to the SUBSCRIBE request from - * {@link #subscribeForCapabilities(List, SubscribeResponseCallback)}. + * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}. * <p> * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate}, @@ -266,7 +267,7 @@ public class RcsCapabilityExchangeImplBase { /** * Notify the framework of the response to the SUBSCRIBE request from - * {@link #subscribeForCapabilities(List, SubscribeResponseCallback)} that also + * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)} that also * includes a reason provided in the “reason” header. See RFC3326 for more * information. * @@ -388,6 +389,7 @@ public class RcsCapabilityExchangeImplBase { * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE * capabilities for. * @param cb The callback of the subscribe request. + * @hide */ // executor used is defined in the constructor. @SuppressLint("ExecutorRegistration") @@ -403,6 +405,40 @@ public class RcsCapabilityExchangeImplBase { } /** + * The user capabilities of one or multiple contacts have been requested by the framework. + * <p> + * The implementer must follow up this call with an + * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed. + * The response from the network to the SUBSCRIBE request must be sent back to the framework + * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}. + * As NOTIFY requests come in from the network, the requested contact’s capabilities should be + * sent back to the framework using + * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and + * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)} + * should be called with the presence information for the contacts specified. + * <p> + * Once the subscription is terminated, + * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the + * framework to finish listening for NOTIFY responses. + * + * @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the + * UCE capabilities for. + * @param cb The callback of the subscribe request. + */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") + public void subscribeForCapabilities(@NonNull Collection<Uri> uris, + @NonNull SubscribeResponseCallback cb) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation."); + try { + cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED); + } catch (ImsException e) { + // Do not do anything, this is a stub implementation. + } + } + + /** * The capabilities of this device have been updated and should be published to the network. * <p> * If this operation succeeds, network response updates should be sent to the framework using |