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.util; 18 19 import android.content.Context; 20 import android.content.SharedPreferences; 21 import android.net.Uri; 22 import android.os.PersistableBundle; 23 import android.preference.PreferenceManager; 24 import android.provider.BlockedNumberContract; 25 import android.telephony.CarrierConfigManager; 26 import android.telephony.SubscriptionManager; 27 import android.telephony.ims.ImsRcsManager; 28 import android.telephony.ims.ProvisioningManager; 29 import android.telephony.ims.stub.ImsRegistrationImplBase; 30 import android.text.TextUtils; 31 import android.util.Log; 32 33 import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult; 34 35 import java.time.Instant; 36 import java.util.Optional; 37 import java.util.concurrent.TimeUnit; 38 39 public class UceUtils { 40 41 public static final int LOG_SIZE = 20; 42 private static final String LOG_PREFIX = "RcsUce."; 43 private static final String LOG_TAG = LOG_PREFIX + "UceUtils"; 44 45 private static final String SHARED_PREF_DEVICE_STATE_KEY = "UceDeviceState"; 46 47 private static final int DEFAULT_RCL_MAX_NUM_ENTRIES = 100; 48 private static final long DEFAULT_RCS_PUBLISH_SOURCE_THROTTLE_MS = 60000L; 49 private static final long DEFAULT_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 50 TimeUnit.DAYS.toSeconds(30); 51 private static final long DEFAULT_REQUEST_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(20); 52 private static final long DEFAULT_MINIMUM_REQUEST_RETRY_AFTER_MS = TimeUnit.SECONDS.toMillis(3); 53 54 // The default of the capabilities request timeout. 55 private static final long DEFAULT_CAP_REQUEST_TIMEOUT_AFTER_MS = TimeUnit.MINUTES.toMillis(3); 56 private static Optional<Long> OVERRIDE_CAP_REQUEST_TIMEOUT_AFTER_MS = Optional.empty(); 57 58 // The default value of the availability cache expiration. 59 private static final long DEFAULT_AVAILABILITY_CACHE_EXPIRATION_SEC = 60L; // 60 seconds 60 61 // The task ID of the UCE request 62 private static long TASK_ID = 0L; 63 64 // The request coordinator ID 65 private static long REQUEST_COORDINATOR_ID = 0; 66 67 /** 68 * Get the log prefix of RCS UCE 69 */ getLogPrefix()70 public static String getLogPrefix() { 71 return LOG_PREFIX; 72 } 73 74 /** 75 * Generate the unique UCE request task id. 76 */ generateTaskId()77 public static synchronized long generateTaskId() { 78 return ++TASK_ID; 79 } 80 81 /** 82 * Generate the unique request coordinator id. 83 */ generateRequestCoordinatorId()84 public static synchronized long generateRequestCoordinatorId() { 85 return ++REQUEST_COORDINATOR_ID; 86 } 87 isEabProvisioned(Context context, int subId)88 public static boolean isEabProvisioned(Context context, int subId) { 89 boolean isProvisioned = false; 90 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 91 Log.w(LOG_TAG, "isEabProvisioned: invalid subscriptionId " + subId); 92 return false; 93 } 94 CarrierConfigManager configManager = (CarrierConfigManager) 95 context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 96 if (configManager != null) { 97 PersistableBundle config = configManager.getConfigForSubId(subId); 98 if (config != null && !config.getBoolean( 99 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONED_BOOL)) { 100 return true; 101 } 102 } 103 try { 104 ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId); 105 isProvisioned = manager.getRcsProvisioningStatusForCapability( 106 ImsRcsManager.CAPABILITY_TYPE_PRESENCE_UCE, 107 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 108 } catch (Exception e) { 109 Log.w(LOG_TAG, "isEabProvisioned: exception=" + e.getMessage()); 110 } 111 return isProvisioned; 112 } 113 114 /** 115 * Check whether or not this carrier supports the exchange of phone numbers with the carrier's 116 * presence server. 117 */ isPresenceCapExchangeEnabled(Context context, int subId)118 public static boolean isPresenceCapExchangeEnabled(Context context, int subId) { 119 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 120 if (configManager == null) { 121 return false; 122 } 123 PersistableBundle config = configManager.getConfigForSubId(subId); 124 if (config == null) { 125 return false; 126 } 127 return config.getBoolean( 128 CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL); 129 } 130 131 /** 132 * Check if Presence is supported by the carrier. 133 */ isPresenceSupported(Context context, int subId)134 public static boolean isPresenceSupported(Context context, int subId) { 135 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 136 if (configManager == null) { 137 return false; 138 } 139 PersistableBundle config = configManager.getConfigForSubId(subId); 140 if (config == null) { 141 return false; 142 } 143 return config.getBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL); 144 } 145 146 /** 147 * Check if SIP OPTIONS is supported by the carrier. 148 */ isSipOptionsSupported(Context context, int subId)149 public static boolean isSipOptionsSupported(Context context, int subId) { 150 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 151 if (configManager == null) { 152 return false; 153 } 154 PersistableBundle config = configManager.getConfigForSubId(subId); 155 if (config == null) { 156 return false; 157 } 158 return config.getBoolean(CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL); 159 } 160 161 /** 162 * Check whether the PRESENCE group subscribe is enabled or not. 163 * 164 * @return true when the Presence group subscribe is enabled, false otherwise. 165 */ isPresenceGroupSubscribeEnabled(Context context, int subId)166 public static boolean isPresenceGroupSubscribeEnabled(Context context, int subId) { 167 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 168 if (configManager == null) { 169 return false; 170 } 171 PersistableBundle config = configManager.getConfigForSubId(subId); 172 if (config == null) { 173 return false; 174 } 175 return config.getBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL); 176 } 177 178 /** 179 * Returns {@code true} if {@code phoneNumber} is blocked. 180 * 181 * @param context the context of the caller. 182 * @param phoneNumber the number to check. 183 * @return true if the number is blocked, false otherwise. 184 */ isNumberBlocked(Context context, String phoneNumber)185 public static boolean isNumberBlocked(Context context, String phoneNumber) { 186 int blockStatus; 187 try { 188 blockStatus = BlockedNumberContract.SystemContract.shouldSystemBlockNumber( 189 context, phoneNumber, null /*extras*/); 190 } catch (Exception e) { 191 return false; 192 } 193 return blockStatus != BlockedNumberContract.STATUS_NOT_BLOCKED; 194 } 195 196 /** 197 * Check whether sip uri should be used for presence subscribe 198 */ isSipUriForPresenceSubscribeEnabled(Context context, int subId)199 public static boolean isSipUriForPresenceSubscribeEnabled(Context context, int subId) { 200 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 201 if (configManager == null) { 202 return false; 203 } 204 PersistableBundle config = configManager.getConfigForSubId(subId); 205 if (config == null) { 206 return false; 207 } 208 return config.getBoolean( 209 CarrierConfigManager.Ims.KEY_USE_SIP_URI_FOR_PRESENCE_SUBSCRIBE_BOOL); 210 } 211 212 /** 213 * Check whether tel uri should be used for pidf xml 214 */ isTelUriForPidfXmlEnabled(Context context, int subId)215 public static boolean isTelUriForPidfXmlEnabled(Context context, int subId) { 216 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 217 if (configManager == null) { 218 return false; 219 } 220 PersistableBundle config = configManager.getConfigForSubId(subId); 221 if (config == null) { 222 return false; 223 } 224 return config.getBoolean( 225 CarrierConfigManager.Ims.KEY_USE_TEL_URI_FOR_PIDF_XML_BOOL); 226 } 227 228 /** 229 * Get the minimum time that allow two PUBLISH requests can be executed continuously. 230 * 231 * @param subId The subscribe ID 232 * @return The milliseconds that allowed two consecutive publish request. 233 */ getRcsPublishThrottle(int subId)234 public static long getRcsPublishThrottle(int subId) { 235 long throttle = DEFAULT_RCS_PUBLISH_SOURCE_THROTTLE_MS; 236 try { 237 ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId); 238 long provisioningValue = manager.getProvisioningIntValue( 239 ProvisioningManager.KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS); 240 if (provisioningValue > 0) { 241 throttle = provisioningValue; 242 } 243 } catch (Exception e) { 244 Log.w(LOG_TAG, "getRcsPublishThrottle: exception=" + e.getMessage()); 245 } 246 return throttle; 247 } 248 249 /** 250 * Retrieve the maximum number of contacts that is in one Request Contained List(RCL) 251 * 252 * @param subId The subscribe ID 253 * @return The maximum number of contacts. 254 */ getRclMaxNumberEntries(int subId)255 public static int getRclMaxNumberEntries(int subId) { 256 int maxNumEntries = DEFAULT_RCL_MAX_NUM_ENTRIES; 257 try { 258 ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId); 259 int provisioningValue = manager.getProvisioningIntValue( 260 ProvisioningManager.KEY_RCS_MAX_NUM_ENTRIES_IN_RCL); 261 if (provisioningValue > 0) { 262 maxNumEntries = provisioningValue; 263 } 264 } catch (Exception e) { 265 Log.w(LOG_TAG, "getRclMaxNumberEntries: exception=" + e.getMessage()); 266 } 267 return maxNumEntries; 268 } 269 getNonRcsCapabilitiesCacheExpiration(Context context, int subId)270 public static long getNonRcsCapabilitiesCacheExpiration(Context context, int subId) { 271 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 272 if (configManager == null) { 273 return DEFAULT_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC; 274 } 275 PersistableBundle config = configManager.getConfigForSubId(subId); 276 if (config == null) { 277 return DEFAULT_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC; 278 } 279 return config.getInt( 280 CarrierConfigManager.Ims.KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT); 281 } 282 isRequestForbiddenBySip489(Context context, int subId)283 public static boolean isRequestForbiddenBySip489(Context context, int subId) { 284 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 285 if (configManager == null) { 286 return false; 287 } 288 PersistableBundle config = configManager.getConfigForSubId(subId); 289 if (config == null) { 290 return false; 291 } 292 return config.getBoolean( 293 CarrierConfigManager.Ims.KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL); 294 } 295 getRequestRetryInterval(Context context, int subId)296 public static long getRequestRetryInterval(Context context, int subId) { 297 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 298 if (configManager == null) { 299 return DEFAULT_REQUEST_RETRY_INTERVAL_MS; 300 } 301 PersistableBundle config = configManager.getConfigForSubId(subId); 302 if (config == null) { 303 return DEFAULT_REQUEST_RETRY_INTERVAL_MS; 304 } 305 return config.getLong( 306 CarrierConfigManager.Ims.KEY_RCS_REQUEST_RETRY_INTERVAL_MILLIS_LONG); 307 } 308 saveDeviceStateToPreference(Context context, int subId, DeviceStateResult deviceState)309 public static boolean saveDeviceStateToPreference(Context context, int subId, 310 DeviceStateResult deviceState) { 311 SharedPreferences sharedPreferences = 312 PreferenceManager.getDefaultSharedPreferences(context); 313 SharedPreferences.Editor editor = sharedPreferences.edit(); 314 editor.putString(getDeviceStateSharedPrefKey(subId), 315 getDeviceStateSharedPrefValue(deviceState)); 316 return editor.commit(); 317 } 318 restoreDeviceState(Context context, int subId)319 public static Optional<DeviceStateResult> restoreDeviceState(Context context, int subId) { 320 SharedPreferences sharedPreferences = 321 PreferenceManager.getDefaultSharedPreferences(context); 322 final String sharedPrefKey = getDeviceStateSharedPrefKey(subId); 323 String sharedPrefValue = sharedPreferences.getString(sharedPrefKey, ""); 324 if (TextUtils.isEmpty(sharedPrefValue)) { 325 return Optional.empty(); 326 } 327 String[] valueAry = sharedPrefValue.split(","); 328 if (valueAry == null || valueAry.length != 4) { 329 return Optional.empty(); 330 } 331 try { 332 int deviceState = Integer.valueOf(valueAry[0]); 333 Optional<Integer> errorCode = (Integer.valueOf(valueAry[1]) == -1L) ? 334 Optional.empty() : Optional.of(Integer.valueOf(valueAry[1])); 335 336 long retryTimeMillis = Long.valueOf(valueAry[2]); 337 Optional<Instant> retryTime = (retryTimeMillis == -1L) ? 338 Optional.empty() : Optional.of(Instant.ofEpochMilli(retryTimeMillis)); 339 340 long exitStateTimeMillis = Long.valueOf(valueAry[3]); 341 Optional<Instant> exitStateTime = (exitStateTimeMillis == -1L) ? 342 Optional.empty() : Optional.of(Instant.ofEpochMilli(exitStateTimeMillis)); 343 344 return Optional.of(new DeviceStateResult(deviceState, errorCode, retryTime, 345 exitStateTime)); 346 } catch (Exception e) { 347 Log.d(LOG_TAG, "restoreDeviceState: exception " + e); 348 return Optional.empty(); 349 } 350 } 351 removeDeviceStateFromPreference(Context context, int subId)352 public static boolean removeDeviceStateFromPreference(Context context, int subId) { 353 SharedPreferences sharedPreferences = 354 PreferenceManager.getDefaultSharedPreferences(context); 355 SharedPreferences.Editor editor = sharedPreferences.edit(); 356 editor.remove(getDeviceStateSharedPrefKey(subId)); 357 return editor.commit(); 358 } 359 getDeviceStateSharedPrefKey(int subId)360 private static String getDeviceStateSharedPrefKey(int subId) { 361 return SHARED_PREF_DEVICE_STATE_KEY + subId; 362 } 363 364 /** 365 * Build the device state preference value. 366 */ getDeviceStateSharedPrefValue(DeviceStateResult deviceState)367 private static String getDeviceStateSharedPrefValue(DeviceStateResult deviceState) { 368 StringBuilder builder = new StringBuilder(); 369 builder.append(deviceState.getDeviceState()) // device state 370 .append(",").append(deviceState.getErrorCode().orElse(-1)); // error code 371 372 long retryTimeMillis = -1L; 373 Optional<Instant> retryTime = deviceState.getRequestRetryTime(); 374 if (retryTime.isPresent()) { 375 retryTimeMillis = retryTime.get().toEpochMilli(); 376 } 377 builder.append(",").append(retryTimeMillis); // retryTime 378 379 long exitStateTimeMillis = -1L; 380 Optional<Instant> exitStateTime = deviceState.getExitStateTime(); 381 if (exitStateTime.isPresent()) { 382 exitStateTimeMillis = exitStateTime.get().toEpochMilli(); 383 } 384 builder.append(",").append(exitStateTimeMillis); // exit state time 385 return builder.toString(); 386 } 387 388 /** 389 * Get the minimum value of the capabilities request retry after. 390 */ getMinimumRequestRetryAfterMillis()391 public static long getMinimumRequestRetryAfterMillis() { 392 return DEFAULT_MINIMUM_REQUEST_RETRY_AFTER_MS; 393 } 394 395 /** 396 * Override the capability request timeout to the millisecond value specified. Sending a 397 * value <= 0 will reset the capabilities. 398 */ setCapRequestTimeoutAfterMillis(long timeoutAfterMs)399 public static synchronized void setCapRequestTimeoutAfterMillis(long timeoutAfterMs) { 400 if (timeoutAfterMs <= 0L) { 401 OVERRIDE_CAP_REQUEST_TIMEOUT_AFTER_MS = Optional.empty(); 402 } else { 403 OVERRIDE_CAP_REQUEST_TIMEOUT_AFTER_MS = Optional.of(timeoutAfterMs); 404 } 405 } 406 407 /** 408 * Get the milliseconds of the capabilities request timed out. 409 * @return the time in milliseconds before a pending capabilities request will time out. 410 */ getCapRequestTimeoutAfterMillis()411 public static synchronized long getCapRequestTimeoutAfterMillis() { 412 if(OVERRIDE_CAP_REQUEST_TIMEOUT_AFTER_MS.isPresent()) { 413 return OVERRIDE_CAP_REQUEST_TIMEOUT_AFTER_MS.get(); 414 } else { 415 return DEFAULT_CAP_REQUEST_TIMEOUT_AFTER_MS; 416 } 417 } 418 419 /** 420 * Get the contact number from the given URI. 421 * @param contactUri The contact uri of the capabilities to request for. 422 * @return The number of the contact uri. NULL if the number cannot be retrieved. 423 */ getContactNumber(Uri contactUri)424 public static String getContactNumber(Uri contactUri) { 425 if (contactUri == null) { 426 return null; 427 } 428 String number = contactUri.getSchemeSpecificPart(); 429 if (TextUtils.isEmpty(number)) { 430 return null; 431 } 432 433 String numberParts[] = number.split("[@;:]"); 434 if (numberParts.length == 0) { 435 Log.d(LOG_TAG, "getContactNumber: the length of numberPars is 0"); 436 return contactUri.toString(); 437 } 438 return numberParts[0]; 439 } 440 441 /** 442 * Get the availability expiration from provisioning manager. 443 * @param subId The subscription ID 444 * @return the number of seconds for the availability cache expiration. 445 */ getAvailabilityCacheExpiration(int subId)446 public static long getAvailabilityCacheExpiration(int subId) { 447 long value = -1; 448 try { 449 ProvisioningManager pm = ProvisioningManager.createForSubscriptionId(subId); 450 value = pm.getProvisioningIntValue( 451 ProvisioningManager.KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC); 452 } catch (Exception e) { 453 Log.w(LOG_TAG, "Exception in getAvailabilityCacheExpiration: " + e); 454 } 455 456 if (value <= 0) { 457 Log.w(LOG_TAG, "The availability expiration cannot be less than 0."); 458 value = DEFAULT_AVAILABILITY_CACHE_EXPIRATION_SEC; 459 } 460 return value; 461 } 462 463 /** 464 * The time interval of milliseconds for the subscribe retry. 465 * 466 * @return The time interval of milliseconds for the subscribe retry. 467 */ getSubscribeRetryInterval(Context context, int subId)468 public static long getSubscribeRetryInterval(Context context, int subId) { 469 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 470 if (configManager == null) { 471 return -1L; 472 } 473 PersistableBundle config = configManager.getConfigForSubId(subId); 474 if (config == null) { 475 return -1L; 476 } 477 478 return config.getLong( 479 CarrierConfigManager.Ims.KEY_SUBSCRIBE_RETRY_DURATION_MILLIS_LONG, -1L); 480 } 481 } 482