1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ims.rcs.uce.presence.publish; 18 19 import android.util.Log; 20 21 import com.android.ims.rcs.uce.presence.publish.PublishController.PublishTriggerType; 22 import com.android.ims.rcs.uce.util.UceUtils; 23 24 import java.time.Duration; 25 import java.time.Instant; 26 import java.time.temporal.ChronoUnit; 27 import java.util.Optional; 28 import java.util.concurrent.TimeUnit; 29 30 /** 31 * The helper class to manage the publish request parameters. 32 */ 33 public class PublishProcessorState { 34 35 private static final String LOG_TAG = UceUtils.getLogPrefix() + "PublishProcessorState"; 36 37 /* 38 * Manager the pending request flag and the trigger type of this pending request. 39 */ 40 private static class PendingRequest { 41 private boolean mPendingFlag; 42 private Optional<Integer> mTriggerType; 43 private final Object mLock = new Object(); 44 PendingRequest()45 public PendingRequest() { 46 mTriggerType = Optional.empty(); 47 } 48 49 // Set the flag to indicate there is a pending request. setPendingRequest(@ublishTriggerType int triggerType)50 public void setPendingRequest(@PublishTriggerType int triggerType) { 51 synchronized (mLock) { 52 mPendingFlag = true; 53 mTriggerType = Optional.of(triggerType); 54 } 55 } 56 57 // Clear the flag. The publish request is triggered and this flag can be cleared. clearPendingRequest()58 public void clearPendingRequest() { 59 synchronized (mLock) { 60 mPendingFlag = false; 61 mTriggerType = Optional.empty(); 62 } 63 } 64 65 // Check if there is pending request need to be executed. hasPendingRequest()66 public boolean hasPendingRequest() { 67 synchronized (mLock) { 68 return mPendingFlag; 69 } 70 } 71 72 // Get the trigger type of the pending request. getPendingRequestTriggerType()73 public Optional<Integer> getPendingRequestTriggerType() { 74 synchronized (mLock) { 75 return mTriggerType; 76 } 77 } 78 } 79 80 /** 81 * Manager when the PUBLISH request can be executed. 82 */ 83 private static class PublishThrottle { 84 // The unit time interval of the request retry. 85 private static final int RETRY_BASE_PERIOD_MIN = 1; 86 87 // The maximum number of the publication retries. 88 private static final int PUBLISH_MAXIMUM_NUM_RETRIES = 3; 89 90 // Get the minimum time that allows two PUBLISH requests can be executed continuously. 91 // It is one of the calculation conditions for the next publish allowed time. 92 private long mRcsPublishThrottle; 93 94 // The number of times the PUBLISH failed to retry. It is one of the calculation conditions 95 // for the next publish allowed time. 96 private int mRetryCount; 97 98 // The subscription ID associated with this throttle helper. 99 private int mSubId; 100 101 // The time when the last PUBLISH request is success. It is one of the calculation 102 // conditions for the next publish allowed time. 103 private Optional<Instant> mLastPublishedTime; 104 105 // The time to allow to execute the publishing request. 106 private Optional<Instant> mPublishAllowedTime; 107 PublishThrottle(int subId)108 public PublishThrottle(int subId) { 109 mSubId = subId; 110 resetState(); 111 } 112 113 // Set the time of the last successful PUBLISH request. setLastPublishedTime(Instant lastPublishedTime)114 public void setLastPublishedTime(Instant lastPublishedTime) { 115 mLastPublishedTime = Optional.of(lastPublishedTime); 116 } 117 118 // Increase the retry count when the PUBLISH has failed and need to be retried. increaseRetryCount()119 public void increaseRetryCount() { 120 if (mRetryCount < PUBLISH_MAXIMUM_NUM_RETRIES) { 121 mRetryCount++; 122 } 123 // Adjust the publish allowed time. 124 calcLatestPublishAllowedTime(); 125 } 126 127 // Reset the retry count when the PUBLISH request is success or it does not need to retry. resetRetryCount()128 public void resetRetryCount() { 129 mRetryCount = 0; 130 // Adjust the publish allowed time. 131 calcLatestPublishAllowedTime(); 132 } 133 134 // In the case that the ImsService is disconnected, reset state for when the service 135 // reconnects resetState()136 public void resetState() { 137 mLastPublishedTime = Optional.empty(); 138 mPublishAllowedTime = Optional.empty(); 139 mRcsPublishThrottle = UceUtils.getRcsPublishThrottle(mSubId); 140 Log.d(LOG_TAG, "RcsPublishThrottle=" + mRcsPublishThrottle); 141 } 142 143 // Check if it has reached the maximum retries. isReachMaximumRetries()144 public boolean isReachMaximumRetries() { 145 return (mRetryCount >= PUBLISH_MAXIMUM_NUM_RETRIES) ? true : false; 146 } 147 148 // Update the RCS publish throttle updatePublishThrottle(int publishThrottle)149 public void updatePublishThrottle(int publishThrottle) { 150 mRcsPublishThrottle = publishThrottle; 151 calcLatestPublishAllowedTime(); 152 } 153 154 // Check if the PUBLISH request can be executed now. isPublishAllowedAtThisTime()155 public boolean isPublishAllowedAtThisTime() { 156 // If the allowed time has not been set, it means that it is not ready to PUBLISH. 157 // It means that it has not received the publish request from the service. 158 if (!mPublishAllowedTime.isPresent()) { 159 return false; 160 } 161 162 // Check whether the current time has exceeded the allowed PUBLISH. 163 return (Instant.now().isBefore(mPublishAllowedTime.get())) ? false : true; 164 } 165 166 // Update the PUBLISH allowed time with the given trigger type. updatePublishingAllowedTime(@ublishTriggerType int triggerType)167 public void updatePublishingAllowedTime(@PublishTriggerType int triggerType) { 168 if (triggerType == PublishController.PUBLISH_TRIGGER_SERVICE) { 169 // If the request is triggered by service, reset the retry count and allow to 170 // execute the PUBLISH immediately. 171 mRetryCount = 0; 172 mPublishAllowedTime = Optional.of(Instant.now()); 173 } else if (triggerType != PublishController.PUBLISH_TRIGGER_RETRY) { 174 // If the trigger type is not RETRY, it means that the device capabilities have 175 // changed, reset the retry cout. 176 resetRetryCount(); 177 } 178 } 179 180 // Get the delay time to allow to execute the PUBLISH request. getPublishingDelayTime()181 public Optional<Long> getPublishingDelayTime() { 182 // If the allowed time has not been set, it means that it is not ready to PUBLISH. 183 // It means that it has not received the publish request from the service. 184 if (!mPublishAllowedTime.isPresent()) { 185 return Optional.empty(); 186 } 187 188 // Setup the delay to the time which publish request is allowed to be executed. 189 long delayTime = ChronoUnit.MILLIS.between(Instant.now(), mPublishAllowedTime.get()); 190 if (delayTime < 0) { 191 delayTime = 0L; 192 } 193 return Optional.of(delayTime); 194 } 195 196 // Calculate the latest time allowed to PUBLISH calcLatestPublishAllowedTime()197 private void calcLatestPublishAllowedTime() { 198 final long retryDelay = getNextRetryDelayTime(); 199 if (!mLastPublishedTime.isPresent()) { 200 // If the publish request has not been successful before, it does not need to 201 // consider the PUBLISH throttle. The publish allowed time is decided by the retry 202 // delay. 203 mPublishAllowedTime = Optional.of( 204 Instant.now().plus(Duration.ofMillis(retryDelay))); 205 Log.d(LOG_TAG, "calcLatestPublishAllowedTime: The last published time is empty"); 206 } else { 207 // The default allowed time is the last published successful time plus the 208 // PUBLISH throttle. 209 Instant lastPublishedTime = mLastPublishedTime.get(); 210 Instant defaultAllowedTime = lastPublishedTime.plus( 211 Duration.ofMillis(mRcsPublishThrottle)); 212 213 if (retryDelay == 0) { 214 // If there is no delay time, the default allowed time is used. 215 mPublishAllowedTime = Optional.of(defaultAllowedTime); 216 } else { 217 // When the retry count is updated and there is delay time, it needs to compare 218 // the default time and the retry delay time. The later time will be the 219 // final decision value. 220 Instant retryDelayTime = Instant.now().plus(Duration.ofMillis(retryDelay)); 221 mPublishAllowedTime = Optional.of( 222 (retryDelayTime.isAfter(defaultAllowedTime)) 223 ? retryDelayTime : defaultAllowedTime); 224 } 225 } 226 Log.d(LOG_TAG, "calcLatestPublishAllowedTime: " + mPublishAllowedTime.get()); 227 } 228 229 // Get the milliseconds of the next retry delay. getNextRetryDelayTime()230 private long getNextRetryDelayTime() { 231 // If the current retry count is zero, the delay time is also zero. 232 if (mRetryCount == 0) return 0L; 233 // Next retry delay time (minute) 234 int power = mRetryCount - 1; 235 Double delayTime = RETRY_BASE_PERIOD_MIN * Math.pow(2, power); 236 // Convert to millis 237 return TimeUnit.MINUTES.toMillis(delayTime.longValue()); 238 } 239 } 240 241 242 private long mTaskId; 243 244 // Used to check whether the publish request is running now. 245 private volatile boolean mIsPublishing; 246 247 // Control the pending request flag. 248 private final PendingRequest mPendingRequest; 249 250 // Control the publish throttle 251 private final PublishThrottle mPublishThrottle; 252 253 private final Object mLock = new Object(); 254 PublishProcessorState(int subId)255 public PublishProcessorState(int subId) { 256 mPendingRequest = new PendingRequest(); 257 mPublishThrottle = new PublishThrottle(subId); 258 } 259 260 /** 261 * @return A unique task Id for this request. 262 */ generatePublishTaskId()263 public long generatePublishTaskId() { 264 synchronized (mLock) { 265 mTaskId = UceUtils.generateTaskId(); 266 return mTaskId; 267 } 268 } 269 270 /** 271 * @return The current valid PUBLISH task ID. 272 */ getCurrentTaskId()273 public long getCurrentTaskId() { 274 synchronized (mLock) { 275 return mTaskId; 276 } 277 } 278 279 /** 280 * Set the publishing flag to indicate whether it is executing a PUBLISH request or not. 281 */ setPublishingFlag(boolean flag)282 public void setPublishingFlag(boolean flag) { 283 mIsPublishing = flag; 284 } 285 286 /** 287 * @return true if it is executing a PUBLISH request now. 288 */ isPublishingNow()289 public boolean isPublishingNow() { 290 return mIsPublishing; 291 } 292 293 /** 294 * Set the flag to indicate there is a pending request waiting to be executed. 295 */ setPendingRequest(@ublishTriggerType int triggerType)296 public void setPendingRequest(@PublishTriggerType int triggerType) { 297 mPendingRequest.setPendingRequest(triggerType); 298 } 299 300 /** 301 * Clear the flag. It means a new publish request is triggered and the pending request flag 302 * can be cleared. 303 */ clearPendingRequest()304 public void clearPendingRequest() { 305 mPendingRequest.clearPendingRequest(); 306 } 307 308 /** 309 * @return true if there is pending request to be executed. 310 */ hasPendingRequest()311 public boolean hasPendingRequest() { 312 return mPendingRequest.hasPendingRequest(); 313 } 314 315 /** 316 * @return The trigger type of the pending request. If there is no pending request, it will 317 * return Optional.empty 318 */ getPendingRequestTriggerType()319 public Optional<Integer> getPendingRequestTriggerType() { 320 return mPendingRequest.getPendingRequestTriggerType(); 321 } 322 323 /** 324 * Set the time of the last successful PUBLISH request. 325 * @param lastPublishedTime The time when the last PUBLISH request is success 326 */ setLastPublishedTime(Instant lastPublishedTime)327 public void setLastPublishedTime(Instant lastPublishedTime) { 328 synchronized (mLock) { 329 mPublishThrottle.setLastPublishedTime(lastPublishedTime); 330 } 331 } 332 333 /** 334 * Increase the retry count when the PUBLISH has failed and need to retry. 335 */ increaseRetryCount()336 public void increaseRetryCount() { 337 synchronized (mLock) { 338 mPublishThrottle.increaseRetryCount(); 339 } 340 } 341 342 /** 343 * Reset the retry count when the PUBLISH request is success or it does not need to retry. 344 */ resetRetryCount()345 public void resetRetryCount() { 346 synchronized (mLock) { 347 mPublishThrottle.resetRetryCount(); 348 } 349 } 350 351 /** 352 * Reset the retry count and related time when the publication status has 353 * changed to not_published. 354 */ resetState()355 public void resetState() { 356 synchronized (mLock) { 357 mPublishThrottle.resetState(); 358 } 359 } 360 361 /* 362 * Check if it has reached the maximum retry count. 363 */ isReachMaximumRetries()364 public boolean isReachMaximumRetries() { 365 synchronized (mLock) { 366 return mPublishThrottle.isReachMaximumRetries(); 367 } 368 } 369 370 /* 371 * Check if the PUBLISH can be executed now. 372 */ isPublishAllowedAtThisTime()373 public boolean isPublishAllowedAtThisTime() { 374 synchronized (mLock) { 375 return mPublishThrottle.isPublishAllowedAtThisTime(); 376 } 377 } 378 379 /** 380 * Update the PUBLISH allowed time with the given trigger type. 381 * @param triggerType The trigger type of this PUBLISH request 382 */ updatePublishingAllowedTime(@ublishTriggerType int triggerType)383 public void updatePublishingAllowedTime(@PublishTriggerType int triggerType) { 384 synchronized (mLock) { 385 mPublishThrottle.updatePublishingAllowedTime(triggerType); 386 } 387 } 388 389 // Get the delay time to allow to execute the PUBLISH request. getPublishingDelayTime()390 public Optional<Long> getPublishingDelayTime() { 391 synchronized (mLock) { 392 return mPublishThrottle.getPublishingDelayTime(); 393 } 394 } 395 updatePublishThrottle(int publishThrottle)396 public void updatePublishThrottle(int publishThrottle) { 397 synchronized (mLock) { 398 mPublishThrottle.updatePublishThrottle(publishThrottle); 399 } 400 } 401 onRcsDisconnected()402 public void onRcsDisconnected() { 403 synchronized (mLock) { 404 setPublishingFlag(false /*isPublishing*/); 405 clearPendingRequest(); 406 mPublishThrottle.resetState(); 407 } 408 } 409 } 410