From 99991f49ee5e179831240809361d21eb3d40ab44 Mon Sep 17 00:00:00 2001 From: "James.cf Lin" Date: Tue, 19 Oct 2021 19:04:18 +0800 Subject: Do not set the device to the forbidden state when receiving the response sip code of the subscribe request is 403 When the framework receives the response code 403 of the SUBSCRIBE request, the device does not need to move to the UCE forbidden state and block all the subsequent PUBLISH/SUBSCRIBE requests. It should trigger the request callback with the error code RcsUceAdapter.ERROR_FORBIDDEN and don't block the subsequent UCE requests. Bug: 202110001 Test: atest RcsUceAdapterTest Change-Id: I974b7bd578aeed803597eb61c627e48f266e9191 --- src/java/com/android/ims/rcs/uce/UceDeviceState.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/java') diff --git a/src/java/com/android/ims/rcs/uce/UceDeviceState.java b/src/java/com/android/ims/rcs/uce/UceDeviceState.java index 773726a6..a122fca2 100644 --- a/src/java/com/android/ims/rcs/uce/UceDeviceState.java +++ b/src/java/com/android/ims/rcs/uce/UceDeviceState.java @@ -238,14 +238,12 @@ public class UceDeviceState { if (requestType == UceController.REQUEST_TYPE_PUBLISH) { // Provisioning error for publish request. setDeviceState(DEVICE_STATE_PROVISION_ERROR); - } else { - setDeviceState(DEVICE_STATE_FORBIDDEN); + updateErrorCode(sipCode, reason, requestType); + // There is no request retry time for SIP code 403 + removeRequestRetryTime(); + // No timer to exit the forbidden state. + removeExitStateTimer(); } - updateErrorCode(sipCode, reason, requestType); - // There is no request retry time for SIP code 403 - removeRequestRetryTime(); - // No timer to exit the forbidden state. - removeExitStateTimer(); break; case NetworkSipCode.SIP_CODE_NOT_FOUND: // sip 404 -- cgit v1.2.3 From a6c414df1be2c1a2f3f1f48475b5c816c03af48f Mon Sep 17 00:00:00 2001 From: hhshin Date: Tue, 14 Sep 2021 08:15:47 +0000 Subject: Create UceStatsWriter for RCS metrics Define interface for metrics data related Uce store Bug: http://b/174871215 Test: build atest UceStatsWriterTest Change-Id: I4ae53a927cf308d58b25fec0eca5efa16f1aca74 Merged-In: I4ae53a927cf308d58b25fec0eca5efa16f1aca74 --- .../com/android/ims/rcs/uce/UceStatsWriter.java | 263 +++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 src/java/com/android/ims/rcs/uce/UceStatsWriter.java (limited to 'src/java') diff --git a/src/java/com/android/ims/rcs/uce/UceStatsWriter.java b/src/java/com/android/ims/rcs/uce/UceStatsWriter.java new file mode 100644 index 00000000..1db9040c --- /dev/null +++ b/src/java/com/android/ims/rcs/uce/UceStatsWriter.java @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2021 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 com.android.ims.rcs.uce; + +import android.annotation.IntDef; +import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.telephony.ims.stub.RcsCapabilityExchangeImplBase; +import android.telephony.ims.RcsContactPresenceTuple; +import android.telephony.ims.RcsContactUceCapability; +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +/** + * The UceStatsWriter should be a singleton class for storing atoms in RcsStats. + * ims-common provides an interface for setting atoms to telephony-common. + **/ +public class UceStatsWriter { + private static UceStatsWriter sInstance = null; + private UceStatsCallback mCallBack; + + /** + * @hide + */ + // Defines which UCE event occurred. + @IntDef(value = { + PUBLISH_EVENT, + SUBSCRIBE_EVENT, + INCOMING_OPTION_EVENT, + OUTGOING_OPTION_EVENT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UceEventType {} + /** + * UCE events related to Publish Method. + */ + public static final int PUBLISH_EVENT = 0; + /** + * UCE events related to Subscribe Method. + */ + public static final int SUBSCRIBE_EVENT = 1; + /** + * UCE events related to incoming Options Method. + */ + public static final int INCOMING_OPTION_EVENT = 2; + /** + * UCE events related to outgoing Options Method. + */ + public static final int OUTGOING_OPTION_EVENT = 3; + + /** + * The callback interface is called by the Metrics data creator to receive information from + * others controllers. + */ + public interface UceStatsCallback { + /** + * Notify the callback listener that the feature tag associated with + * IMS registration of this device have changed. + */ + public void onImsRegistrationFeatureTagStats(int subId, List featureTagList, + int registrationTech); + + /** + * Notify that the active IMS registration to the carrier network has been terminated. + */ + public void onStoreCompleteImsRegistrationFeatureTagStats(int subId); + + /** + * Notify the callback listener that the PIDF ServiceDescriptions associated with + * the UCE presence of this device have changed. + */ + void onImsRegistrationServiceDescStats(int subId, List serviceIdList, + List serviceIdVersionList, int registrationTech); + + /** + * Notify the callback listener that a subscribe response received. + */ + void onSubscribeResponse(int subId, long taskId, int networkResponse); + + /** + * Notify the callback listener that a UCE Network Event has occurred. + */ + void onUceEvent(int subId, int type, boolean successful, int commandCode, + int networkResponse); + + /** + * Notify the callback listener that a subscribe has ended. + */ + void onSubscribeTerminated(int subId, long taskId, String reason); + + /** + * Notify that the Presence Notify Event has changed. + */ + void onPresenceNotifyEvent(int subId, long taskId, + List updatedCapList); + + /** + * Notify that the active UCE PUBLISH to the carrier network has been terminated. + */ + void onStoreCompleteImsRegistrationServiceDescStats(int subId); + } + + /** + * create an instance of UceStatsWriter + */ + public static UceStatsWriter init(UceStatsCallback callback) { + synchronized (UceStatsWriter.class) { + if (sInstance == null) { + sInstance = new UceStatsWriter(callback); + } + return sInstance; + } + } + + /** + * get the current instance of UceStatsWriter + */ + public static UceStatsWriter getInstance() { + synchronized (UceStatsWriter.class) { + return sInstance; + } + } + + /** + * Stats about each Feature tag that was included in IMS registration received from + * the network during register. + * @param subId The subId associated with the event. + * @param featureTagList Ims Feature tag list. + * @param registrationTech The registration tech associated with the feature tag. + */ + public void setImsRegistrationFeatureTagStats(int subId, List featureTagList, + @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) { + if (mCallBack == null) { + return; + } + mCallBack.onImsRegistrationFeatureTagStats(subId, featureTagList, registrationTech); + } + + /** + * Update time of stats for each stored feature tag. + * @param subId The subId associated with the event. + */ + public void setStoreCompleteImsRegistrationFeatureTagStats(int subId) { + if (mCallBack == null) { + return; + } + mCallBack.onStoreCompleteImsRegistrationFeatureTagStats(subId); + } + + /** + * Stats about each ServiceDescription that was included in the PIDF XML sent to + * the network during publish + * @param subId The subId associated with the event. + * @param tupleList Tuple information set in PIDF. + * @param registrationTech The registration tech associated with the feature tag. + */ + public void setImsRegistrationServiceDescStats(int subId, + List tupleList, + @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) { + if (mCallBack == null) { + return; + } + ArrayList svcId = new ArrayList<>(); + ArrayList svcVersion = new ArrayList<>(); + + for (RcsContactPresenceTuple tuple : tupleList) { + svcId.add(tuple.getServiceId()); + svcVersion.add(tuple.getServiceVersion()); + } + mCallBack.onImsRegistrationServiceDescStats(subId, svcId, svcVersion, registrationTech); + } + + /** + * Stats related to UCE queries to the network + * @param subId The subId associated with the event. + * @param taskId The taskId associate with the event. + * @param networkResponse The network response code for the Uce event + */ + public void setSubscribeResponse(int subId, long taskId, int networkResponse) { + if (mCallBack != null) { + mCallBack.onSubscribeResponse(subId, taskId, networkResponse); + } + } + + /** + * Stats related to UCE queries to the network + * @param subId The subId associated with the event. + * @param type Used to identify the message type. + * @param successful Whether the UCE event is successfully finished. + * @param commandCode The command error code for the Uce event + * @param networkResponse The network response code for the Uce event + */ + public void setUceEvent(int subId, @UceEventType int type, boolean successful, + @RcsCapabilityExchangeImplBase.CommandCode int commandCode, int networkResponse) { + if (mCallBack != null) { + mCallBack.onUceEvent(subId, type, successful, commandCode, networkResponse); + } + } + + /** + * The result of processing received notify messages. + * @param subId The subId associated with the event. + * @param taskId The taskId associate with the event. + * @param updatedCapList Capability information of the user contained in Presence Notify. + */ + public void setPresenceNotifyEvent(int subId, long taskId, + List updatedCapList) { + if (mCallBack == null || updatedCapList == null || updatedCapList.isEmpty()) { + return; + } + mCallBack.onPresenceNotifyEvent(subId, taskId, updatedCapList); + } + + /** + * Indicates that the subscription request has become a terminated state. + * @param subId The subId associated with the event. + * @param taskId The taskId associate with the event. + * @param reason The terminated reason associated with the subscription state. + */ + public void setSubscribeTerminated(int subId, long taskId, String reason) { + if (mCallBack != null) { + mCallBack.onSubscribeTerminated(subId, taskId, reason); + } + } + + /** + * indicates that the device has removed an existing PUBLISH from the carrier's network + * containing the device's RCS capabilities state. + * The registered time of publication must be set in ImsRegistrationServiceDescStats, + * which is the life time of publication, so it can be set only when publication is over. + * @param subId The subId associated with the event. + */ + public void setUnPublish(int subId) { + if (mCallBack != null) { + mCallBack.onStoreCompleteImsRegistrationServiceDescStats(subId); + } + } + + @VisibleForTesting + protected UceStatsWriter(UceStatsCallback callback) { + mCallBack = callback; + } +} -- cgit v1.2.3 From 14496949acd14fc9be765fad62acabe215cdc62a Mon Sep 17 00:00:00 2001 From: hhshin Date: Thu, 4 Nov 2021 00:53:46 +0000 Subject: [UCE] Reset the retry count and times related publish when the publish status changed to not_published we do not reset the PUBLISH throttling timer when CapabilityExchangeEventListener#onUnpublish is called and PDN is torn down. This means that PUBLISH is delayed up to a minute when IMS PDN comes back up. Instead, we should be resetting this timer when the onUnpublish event is received Bug: http://b/204198074 Test: atest PublishControllerImplTest Change-Id: I3a01bf33921922fcc440fc5d8f4efb3d99916b28 Merged-In: I3a01bf33921922fcc440fc5d8f4efb3d99916b28 --- .../presence/publish/PublishControllerImpl.java | 36 ++++++++++++++++++++-- .../rcs/uce/presence/publish/PublishProcessor.java | 6 ++++ .../presence/publish/PublishProcessorState.java | 10 ++++++ 3 files changed, 50 insertions(+), 2 deletions(-) (limited to 'src/java') diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java b/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java index e2340ff5..83733e3a 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java @@ -330,8 +330,7 @@ public class PublishControllerImpl implements PublishController { public void onUnpublish() { logd("onUnpublish"); if (mIsDestroyedFlag) return; - mPublishHandler.sendPublishStateChangedMessage(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED, - Instant.now(), null /*pidfXml*/); + mPublishHandler.sendUnpublishedMessage(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED); } @Override @@ -416,6 +415,7 @@ public class PublishControllerImpl implements PublishController { private static final int MSG_REQUEST_NETWORK_RESPONSE = 10; private static final int MSG_REQUEST_CANCELED = 11; private static final int MSG_RESET_DEVICE_STATE = 12; + private static final int MSG_UNPUBLISHED = 13; private final WeakReference mPublishControllerRef; @@ -496,6 +496,14 @@ public class PublishControllerImpl implements PublishController { publishCtrl.handleResetDeviceStateMessage(); break; + case MSG_UNPUBLISHED: { + SomeArgs args = (SomeArgs) message.obj; + int newPublishState = (Integer) args.arg1; + Instant updatedTimestamp = (Instant) args.arg2; + args.recycle(); + publishCtrl.handleUnpublishedMessage(newPublishState, updatedTimestamp); + break; + } default: publishCtrl.logd("invalid message: " + message.what); break; @@ -583,6 +591,22 @@ public class PublishControllerImpl implements PublishController { sendMessage(message); } + /** + * Send the message to notify the publish state is changed. + */ + public void sendUnpublishedMessage(@PublishState int publishState) { + PublishControllerImpl publishCtrl = mPublishControllerRef.get(); + if (publishCtrl == null) return; + if (publishCtrl.mIsDestroyedFlag) return; + + SomeArgs args = SomeArgs.obtain(); + args.arg1 = publishState; + args.arg2 = Instant.now(); + Message message = obtainMessage(); + message.what = MSG_UNPUBLISHED; + message.obj = args; + sendMessage(message); + } /** * Send the message to notify the new added callback of the latest publish state. */ @@ -703,6 +727,7 @@ public class PublishControllerImpl implements PublishController { EVENT_DESCRIPTION.put(MSG_REQUEST_NETWORK_RESPONSE, "REQUEST_NETWORK_RESPONSE"); EVENT_DESCRIPTION.put(MSG_REQUEST_CANCELED, "REQUEST_CANCELED"); EVENT_DESCRIPTION.put(MSG_RESET_DEVICE_STATE, "RESET_DEVICE_STATE"); + EVENT_DESCRIPTION.put(MSG_UNPUBLISHED, "MSG_UNPUBLISHED"); } } @@ -988,6 +1013,13 @@ public class PublishControllerImpl implements PublishController { mUceCtrlCallback.resetDeviceState(); } + private void handleUnpublishedMessage(@PublishState int newPublishState, + Instant updatedTimestamp) { + if (mIsDestroyedFlag) return; + mPublishProcessor.resetState(); + handlePublishStateChangedMessage(newPublishState, updatedTimestamp, null); + } + @VisibleForTesting public void setCapabilityType(int type) { mCapabilityType = type; diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java b/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java index 68aeaa8f..86d21366 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java @@ -445,6 +445,12 @@ public class PublishProcessor { return mProcessorState.isPublishingNow(); } + /** + * Reset the retry count and time related publish. + */ + public void resetState() { + mProcessorState.resetState(); + } @VisibleForTesting public void setProcessorState(PublishProcessorState processorState) { mProcessorState = processorState; diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessorState.java b/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessorState.java index 40d901f6..0e9adfad 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessorState.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessorState.java @@ -348,6 +348,16 @@ public class PublishProcessorState { } } + /** + * Reset the retry count and related time when the publication status has + * changed to not_published. + */ + public void resetState() { + synchronized (mLock) { + mPublishThrottle.resetState(); + } + } + /* * Check if it has reached the maximum retry count. */ -- cgit v1.2.3 From be5550dc2f7afb5aa732f1cf8238890924ed873e Mon Sep 17 00:00:00 2001 From: hhshin Date: Fri, 17 Sep 2021 18:08:15 +0000 Subject: Add metrics for UCE event about Publish, Subscribe, Options and metrics for RCS registration features Bug: http://b/174871215 Test: atest PublishControllerImplTest, PublishProcessorTest, OptionsCoordinatorTest, RemoteOptionsCoordinatorTest, SubscribeCoordinator for UCE event metrics atest DeviceCapabilityListenerTest for RCS registration features Change-Id: I738f05ac69d8a6c47543025d71194238bd32c578 Merged-In: I738f05ac69d8a6c47543025d71194238bd32c578 --- .../presence/publish/DeviceCapabilityListener.java | 18 ++++++++++- .../presence/publish/PublishControllerImpl.java | 19 ++++++++---- .../rcs/uce/presence/publish/PublishProcessor.java | 35 ++++++++++++++++++++++ .../rcs/uce/request/OptionsRequestCoordinator.java | 25 ++++++++++++++-- .../rcs/uce/request/RemoteOptionsCoordinator.java | 20 +++++++++++-- .../uce/request/SubscribeRequestCoordinator.java | 25 ++++++++++++++-- 6 files changed, 130 insertions(+), 12 deletions(-) (limited to 'src/java') diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListener.java b/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListener.java index e881ae0c..872d35dc 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListener.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/DeviceCapabilityListener.java @@ -46,6 +46,7 @@ import android.util.IndentingPrintWriter; import android.util.LocalLog; import android.util.Log; +import com.android.ims.rcs.uce.UceStatsWriter; import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControllerCallback; import com.android.ims.rcs.uce.presence.publish.PublishController.PublishTriggerType; import com.android.ims.rcs.uce.util.UceUtils; @@ -53,7 +54,12 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.util.HandlerExecutor; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; +import java.util.Set; /** * Listen to the device changes and notify the PublishController to publish the device's @@ -65,6 +71,8 @@ public class DeviceCapabilityListener { private static final long REGISTER_IMS_CHANGED_DELAY = 15000L; // 15 seconds + private final UceStatsWriter mUceStatsWriter; + /** * Used to inject ImsMmTelManager instances for testing. */ @@ -180,7 +188,7 @@ public class DeviceCapabilityListener { private final Object mLock = new Object(); public DeviceCapabilityListener(Context context, int subId, DeviceCapabilityInfo info, - PublishControllerCallback callback) { + PublishControllerCallback callback, UceStatsWriter uceStatsWriter) { mSubId = subId; logi("create"); @@ -188,6 +196,7 @@ public class DeviceCapabilityListener { mCallback = callback; mCapabilityInfo = info; mInitialized = false; + mUceStatsWriter = uceStatsWriter; mHandlerThread = new HandlerThread("DeviceCapListenerThread"); mHandlerThread.start(); @@ -447,6 +456,12 @@ public class DeviceCapabilityListener { synchronized (mLock) { logi("onRcsRegistered: " + attributes); if (!mIsImsCallbackRegistered) return; + + List featureTagList = new ArrayList<>(attributes.getFeatureTags()); + int registrationTech = attributes.getRegistrationTechnology(); + + mUceStatsWriter.setImsRegistrationFeatureTagStats( + mSubId, featureTagList, registrationTech); handleImsRcsRegistered(attributes); } } @@ -456,6 +471,7 @@ public class DeviceCapabilityListener { synchronized (mLock) { logi("onRcsUnregistered: " + info); if (!mIsImsCallbackRegistered) return; + mUceStatsWriter.setStoreCompleteImsRegistrationFeatureTagStats(mSubId); handleImsRcsUnregistered(); } } diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java b/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java index 83733e3a..0ff48856 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/PublishControllerImpl.java @@ -44,6 +44,7 @@ import com.android.ims.rcs.uce.UceController; import com.android.ims.rcs.uce.UceController.UceControllerCallback; import com.android.ims.rcs.uce.UceDeviceState; import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult; +import com.android.ims.rcs.uce.UceStatsWriter; import com.android.ims.rcs.uce.util.UceUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; @@ -79,7 +80,8 @@ public class PublishControllerImpl implements PublishController { @VisibleForTesting public interface DeviceCapListenerFactory { DeviceCapabilityListener createDeviceCapListener(Context context, int subId, - DeviceCapabilityInfo capInfo, PublishControllerCallback callback); + DeviceCapabilityInfo capInfo, PublishControllerCallback callback, + UceStatsWriter uceStatsWriter); } private final int mSubId; @@ -90,6 +92,7 @@ public class PublishControllerImpl implements PublishController { private volatile boolean mReceivePublishFromService; private volatile RcsFeatureManager mRcsFeatureManager; private final UceControllerCallback mUceCtrlCallback; + private final UceStatsWriter mUceStatsWriter; // The capability type that the device is using. private @RcsImsCapabilityFlag int mCapabilityType; @@ -115,8 +118,9 @@ public class PublishControllerImpl implements PublishController { // The listener to listen to the device's capabilities changed. private DeviceCapabilityListener mDeviceCapListener; - private DeviceCapListenerFactory mDeviceCapListenerFactory = (context, subId, capInfo, callback) - -> new DeviceCapabilityListener(context, subId, capInfo, callback); + private DeviceCapListenerFactory mDeviceCapListenerFactory = (context, subId, capInfo, callback, + uceStatsWriter) + -> new DeviceCapabilityListener(context, subId, capInfo, callback, uceStatsWriter); // Listen to the RCS availability status changed. private final IImsCapabilityCallback mRcsCapabilitiesCallback = @@ -141,6 +145,7 @@ public class PublishControllerImpl implements PublishController { mSubId = subId; mContext = context; mUceCtrlCallback = callback; + mUceStatsWriter = UceStatsWriter.getInstance(); logi("create"); initPublishController(looper); } @@ -148,12 +153,13 @@ public class PublishControllerImpl implements PublishController { @VisibleForTesting public PublishControllerImpl(Context context, int subId, UceControllerCallback c, Looper looper, DeviceCapListenerFactory deviceCapFactory, - PublishProcessorFactory processorFactory) { + PublishProcessorFactory processorFactory, UceStatsWriter instance) { mSubId = subId; mContext = context; mUceCtrlCallback = c; mDeviceCapListenerFactory = deviceCapFactory; mPublishProcessorFactory = processorFactory; + mUceStatsWriter = instance; initPublishController(looper); } @@ -200,7 +206,7 @@ public class PublishControllerImpl implements PublishController { private void initDeviceCapabilitiesListener() { mDeviceCapListener = mDeviceCapListenerFactory.createDeviceCapListener(mContext, mSubId, - mDeviceCapabilityInfo, mPublishControllerCallback); + mDeviceCapabilityInfo, mPublishControllerCallback, mUceStatsWriter); } @Override @@ -934,6 +940,9 @@ public class PublishControllerImpl implements PublishController { if (mPublishState == newPublishState) return; mPublishState = newPublishState; } + if (newPublishState == RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED) { + mUceStatsWriter.setUnPublish(mSubId); + } // Trigger the publish state changed in handler thread since it may take time. logd("Notify publish state changed: " + mPublishState); diff --git a/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java b/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java index 86d21366..01aa22b9 100644 --- a/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java +++ b/src/java/com/android/ims/rcs/uce/presence/publish/PublishProcessor.java @@ -22,6 +22,8 @@ import android.annotation.NonNull; import android.content.Context; import android.os.RemoteException; import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.telephony.ims.stub.RcsCapabilityExchangeImplBase; import android.text.TextUtils; import android.util.IndentingPrintWriter; import android.util.LocalLog; @@ -31,6 +33,7 @@ import com.android.ims.RcsFeatureManager; import com.android.ims.rcs.uce.presence.pidfparser.PidfParser; import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControllerCallback; import com.android.ims.rcs.uce.presence.publish.PublishController.PublishTriggerType; +import com.android.ims.rcs.uce.UceStatsWriter; import com.android.ims.rcs.uce.util.UceUtils; import com.android.internal.annotations.VisibleForTesting; @@ -53,6 +56,8 @@ public class PublishProcessor { private volatile boolean mIsDestroyed; private volatile RcsFeatureManager mRcsFeatureManager; + private final UceStatsWriter mUceStatsWriter; + // Manage the state of the publish processor. private PublishProcessorState mProcessorState; @@ -74,6 +79,18 @@ public class PublishProcessor { mDeviceCapabilities = capabilityInfo; mPublishCtrlCallback = publishCtrlCallback; mProcessorState = new PublishProcessorState(subId); + mUceStatsWriter = UceStatsWriter.getInstance(); + } + + @VisibleForTesting + public PublishProcessor(Context context, int subId, DeviceCapabilityInfo capabilityInfo, + PublishControllerCallback publishCtrlCallback, UceStatsWriter instance) { + mSubId = subId; + mContext = context; + mDeviceCapabilities = capabilityInfo; + mPublishCtrlCallback = publishCtrlCallback; + mProcessorState = new PublishProcessorState(subId); + mUceStatsWriter = instance; } /** @@ -158,6 +175,13 @@ public class PublishProcessor { return false; } + featureManager.getImsRegistrationTech((tech) -> { + int registrationTech = (tech == null) + ? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : tech; + mUceStatsWriter.setImsRegistrationServiceDescStats(mSubId, + deviceCapability.getCapabilityTuples(), registrationTech); + }); + // Publish to the Presence server. return publishCapabilities(featureManager, pidfXml); } @@ -244,6 +268,13 @@ public class PublishProcessor { mLocalLog.log("Receive command error code=" + requestResponse.getCmdErrorCode()); logd("onCommandError: " + requestResponse.toString()); + int cmdError = requestResponse.getCmdErrorCode().orElse(0); + boolean successful = false; + if (cmdError == RcsCapabilityExchangeImplBase.COMMAND_CODE_NO_CHANGE) { + successful = true; + } + mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.PUBLISH_EVENT, successful, cmdError, 0); + if (requestResponse.needRetry() && !mProcessorState.isReachMaximumRetries()) { handleRequestRespWithRetry(requestResponse); } else { @@ -267,6 +298,10 @@ public class PublishProcessor { mLocalLog.log("Receive network response code=" + requestResponse.getNetworkRespSipCode()); logd("onNetworkResponse: " + requestResponse.toString()); + int responseCode = requestResponse.getNetworkRespSipCode().orElse(0); + mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.PUBLISH_EVENT, true, 0, + responseCode); + if (requestResponse.needRetry() && !mProcessorState.isReachMaximumRetries()) { handleRequestRespWithRetry(requestResponse); } else { diff --git a/src/java/com/android/ims/rcs/uce/request/OptionsRequestCoordinator.java b/src/java/com/android/ims/rcs/uce/request/OptionsRequestCoordinator.java index a150dd6d..a2660931 100644 --- a/src/java/com/android/ims/rcs/uce/request/OptionsRequestCoordinator.java +++ b/src/java/com/android/ims/rcs/uce/request/OptionsRequestCoordinator.java @@ -24,6 +24,7 @@ import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.aidl.IRcsUceControllerCallback; import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; +import com.android.ims.rcs.uce.UceStatsWriter; import com.android.internal.annotations.VisibleForTesting; import java.util.Collection; @@ -44,7 +45,14 @@ public class OptionsRequestCoordinator extends UceRequestCoordinator { public Builder(int subId, Collection requests, RequestManagerCallback callback) { - mRequestCoordinator = new OptionsRequestCoordinator(subId, requests, callback); + mRequestCoordinator = new OptionsRequestCoordinator(subId, requests, callback, + UceStatsWriter.getInstance()); + } + @VisibleForTesting + public Builder(int subId, Collection requests, + RequestManagerCallback callback, UceStatsWriter instance) { + mRequestCoordinator = new OptionsRequestCoordinator(subId, requests, callback, + instance); } public Builder setCapabilitiesCallback(IRcsUceControllerCallback callback) { @@ -105,9 +113,12 @@ public class OptionsRequestCoordinator extends UceRequestCoordinator { // The callback to notify the result of the capabilities request. private IRcsUceControllerCallback mCapabilitiesCallback; + private final UceStatsWriter mUceStatsWriter; + private OptionsRequestCoordinator(int subId, Collection requests, - RequestManagerCallback requestMgrCallback) { + RequestManagerCallback requestMgrCallback, UceStatsWriter instance) { super(subId, requests, requestMgrCallback); + mUceStatsWriter = instance; logd("OptionsRequestCoordinator: created"); } @@ -189,6 +200,11 @@ public class OptionsRequestCoordinator extends UceRequestCoordinator { // Finish this request. request.onFinish(); + int commandErrorCode = response.getCommandError().orElse(0); + mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.OUTGOING_OPTION_EVENT, + false, commandErrorCode, 0); + + // Remove this request from the activated collection and notify RequestManager. Long taskId = request.getTaskId(); RequestResult requestResult = sCommandErrorCreator.createRequestResult(taskId, response); @@ -203,6 +219,11 @@ public class OptionsRequestCoordinator extends UceRequestCoordinator { CapabilityRequestResponse response = request.getRequestResponse(); logd("handleNetworkResponse: " + response.toString()); + int responseCode = response.getNetworkRespSipCode().orElse(0); + mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.OUTGOING_OPTION_EVENT, true, + 0, responseCode); + + List updatedCapList = response.getUpdatedContactCapability(); if (!updatedCapList.isEmpty()) { // Save the capabilities and trigger the capabilities callback diff --git a/src/java/com/android/ims/rcs/uce/request/RemoteOptionsCoordinator.java b/src/java/com/android/ims/rcs/uce/request/RemoteOptionsCoordinator.java index c8aa3f77..5a3e33bb 100644 --- a/src/java/com/android/ims/rcs/uce/request/RemoteOptionsCoordinator.java +++ b/src/java/com/android/ims/rcs/uce/request/RemoteOptionsCoordinator.java @@ -25,6 +25,7 @@ import android.telephony.ims.aidl.IOptionsRequestCallback; import com.android.ims.rcs.uce.request.RemoteOptionsRequest.RemoteOptResponse; import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; +import com.android.ims.rcs.uce.UceStatsWriter; import com.android.internal.annotations.VisibleForTesting; import java.util.Collection; @@ -41,7 +42,13 @@ public class RemoteOptionsCoordinator extends UceRequestCoordinator { RemoteOptionsCoordinator mRemoteOptionsCoordinator; public Builder(int subId, Collection requests, RequestManagerCallback c) { - mRemoteOptionsCoordinator = new RemoteOptionsCoordinator(subId, requests, c); + mRemoteOptionsCoordinator = new RemoteOptionsCoordinator(subId, requests, c, + UceStatsWriter.getInstance()); + } + @VisibleForTesting + public Builder(int subId, Collection requests, RequestManagerCallback c, + UceStatsWriter instance) { + mRemoteOptionsCoordinator = new RemoteOptionsCoordinator(subId, requests, c, instance); } public Builder setOptionsRequestCallback(IOptionsRequestCallback callback) { @@ -78,9 +85,12 @@ public class RemoteOptionsCoordinator extends UceRequestCoordinator { // The callback to notify the result of the remote options request. private IOptionsRequestCallback mOptionsReqCallback; + private final UceStatsWriter mUceStatsWriter; + private RemoteOptionsCoordinator(int subId, Collection requests, - RequestManagerCallback requestMgrCallback) { + RequestManagerCallback requestMgrCallback, UceStatsWriter instance) { super(subId, requests, requestMgrCallback); + mUceStatsWriter = instance; logd("RemoteOptionsCoordinator: created"); } @@ -144,6 +154,9 @@ public class RemoteOptionsCoordinator extends UceRequestCoordinator { boolean isRemoteNumberBlocked) { try { logd("triggerOptionsReqCallback: start"); + mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.INCOMING_OPTION_EVENT, true, 0, + 200); + mOptionsReqCallback.respondToCapabilityRequest(deviceCaps, isRemoteNumberBlocked); } catch (RemoteException e) { logw("triggerOptionsReqCallback exception: " + e); @@ -155,6 +168,9 @@ public class RemoteOptionsCoordinator extends UceRequestCoordinator { private void triggerOptionsReqWithErrorCallback(int errorCode, String reason) { try { logd("triggerOptionsReqWithErrorCallback: start"); + mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.INCOMING_OPTION_EVENT, true, 0, + errorCode); + mOptionsReqCallback.respondToCapabilityRequestWithError(errorCode, reason); } catch (RemoteException e) { logw("triggerOptionsReqWithErrorCallback exception: " + e); diff --git a/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java b/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java index ee6bd356..8913340b 100644 --- a/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java +++ b/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java @@ -28,6 +28,7 @@ import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult; import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils; import com.android.ims.rcs.uce.request.SubscriptionTerminatedHelper.TerminatedResult; import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; +import com.android.ims.rcs.uce.UceStatsWriter; import com.android.internal.annotations.VisibleForTesting; import java.util.Collection; @@ -51,7 +52,13 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { * The builder of the SubscribeRequestCoordinator class. */ public Builder(int subId, Collection requests, RequestManagerCallback c) { - mRequestCoordinator = new SubscribeRequestCoordinator(subId, requests, c); + mRequestCoordinator = new SubscribeRequestCoordinator(subId, requests, c, + UceStatsWriter.getInstance()); + } + @VisibleForTesting + public Builder(int subId, Collection requests, RequestManagerCallback c, + UceStatsWriter instance) { + mRequestCoordinator = new SubscribeRequestCoordinator(subId, requests, c, instance); } /** @@ -152,9 +159,12 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { // The callback to notify the result of the capabilities request. private volatile IRcsUceControllerCallback mCapabilitiesCallback; + private final UceStatsWriter mUceStatsWriter; + private SubscribeRequestCoordinator(int subId, Collection requests, - RequestManagerCallback requestMgrCallback) { + RequestManagerCallback requestMgrCallback, UceStatsWriter instance) { super(subId, requests, requestMgrCallback); + mUceStatsWriter = instance; logd("SubscribeRequestCoordinator: created"); } @@ -246,6 +256,10 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { // Finish this request. request.onFinish(); + int commandErrorCode = response.getCommandError().orElse(0); + mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.SUBSCRIBE_EVENT, + false, commandErrorCode, 0); + // Remove this request from the activated collection and notify RequestManager. Long taskId = request.getTaskId(); RequestResult requestResult = sCommandErrorCreator.createRequestResult(taskId, response, @@ -261,6 +275,9 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { CapabilityRequestResponse response = request.getRequestResponse(); logd("handleNetworkResponse: " + response.toString()); + int respCode = response.getNetworkRespSipCode().orElse(0); + mUceStatsWriter.setSubscribeResponse(mSubId, request.getTaskId(), respCode); + // Refresh the device state with the request result. response.getResponseSipCode().ifPresent(sipCode -> { String reason = response.getResponseReason().orElse(""); @@ -333,6 +350,7 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { return; } + mUceStatsWriter.setPresenceNotifyEvent(mSubId, taskId, updatedCapList); // Save the updated capabilities to the cache. mRequestManagerCallback.saveCapabilities(updatedCapList); @@ -356,6 +374,8 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { return; } + mUceStatsWriter.setPresenceNotifyEvent(mSubId, taskId, terminatedResources); + // Save the terminated capabilities to the cache. mRequestManagerCallback.saveCapabilities(terminatedResources); @@ -396,6 +416,7 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { // Remove this request from the activated collection and notify RequestManager. Long taskId = request.getTaskId(); + mUceStatsWriter.setSubscribeTerminated(mSubId, taskId, response.getTerminatedReason()); RequestResult requestResult = sTerminatedCreator.createRequestResult(taskId, response, mRequestManagerCallback); moveRequestToFinishedCollection(taskId, requestResult); -- cgit v1.2.3 From b6cf253315e975af81f9f874943dd1abf9dd1430 Mon Sep 17 00:00:00 2001 From: "James.cf Lin" Date: Thu, 11 Nov 2021 08:44:54 +0800 Subject: Use expired cache infomation instead if the result of the subscription request inconclusive When the cnontact capability request result is unsuccessful and not a 404 error, the framework should use cached information even if the capabilities have expired. If the capability doesn't exist, use non-RCS result instead. Bug: 204427048 Test: atest RcsUceAdapterTest --iterations 3 Change-Id: I90ca67b682807db4501bc36b3658a23f85912bb5 Merged-In: I90ca67b682807db4501bc36b3658a23f85912bb5 --- .../com/android/ims/rcs/uce/UceController.java | 23 ++++ .../com/android/ims/rcs/uce/eab/EabController.java | 12 +++ .../android/ims/rcs/uce/eab/EabControllerImpl.java | 87 +++++++++++++++ .../ims/rcs/uce/request/CapabilityRequest.java | 105 ++++++++++++++++-- .../ims/rcs/uce/request/ContactThrottlingList.java | 119 +++++++++++++++++++++ .../uce/request/SubscribeRequestCoordinator.java | 81 ++++++++++++-- .../ims/rcs/uce/request/UceRequestManager.java | 54 ++++++++++ .../com/android/ims/rcs/uce/util/UceUtils.java | 25 +++++ 8 files changed, 489 insertions(+), 17 deletions(-) create mode 100644 src/java/com/android/ims/rcs/uce/request/ContactThrottlingList.java (limited to 'src/java') diff --git a/src/java/com/android/ims/rcs/uce/UceController.java b/src/java/com/android/ims/rcs/uce/UceController.java index c6099097..70677907 100644 --- a/src/java/com/android/ims/rcs/uce/UceController.java +++ b/src/java/com/android/ims/rcs/uce/UceController.java @@ -79,11 +79,23 @@ public class UceController { */ List getCapabilitiesFromCache(@NonNull List uris); + /** + * Retrieve the capabilities associated with the given uris from the cache including + * expired capabilities. + */ + List getCapabilitiesFromCacheIncludingExpired(@NonNull List uris); + /** * Retrieve the contact's capabilities from the availability cache. */ EabCapabilityResult getAvailabilityFromCache(@NonNull Uri uri); + /** + * Retrieve the contact's capabilities from the availability cache including expired + * capabilities + */ + EabCapabilityResult getAvailabilityFromCacheIncludingExpired(@NonNull Uri uri); + /** * Store the given capabilities to the cache. */ @@ -475,11 +487,21 @@ public class UceController { return mEabController.getCapabilities(uris); } + @Override + public List getCapabilitiesFromCacheIncludingExpired(List uris) { + return mEabController.getCapabilitiesIncludingExpired(uris); + } + @Override public EabCapabilityResult getAvailabilityFromCache(Uri contactUri) { return mEabController.getAvailability(contactUri); } + @Override + public EabCapabilityResult getAvailabilityFromCacheIncludingExpired(Uri contactUri) { + return mEabController.getAvailabilityIncludingExpired(contactUri); + } + @Override public void saveCapabilities(List contactCapabilities) { mEabController.saveCapabilities(contactCapabilities); @@ -755,6 +777,7 @@ public class UceController { public void removeRequestDisallowedStatus() { logd("removeRequestDisallowedStatus"); mDeviceState.resetDeviceState(); + mRequestManager.resetThrottlingList(); } /** diff --git a/src/java/com/android/ims/rcs/uce/eab/EabController.java b/src/java/com/android/ims/rcs/uce/eab/EabController.java index 903a19df..b03e4659 100644 --- a/src/java/com/android/ims/rcs/uce/eab/EabController.java +++ b/src/java/com/android/ims/rcs/uce/eab/EabController.java @@ -34,11 +34,23 @@ public interface EabController extends ControllerBase { */ @NonNull List getCapabilities(@NonNull List uris); + /** + * Get contact capabilities from cache including expired capabilities. + * @param uris the uri list to get contact capabilities from cache. + * @return The contact capabilities of the given uri list. + */ + @NonNull List getCapabilitiesIncludingExpired(@NonNull List uris); + /** * Retrieve the contact's capabilities from the availability cache. */ @NonNull EabCapabilityResult getAvailability(@NonNull Uri contactUri); + /** + * Retrieve the contact's capabilities from the availability cache. + */ + @NonNull EabCapabilityResult getAvailabilityIncludingExpired(@NonNull Uri contactUri); + /** * Save the capabilities to the EAB database. */ diff --git a/src/java/com/android/ims/rcs/uce/eab/EabControllerImpl.java b/src/java/com/android/ims/rcs/uce/eab/EabControllerImpl.java index cc1011fc..388155a5 100644 --- a/src/java/com/android/ims/rcs/uce/eab/EabControllerImpl.java +++ b/src/java/com/android/ims/rcs/uce/eab/EabControllerImpl.java @@ -56,6 +56,7 @@ import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.function.Predicate; /** @@ -162,6 +163,29 @@ public class EabControllerImpl implements EabController { return capabilityResultList; } + /** + * Retrieve the contacts' capabilities from the EAB database including expired capabilities. + */ + @Override + public @NonNull List getCapabilitiesIncludingExpired( + @NonNull List uris) { + Objects.requireNonNull(uris); + if (mIsSetDestroyedFlag) { + Log.d(TAG, "EabController destroyed."); + return generateDestroyedResult(uris); + } + + Log.d(TAG, "getCapabilitiesIncludingExpired uri size=" + uris.size()); + List capabilityResultList = new ArrayList(); + + for (Uri uri : uris) { + EabCapabilityResult result = generateEabResultIncludingExpired(uri, + this::isCapabilityExpired); + capabilityResultList.add(result); + } + return capabilityResultList; + } + /** * Retrieve the contact's capabilities from the availability cache. */ @@ -178,6 +202,23 @@ public class EabControllerImpl implements EabController { return generateEabResult(contactUri, this::isAvailabilityExpired); } + /** + * Retrieve the contact's capabilities from the availability cache including expired + * capabilities. + */ + @Override + public @NonNull EabCapabilityResult getAvailabilityIncludingExpired(@NonNull Uri contactUri) { + Objects.requireNonNull(contactUri); + if (mIsSetDestroyedFlag) { + Log.d(TAG, "EabController destroyed."); + return new EabCapabilityResult( + contactUri, + EabCapabilityResult.EAB_CONTROLLER_DESTROYED_FAILURE, + null); + } + return generateEabResultIncludingExpired(contactUri, this::isAvailabilityExpired); + } + /** * Update the availability catch and save the capabilities to the EAB database. */ @@ -296,6 +337,52 @@ public class EabControllerImpl implements EabController { return result; } + private EabCapabilityResult generateEabResultIncludingExpired(Uri contactUri, + Predicate isExpiredMethod) { + RcsUceCapabilityBuilderWrapper builder = null; + EabCapabilityResult result; + Optional isExpired = Optional.empty(); + + // query EAB provider + Uri queryUri = Uri.withAppendedPath( + Uri.withAppendedPath(EabProvider.ALL_DATA_URI, String.valueOf(mSubId)), + getNumberFromUri(mContext, contactUri)); + Cursor cursor = mContext.getContentResolver().query(queryUri, null, null, null, null); + + if (cursor != null && cursor.getCount() != 0) { + while (cursor.moveToNext()) { + // Record whether it has expired. + if (!isExpired.isPresent()) { + isExpired = Optional.of(isExpiredMethod.test(cursor)); + } + if (builder == null) { + builder = createNewBuilder(contactUri, cursor); + } else { + updateCapability(contactUri, cursor, builder); + } + } + cursor.close(); + + // Determine the query result + int eabResult = EabCapabilityResult.EAB_QUERY_SUCCESSFUL; + if (isExpired.orElse(false)) { + eabResult = EabCapabilityResult.EAB_CONTACT_EXPIRED_FAILURE; + } + + if (builder.getMechanism() == CAPABILITY_MECHANISM_PRESENCE) { + PresenceBuilder presenceBuilder = builder.getPresenceBuilder(); + result = new EabCapabilityResult(contactUri, eabResult, presenceBuilder.build()); + } else { + OptionsBuilder optionsBuilder = builder.getOptionsBuilder(); + result = new EabCapabilityResult(contactUri, eabResult, optionsBuilder.build()); + } + } else { + result = new EabCapabilityResult(contactUri, + EabCapabilityResult.EAB_CONTACT_NOT_FOUND_FAILURE, null); + } + return result; + } + private void updateCapability(Uri contactUri, Cursor cursor, RcsUceCapabilityBuilderWrapper builderWrapper) { if (builderWrapper.getMechanism() == CAPABILITY_MECHANISM_PRESENCE) { diff --git a/src/java/com/android/ims/rcs/uce/request/CapabilityRequest.java b/src/java/com/android/ims/rcs/uce/request/CapabilityRequest.java index f7a4acc6..17cec90b 100644 --- a/src/java/com/android/ims/rcs/uce/request/CapabilityRequest.java +++ b/src/java/com/android/ims/rcs/uce/request/CapabilityRequest.java @@ -23,6 +23,7 @@ import android.util.Log; import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult; import com.android.ims.rcs.uce.eab.EabCapabilityResult; +import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils; import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; import com.android.ims.rcs.uce.util.UceUtils; import com.android.internal.annotations.VisibleForTesting; @@ -140,20 +141,36 @@ public abstract class CapabilityRequest implements UceRequest { return; } - // Get the capabilities from the cache. - final List cachedCapList - = isSkipGettingFromCache() ? Collections.EMPTY_LIST : getCapabilitiesFromCache(); + // Get the contact capabilities from the cache including the expired capabilities. + final List eabResultList = getCapabilitiesFromCache(); + + // Get all the unexpired capabilities from the EAB result list and add to the response. + final List cachedCapList = isSkipGettingFromCache() ? + Collections.EMPTY_LIST : getUnexpiredCapabilities(eabResultList); mRequestResponse.addCachedCapabilities(cachedCapList); logd("executeRequest: cached capabilities size=" + cachedCapList.size()); + // Get the rest contacts which are not in the cache or has expired. + final List expiredUris = getRequestingFromNetworkUris(cachedCapList); + + // For those uris that are not in the cache or have expired, we should request their + // capabilities from the network. However, we still need to check whether these contacts + // are in the throttling list. If the contact is in the throttling list, even if it has + // expired, we will get the cached capabilities. + final List throttlingUris = + getFromThrottlingList(expiredUris, eabResultList); + mRequestResponse.addCachedCapabilities(throttlingUris); + + logd("executeRequest: contacts in throttling list size=" + throttlingUris.size()); + // Notify that the cached capabilities are updated. - if (!cachedCapList.isEmpty()) { + if (!cachedCapList.isEmpty() || !throttlingUris.isEmpty()) { mRequestManagerCallback.notifyCachedCapabilitiesUpdated(mCoordinatorId, mTaskId); } // Get the rest contacts which need to request capabilities from the network. - final List requestCapUris = getRequestingFromNetworkUris(cachedCapList); + List requestCapUris = getRequestingFromNetworkUris(cachedCapList, throttlingUris); logd("executeRequest: requestCapUris size=" + requestCapUris.size()); @@ -193,21 +210,30 @@ public abstract class CapabilityRequest implements UceRequest { } // Get the cached capabilities by the given request type. - private List getCapabilitiesFromCache() { + private List getCapabilitiesFromCache() { List resultList = null; if (mRequestType == REQUEST_TYPE_CAPABILITY) { - resultList = mRequestManagerCallback.getCapabilitiesFromCache(mUriList); + resultList = mRequestManagerCallback.getCapabilitiesFromCacheIncludingExpired(mUriList); } else if (mRequestType == REQUEST_TYPE_AVAILABILITY) { // Always get the first element if the request type is availability. Uri uri = mUriList.get(0); - EabCapabilityResult eabResult = mRequestManagerCallback.getAvailabilityFromCache(uri); + EabCapabilityResult eabResult = + mRequestManagerCallback.getAvailabilityFromCacheIncludingExpired(uri); resultList = new ArrayList<>(); resultList.add(eabResult); } if (resultList == null) { return Collections.emptyList(); } - return resultList.stream() + return resultList; + } + + /** + * Get the unexpired contact capabilities from the given EAB result list. + * @param list the query result from the EAB + */ + private List getUnexpiredCapabilities(List list) { + return list.stream() .filter(Objects::nonNull) .filter(result -> result.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL) .map(EabCapabilityResult::getContactCapabilities) @@ -226,6 +252,67 @@ public abstract class CapabilityRequest implements UceRequest { .collect(Collectors.toList()); } + /** + * Get the contact uris which cannot retrieve capabilities from the cache. + * @param cachedCapList The capabilities which are already stored in the cache. + * @param throttlingUris The capabilities which are in the throttling list. + */ + private List getRequestingFromNetworkUris(List cachedCapList, + List throttlingUris) { + // We won't request the network query for those contacts in the cache and in the + // throttling list. Merging the two list and get the rest contact uris. + List notNetworkQueryList = new ArrayList<>(cachedCapList); + notNetworkQueryList.addAll(throttlingUris); + return getRequestingFromNetworkUris(notNetworkQueryList); + } + + /** + * Get the contact capabilities for those uri are in the throttling list. If the contact uri is + * in the throttling list, the capabilities will be retrieved from cache even if it has expired. + * If the capabilities cannot be found, return the non-RCS contact capabilities instead. + * @param expiredUris the expired/unknown uris to check whether are in the throttling list + * @return the contact capabilities for the uris are in the throttling list + */ + private List getFromThrottlingList(final List expiredUris, + final List eabResultList) { + List resultList = new ArrayList<>(); + List notFoundFromCacheList = new ArrayList<>(); + + // Retrieve the uris put in the throttling list from the expired/unknown contacts. + List throttlingUris = mRequestManagerCallback.getInThrottlingListUris(expiredUris); + + // For these uris in the throttling list, check whether their capabilities are in the cache. + List throttlingUriFoundInEab = new ArrayList<>(); + for (Uri uri : throttlingUris) { + for (EabCapabilityResult eabResult : eabResultList) { + if (eabResult.getContact().equals(uri)) { + throttlingUriFoundInEab.add(eabResult); + break; + } + } + } + + throttlingUriFoundInEab.forEach(eabResult -> { + if (eabResult.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL || + eabResult.getStatus() == EabCapabilityResult.EAB_CONTACT_EXPIRED_FAILURE) { + // The capabilities are found, add to the result list + resultList.add(eabResult.getContactCapabilities()); + } else { + // Cannot get the capabilities from cache, create the non-RCS capabilities instead. + notFoundFromCacheList.add(PidfParserUtils.getNotFoundContactCapabilities( + eabResult.getContact())); + } + }); + + if (!notFoundFromCacheList.isEmpty()) { + resultList.addAll(notFoundFromCacheList); + } + + logd("getFromThrottlingList: requesting uris in the list size=" + throttlingUris.size() + + ", generate non-RCS size=" + notFoundFromCacheList.size()); + return resultList; + } + /** * Set the timeout timer of this request. */ diff --git a/src/java/com/android/ims/rcs/uce/request/ContactThrottlingList.java b/src/java/com/android/ims/rcs/uce/request/ContactThrottlingList.java new file mode 100644 index 00000000..da67f6c9 --- /dev/null +++ b/src/java/com/android/ims/rcs/uce/request/ContactThrottlingList.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 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 com.android.ims.rcs.uce.request; + +import android.net.Uri; +import android.util.Log; + +import com.android.ims.rcs.uce.util.UceUtils; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * The class is used to store when the contact's capabilities request result is inconclusive. + */ +public class ContactThrottlingList { + private static final String LOG_TAG = UceUtils.getLogPrefix() + "ThrottlingList"; + + private static class ContactInfo { + Uri mContactUri; + int mSipCode; + Instant mThrottleEndTimestamp; + + public ContactInfo(Uri contactUri, int sipCode, Instant timestamp) { + mContactUri = contactUri; + mSipCode = sipCode; + mThrottleEndTimestamp = timestamp; + } + } + + private final int mSubId; + private final List mThrottlingList = new ArrayList<>(); + + public ContactThrottlingList(int subId) { + mSubId = subId; + } + + public synchronized void reset() { + mThrottlingList.clear(); + } + + public synchronized void addToThrottlingList(List uriList, int sipCode) { + // Clean up the expired contacts before starting. + cleanUpExpiredContacts(); + + List addToThrottlingList = getNotInThrottlingListUris(uriList); + long expiration = UceUtils.getAvailabilityCacheExpiration(mSubId); + Instant timestamp = Instant.now().plusSeconds(expiration); + + List list = addToThrottlingList.stream().map(uri -> + new ContactInfo(uri, sipCode, timestamp)).collect(Collectors.toList()); + + int previousSize = mThrottlingList.size(); + mThrottlingList.addAll(list); + + logd("addToThrottlingList: previous size=" + previousSize + + ", current size=" + mThrottlingList.size() + ", expired time=" + timestamp); + } + + private synchronized List getNotInThrottlingListUris(List uriList) { + List throttlingUris = mThrottlingList.stream().map(contactInfo -> + contactInfo.mContactUri).collect(Collectors.toList()); + List addToThrottlingUris = new ArrayList<>(uriList); + addToThrottlingUris.removeAll(throttlingUris); + return addToThrottlingUris; + } + + public synchronized List getInThrottlingListUris(List uriList) { + // Clean up the expired contacts before starting. + cleanUpExpiredContacts(); + + return uriList.stream() + .filter(uri -> mThrottlingList.stream() + .anyMatch(contactInfo -> contactInfo.mContactUri.equals(uri))) + .collect(Collectors.toList()); + } + + /** + * Clean up the expired contacts from the throttling list. + */ + private synchronized void cleanUpExpiredContacts() { + final int previousSize = mThrottlingList.size(); + List expiredContacts = mThrottlingList.stream() + .filter(contactInfo -> Instant.now() + .isAfter(contactInfo.mThrottleEndTimestamp)) + .collect(Collectors.toList()); + mThrottlingList.removeAll(expiredContacts); + + logd("cleanUpExpiredContacts: previous size=" + previousSize + + ", current size=" + mThrottlingList.size()); + } + + private void logd(String log) { + Log.d(LOG_TAG, getLogPrefix().append(log).toString()); + } + + private StringBuilder getLogPrefix() { + StringBuilder builder = new StringBuilder("["); + builder.append(mSubId); + builder.append("] "); + return builder; + } +} diff --git a/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java b/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java index ee6bd356..b3cc781b 100644 --- a/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java +++ b/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java @@ -25,11 +25,13 @@ import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.aidl.IRcsUceControllerCallback; import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult; +import com.android.ims.rcs.uce.eab.EabCapabilityResult; import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils; import com.android.ims.rcs.uce.request.SubscriptionTerminatedHelper.TerminatedResult; import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; import com.android.internal.annotations.VisibleForTesting; +import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; @@ -294,6 +296,7 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { private RequestResult handleNetworkResponseFailed(SubscribeRequest request) { final long taskId = request.getTaskId(); final CapabilityRequestResponse response = request.getRequestResponse(); + final List requestUris = response.getNotReceiveCapabilityUpdatedContact(); RequestResult requestResult = null; if (response.isNotFound()) { @@ -301,8 +304,7 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { // updated callback from the ImsService afterward. Therefore, we create the capabilities // with the result REQUEST_RESULT_NOT_FOUND by ourself and will trigger the // capabilities received callback to the clients later. - List uriList = request.getContactUri(); - List capabilityList = uriList.stream().map(uri -> + List capabilityList = requestUris.stream().map(uri -> PidfParserUtils.getNotFoundContactCapabilities(uri)) .collect(Collectors.toList()); response.addUpdatedCapabilities(capabilityList); @@ -310,15 +312,59 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { // We treat the NOT FOUND is a successful result. requestResult = sNetworkRespSuccessfulCreator.createRequestResult(taskId, response, mRequestManagerCallback); - } + } else { + // The request result is unsuccessful and it's not the NOT FOUND error. we need to get + // the capabilities from the cache. + List capabilitiesList = + getCapabilitiesFromCacheIncludingExpired(requestUris); + response.addUpdatedCapabilities(capabilitiesList); + + // Add to the throttling list for the inconclusive result of the contacts. + mRequestManagerCallback.addToThrottlingList(requestUris, + response.getResponseSipCode().orElse( + com.android.ims.rcs.uce.util.NetworkSipCode.SIP_CODE_REQUEST_TIMEOUT)); - if (requestResult == null) { requestResult = sNetworkRespErrorCreator.createRequestResult(taskId, response, mRequestManagerCallback); } return requestResult; } + /** + * Get the contact capabilities from the cache even if the capabilities have expired. If the + * capabilities doesn't exist, create the non-RCS capabilities instead. + * @param uris the uris to get the capabilities from cache. + * @return The contact capabilities for the given uris. + */ + private List getCapabilitiesFromCacheIncludingExpired(List uris) { + List resultList = new ArrayList<>(); + List notFoundFromCacheList = new ArrayList<>(); + + // Get the capabilities from the cache. + List eabResultList = + mRequestManagerCallback.getCapabilitiesFromCacheIncludingExpired(uris); + + eabResultList.forEach(eabResult -> { + if (eabResult.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL || + eabResult.getStatus() == EabCapabilityResult.EAB_CONTACT_EXPIRED_FAILURE) { + // The capabilities are found, add to the result list + resultList.add(eabResult.getContactCapabilities()); + } else { + // Cannot get the capabilities from cache, create the non-RCS capabilities instead. + notFoundFromCacheList.add(PidfParserUtils.getNotFoundContactCapabilities( + eabResult.getContact())); + } + }); + + if (!notFoundFromCacheList.isEmpty()) { + resultList.addAll(notFoundFromCacheList); + } + + logd("getCapabilitiesFromCacheIncludingExpired: requesting uris size=" + uris.size() + + ", capabilities not found from cache size=" + notFoundFromCacheList.size()); + return resultList; + } + /** * This method is called when the given SubscribeRequest received the onNotifyCapabilitiesUpdate * callback from the ImsService. @@ -420,20 +466,39 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator { } /** - * This method is called when the framework does not receive receive the result for - * capabilities request. + * This method is called when the framework did not receive the capabilities request result. */ private void handleRequestTimeout(SubscribeRequest request) { CapabilityRequestResponse response = request.getRequestResponse(); + List requestUris = response.getNotReceiveCapabilityUpdatedContact(); logd("handleRequestTimeout: " + response); + logd("handleRequestTimeout: not received updated uri size=" + requestUris.size()); - // Finish this request - request.onFinish(); + // Add to the throttling list for the inconclusive result of the contacts. + mRequestManagerCallback.addToThrottlingList(requestUris, + com.android.ims.rcs.uce.util.NetworkSipCode.SIP_CODE_REQUEST_TIMEOUT); + + // Get the capabilities from the cache instead and add to the response. + List capabilitiesList = + getCapabilitiesFromCacheIncludingExpired(requestUris); + response.addUpdatedCapabilities(capabilitiesList); + + // Trigger capabilities updated callback if there is any. + List updatedCapList = response.getUpdatedContactCapability(); + if (!updatedCapList.isEmpty()) { + triggerCapabilitiesReceivedCallback(updatedCapList); + response.removeUpdatedCapabilities(updatedCapList); + } // Remove this request from the activated collection and notify RequestManager. long taskId = request.getTaskId(); RequestResult requestResult = sRequestTimeoutCreator.createRequestResult(taskId, response, mRequestManagerCallback); + + // Finish this request + request.onFinish(); + + // Remove this request from the activated collection and notify RequestManager. moveRequestToFinishedCollection(taskId, requestResult); } diff --git a/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java b/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java index 3e12ba30..c5fa64ce 100644 --- a/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java +++ b/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java @@ -147,11 +147,21 @@ public class UceRequestManager { */ List getCapabilitiesFromCache(List uriList); + /** + * Retrieve the contact capabilities from the cache including the expired capabilities. + */ + List getCapabilitiesFromCacheIncludingExpired(List uriList); + /** * Retrieve the contact availability from the cache. */ EabCapabilityResult getAvailabilityFromCache(Uri uri); + /** + * Retrieve the contact availability from the cache including the expired capabilities. + */ + EabCapabilityResult getAvailabilityFromCacheIncludingExpired(Uri uri); + /** * Store the given contact capabilities to the cache. */ @@ -241,6 +251,19 @@ public class UceRequestManager { * to remove the coordinator from the UceRequestRepository. */ void notifyRequestCoordinatorFinished(long requestCoordinatorId); + + /** + * Check whether the given uris are in the throttling list. + * @param uriList the uris to check if it is in the throttling list + * @return the uris in the throttling list + */ + List getInThrottlingListUris(List uriList); + + /** + * Add the given uris to the throttling list because the capabilities request result + * is inconclusive. + */ + void addToThrottlingList(List uriList, int sipCode); } private RequestManagerCallback mRequestMgrCallback = new RequestManagerCallback() { @@ -254,11 +277,21 @@ public class UceRequestManager { return mControllerCallback.getCapabilitiesFromCache(uriList); } + @Override + public List getCapabilitiesFromCacheIncludingExpired(List uris) { + return mControllerCallback.getCapabilitiesFromCacheIncludingExpired(uris); + } + @Override public EabCapabilityResult getAvailabilityFromCache(Uri uri) { return mControllerCallback.getAvailabilityFromCache(uri); } + @Override + public EabCapabilityResult getAvailabilityFromCacheIncludingExpired(Uri uri) { + return mControllerCallback.getAvailabilityFromCacheIncludingExpired(uri); + } + @Override public void saveCapabilities(List contactCapabilities) { mControllerCallback.saveCapabilities(contactCapabilities); @@ -350,12 +383,23 @@ public class UceRequestManager { public void notifyRequestCoordinatorFinished(long requestCoordinatorId) { mHandler.sendRequestCoordinatorFinishedMessage(requestCoordinatorId); } + + @Override + public List getInThrottlingListUris(List uriList) { + return mThrottlingList.getInThrottlingListUris(uriList); + } + + @Override + public void addToThrottlingList(List uriList, int sipCode) { + mThrottlingList.addToThrottlingList(uriList, sipCode); + } }; private final int mSubId; private final Context mContext; private final UceRequestHandler mHandler; private final UceRequestRepository mRequestRepository; + private final ContactThrottlingList mThrottlingList; private volatile boolean mIsDestroyed; private OptionsController mOptionsCtrl; @@ -367,6 +411,7 @@ public class UceRequestManager { mContext = context; mControllerCallback = c; mHandler = new UceRequestHandler(this, looper); + mThrottlingList = new ContactThrottlingList(mSubId); mRequestRepository = new UceRequestRepository(subId, mRequestMgrCallback); logi("create"); } @@ -379,6 +424,7 @@ public class UceRequestManager { mControllerCallback = c; mHandler = new UceRequestHandler(this, looper); mRequestRepository = requestRepository; + mThrottlingList = new ContactThrottlingList(mSubId); } /** @@ -402,9 +448,17 @@ public class UceRequestManager { logi("onDestroy"); mIsDestroyed = true; mHandler.onDestroy(); + mThrottlingList.reset(); mRequestRepository.onDestroy(); } + /** + * Clear the throttling list. + */ + public void resetThrottlingList() { + mThrottlingList.reset(); + } + /** * Send a new capability request. It is called by UceController. */ diff --git a/src/java/com/android/ims/rcs/uce/util/UceUtils.java b/src/java/com/android/ims/rcs/uce/util/UceUtils.java index c88de0b3..d93f2a00 100644 --- a/src/java/com/android/ims/rcs/uce/util/UceUtils.java +++ b/src/java/com/android/ims/rcs/uce/util/UceUtils.java @@ -53,6 +53,9 @@ public class UceUtils { private static final long DEFAULT_CAP_REQUEST_TIMEOUT_AFTER_MS = TimeUnit.MINUTES.toMillis(3); private static Optional OVERRIDE_CAP_REQUEST_TIMEOUT_AFTER_MS = Optional.empty(); + // The default value of the availability cache expiration. + private static final long DEFAULT_AVAILABILITY_CACHE_EXPIRATION_SEC = 60L; // 60 seconds + // The task ID of the UCE request private static long TASK_ID = 0L; @@ -400,4 +403,26 @@ public class UceUtils { } return numberParts[0]; } + + /** + * Get the availability expiration from provisioning manager. + * @param subId The subscription ID + * @return the number of seconds for the availability cache expiration. + */ + public static long getAvailabilityCacheExpiration(int subId) { + long value = -1; + try { + ProvisioningManager pm = ProvisioningManager.createForSubscriptionId(subId); + value = pm.getProvisioningIntValue( + ProvisioningManager.KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC); + } catch (Exception e) { + Log.w(LOG_TAG, "Exception in getAvailabilityCacheExpiration: " + e); + } + + if (value <= 0) { + Log.w(LOG_TAG, "The availability expiration cannot be less than 0."); + value = DEFAULT_AVAILABILITY_CACHE_EXPIRATION_SEC; + } + return value; + } } -- cgit v1.2.3