diff options
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 |