1 /* 2 * Copyright (C) 2022 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.safetycenter; 18 19 import static android.safetycenter.SafetyCenterManager.RefreshReason; 20 21 import android.os.Binder; 22 import android.provider.DeviceConfig; 23 import android.safetycenter.SafetySourceData; 24 import android.safetycenter.SafetySourceIssue; 25 import android.util.ArraySet; 26 import android.util.Log; 27 28 import androidx.annotation.Nullable; 29 30 import com.android.modules.utils.build.SdkLevel; 31 import com.android.safetycenter.resources.SafetyCenterResourcesApk; 32 33 import java.io.PrintWriter; 34 import java.time.Duration; 35 import java.util.Arrays; 36 import java.util.Collections; 37 import java.util.List; 38 39 /** 40 * A class to access the Safety Center {@link DeviceConfig} flags. 41 * 42 * @hide 43 */ 44 public final class SafetyCenterFlags { 45 46 private static final String TAG = "SafetyCenterFlags"; 47 48 /** {@link DeviceConfig} property name for {@link #getSafetyCenterEnabled()}. */ 49 static final String PROPERTY_SAFETY_CENTER_ENABLED = "safety_center_is_enabled"; 50 51 private static final String PROPERTY_NOTIFICATIONS_ENABLED = 52 "safety_center_notifications_enabled"; 53 54 private static final String PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES = 55 "safety_center_notifications_allowed_sources"; 56 57 private static final String PROPERTY_NOTIFICATIONS_MIN_DELAY = 58 "safety_center_notifications_min_delay"; 59 60 private static final String PROPERTY_NOTIFICATIONS_IMMEDIATE_BEHAVIOR_ISSUES = 61 "safety_center_notifications_immediate_behavior_issues"; 62 63 private static final String PROPERTY_NOTIFICATION_RESURFACE_INTERVAL = 64 "safety_center_notification_resurface_interval"; 65 66 private static final String PROPERTY_REPLACE_LOCK_SCREEN_ICON_ACTION = 67 "safety_center_replace_lock_screen_icon_action"; 68 69 private static final String PROPERTY_RESOLVING_ACTION_TIMEOUT_MILLIS = 70 "safety_center_resolve_action_timeout_millis"; 71 72 private static final String PROPERTY_FGS_ALLOWLIST_DURATION_MILLIS = 73 "safety_center_refresh_fgs_allowlist_duration_millis"; 74 75 private static final String PROPERTY_RESURFACE_ISSUE_MAX_COUNTS = 76 "safety_center_resurface_issue_max_counts"; 77 78 private static final String PROPERTY_RESURFACE_ISSUE_DELAYS_MILLIS = 79 "safety_center_resurface_issue_delays_millis"; 80 81 private static final String PROPERTY_UNTRACKED_SOURCES = "safety_center_untracked_sources"; 82 83 private static final String PROPERTY_BACKGROUND_REFRESH_DENIED_SOURCES = 84 "safety_center_background_refresh_denied_sources"; 85 86 private static final String PROPERTY_REFRESH_SOURCES_TIMEOUTS_MILLIS = 87 "safety_center_refresh_sources_timeouts_millis"; 88 89 private static final String PROPERTY_ISSUE_CATEGORY_ALLOWLISTS = 90 "safety_center_issue_category_allowlists"; 91 92 private static final String PROPERTY_ALLOW_STATSD_LOGGING = 93 "safety_center_allow_statsd_logging"; 94 95 private static final String PROPERTY_SHOW_SUBPAGES = "safety_center_show_subpages"; 96 97 private static final String PROPERTY_OVERRIDE_REFRESH_ON_PAGE_OPEN_SOURCES = 98 "safety_center_override_refresh_on_page_open_sources"; 99 100 private static final String PROPERTY_ADDITIONAL_ALLOW_PACKAGE_CERTS = 101 "safety_center_additional_allow_package_certs"; 102 103 private static final Duration FGS_ALLOWLIST_DEFAULT_DURATION = Duration.ofSeconds(20); 104 105 private static final String PROPERTY_TEMP_HIDDEN_ISSUE_RESURFACE_DELAY_MILLIS = 106 "safety_center_temp_hidden_issue_resurface_delay_millis"; 107 108 private static final String PROPERTY_ACTIONS_TO_OVERRIDE_WITH_DEFAULT_INTENT = 109 "safety_center_actions_to_override_with_default_intent"; 110 111 private static final Duration RESOLVING_ACTION_TIMEOUT_DEFAULT_DURATION = 112 Duration.ofSeconds(10); 113 114 private static final Duration NOTIFICATIONS_MIN_DELAY_DEFAULT_DURATION = Duration.ofDays(180); 115 116 private static final String REFRESH_SOURCES_TIMEOUT_DEFAULT = 117 "100:15000,200:60000,300:30000,400:30000,500:30000,600:3600000"; 118 private static final Duration REFRESH_SOURCES_TIMEOUT_DEFAULT_DURATION = Duration.ofSeconds(15); 119 120 private static final String RESURFACE_ISSUE_MAX_COUNT_DEFAULT = "200:0,300:1,400:1"; 121 private static final long RESURFACE_ISSUE_MAX_COUNT_DEFAULT_COUNT = 0; 122 123 private static final String RESURFACE_ISSUE_DELAYS_DEFAULT = ""; 124 private static final Duration RESURFACE_ISSUE_DELAYS_DEFAULT_DURATION = Duration.ofDays(180); 125 126 private static final ArraySet<String> sAllowedNotificationSourcesUPlus = 127 new ArraySet<>(new String[] {"GoogleBackupAndRestore"}); 128 129 private static volatile String sUntrackedSourcesDefault = 130 "AndroidAccessibility,AndroidBackgroundLocation," 131 + "AndroidNotificationListener,AndroidPermissionAutoRevoke"; 132 133 private static volatile String sBackgroundRefreshDenyDefault = ""; 134 135 private static volatile String sIssueCategoryAllowlistDefault = ""; 136 137 private static volatile String sRefreshOnPageOpenSourcesDefault = "AndroidBiometrics"; 138 139 private static volatile String sActionsToOverrideWithDefaultIntentDefault = ""; 140 init(SafetyCenterResourcesApk safetyCenterResourcesApk)141 static void init(SafetyCenterResourcesApk safetyCenterResourcesApk) { 142 String untrackedSourcesDefault = 143 safetyCenterResourcesApk.getOptionalStringByName("config_defaultUntrackedSources"); 144 if (untrackedSourcesDefault != null) { 145 sUntrackedSourcesDefault = untrackedSourcesDefault; 146 } 147 String backgroundRefreshDenyDefault = 148 safetyCenterResourcesApk.getOptionalStringByName( 149 "config_defaultBackgroundRefreshDeny"); 150 if (backgroundRefreshDenyDefault != null) { 151 sBackgroundRefreshDenyDefault = backgroundRefreshDenyDefault; 152 } 153 String issueCategoryAllowlistDefault = 154 safetyCenterResourcesApk.getOptionalStringByName( 155 "config_defaultIssueCategoryAllowlist"); 156 if (issueCategoryAllowlistDefault != null) { 157 sIssueCategoryAllowlistDefault = issueCategoryAllowlistDefault; 158 } 159 String refreshOnPageOpenSourcesDefault = 160 safetyCenterResourcesApk.getOptionalStringByName( 161 "config_defaultRefreshOnPageOpenSources"); 162 if (refreshOnPageOpenSourcesDefault != null) { 163 sRefreshOnPageOpenSourcesDefault = refreshOnPageOpenSourcesDefault; 164 } 165 String actionsToOverrideWithDefaultIntentDefault = 166 safetyCenterResourcesApk.getOptionalStringByName( 167 "config_defaultActionsToOverrideWithDefaultIntent"); 168 if (actionsToOverrideWithDefaultIntentDefault != null) { 169 sActionsToOverrideWithDefaultIntentDefault = actionsToOverrideWithDefaultIntentDefault; 170 } 171 } 172 173 private static final Duration TEMP_HIDDEN_ISSUE_RESURFACE_DELAY_DEFAULT_DURATION = 174 Duration.ofDays(2); 175 176 /** Dumps state for debugging purposes. */ dump(PrintWriter fout)177 static void dump(PrintWriter fout) { 178 fout.println("FLAGS"); 179 printFlag(fout, PROPERTY_SAFETY_CENTER_ENABLED, getSafetyCenterEnabled()); 180 printFlag(fout, PROPERTY_NOTIFICATIONS_ENABLED, getNotificationsEnabled()); 181 printFlag( 182 fout, 183 PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES, 184 getNotificationsAllowedSourceIdsFlag()); 185 printFlag(fout, PROPERTY_NOTIFICATIONS_MIN_DELAY, getNotificationsMinDelay()); 186 printFlag( 187 fout, 188 PROPERTY_NOTIFICATIONS_IMMEDIATE_BEHAVIOR_ISSUES, 189 getImmediateNotificationBehaviorIssues()); 190 printFlag( 191 fout, PROPERTY_NOTIFICATION_RESURFACE_INTERVAL, getNotificationResurfaceInterval()); 192 printFlag(fout, PROPERTY_REPLACE_LOCK_SCREEN_ICON_ACTION, getReplaceLockScreenIconAction()); 193 printFlag(fout, PROPERTY_RESOLVING_ACTION_TIMEOUT_MILLIS, getResolvingActionTimeout()); 194 printFlag(fout, PROPERTY_FGS_ALLOWLIST_DURATION_MILLIS, getFgsAllowlistDuration()); 195 printFlag(fout, PROPERTY_UNTRACKED_SOURCES, getUntrackedSourceIds()); 196 printFlag(fout, PROPERTY_RESURFACE_ISSUE_MAX_COUNTS, getResurfaceIssueMaxCounts()); 197 printFlag(fout, PROPERTY_RESURFACE_ISSUE_DELAYS_MILLIS, getResurfaceIssueDelaysMillis()); 198 printFlag( 199 fout, 200 PROPERTY_BACKGROUND_REFRESH_DENIED_SOURCES, 201 getBackgroundRefreshDeniedSourceIds()); 202 printFlag( 203 fout, PROPERTY_REFRESH_SOURCES_TIMEOUTS_MILLIS, getRefreshSourcesTimeoutsMillis()); 204 printFlag(fout, PROPERTY_ISSUE_CATEGORY_ALLOWLISTS, getIssueCategoryAllowlists()); 205 printFlag(fout, PROPERTY_ALLOW_STATSD_LOGGING, getAllowStatsdLogging()); 206 printFlag(fout, PROPERTY_SHOW_SUBPAGES, getShowSubpages()); 207 printFlag( 208 fout, 209 PROPERTY_OVERRIDE_REFRESH_ON_PAGE_OPEN_SOURCES, 210 getOverrideRefreshOnPageOpenSourceIds()); 211 printFlag( 212 fout, 213 PROPERTY_ADDITIONAL_ALLOW_PACKAGE_CERTS, 214 getAdditionalAllowedPackageCertsString()); 215 fout.println(); 216 } 217 printFlag(PrintWriter pw, String key, @Nullable Duration duration)218 private static void printFlag(PrintWriter pw, String key, @Nullable Duration duration) { 219 if (duration == null) { 220 printFlag(pw, key, "null"); 221 } else { 222 printFlag(pw, key, duration.toMillis() + " (" + duration + ")"); 223 } 224 } 225 printFlag(PrintWriter pw, String key, Object value)226 private static void printFlag(PrintWriter pw, String key, Object value) { 227 pw.println("\t" + key + "=" + value); 228 } 229 230 /** Returns whether Safety Center is enabled. */ getSafetyCenterEnabled()231 public static boolean getSafetyCenterEnabled() { 232 return getBoolean(PROPERTY_SAFETY_CENTER_ENABLED, SdkLevel.isAtLeastU()); 233 } 234 235 /** Returns whether Safety Center notifications are enabled. */ getNotificationsEnabled()236 public static boolean getNotificationsEnabled() { 237 return getBoolean(PROPERTY_NOTIFICATIONS_ENABLED, SdkLevel.isAtLeastU()); 238 } 239 240 /** 241 * Returns the IDs of sources that Safety Center can send notifications about, in addition to 242 * those permitted by the current XML config. 243 * 244 * <p>If the ID of a source appears on this list then Safety Center may send notifications about 245 * issues from that source, regardless of (overriding) the XML config. If the ID of a source is 246 * absent from this list, then Safety Center may send such notifications only if the XML config 247 * allows it. 248 * 249 * <p>Note that the {@code areNotificationsAllowed} config attribute is only available on API U+ 250 * and therefore this is the only way to enable notifications for sources on Android T. 251 */ getNotificationsAllowedSourceIds()252 public static ArraySet<String> getNotificationsAllowedSourceIds() { 253 ArraySet<String> sources = getNotificationsAllowedSourceIdsFlag(); 254 if (SdkLevel.isAtLeastU()) { 255 // This is a hack to update the flag value via mainline update. Reasons why we can't do 256 // this via: 257 // remote flag update - these are generally avoided and considered risky 258 // XML config - it would break GTS tests for OEMs that have a separate config copy 259 // default flag value - it would also require a remote flag update 260 sources.addAll(sAllowedNotificationSourcesUPlus); 261 } 262 263 return sources; 264 } 265 getNotificationsAllowedSourceIdsFlag()266 private static ArraySet<String> getNotificationsAllowedSourceIdsFlag() { 267 return getCommaSeparatedStrings(PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES); 268 } 269 270 /** 271 * Returns the minimum delay before Safety Center can send a notification for an issue with 272 * {@link SafetySourceIssue#NOTIFICATION_BEHAVIOR_DELAYED}. 273 * 274 * <p>The actual delay used may be longer. 275 */ getNotificationsMinDelay()276 public static Duration getNotificationsMinDelay() { 277 return getDuration( 278 PROPERTY_NOTIFICATIONS_MIN_DELAY, NOTIFICATIONS_MIN_DELAY_DEFAULT_DURATION); 279 } 280 281 /** 282 * Returns the issue type IDs for which, if otherwise undefined, Safety Center should use {@link 283 * SafetySourceIssue#NOTIFICATION_BEHAVIOR_IMMEDIATELY}. 284 * 285 * <p>If a safety source specifies the notification behavior of an issue explicitly this flag 286 * has no effect, even if the issue matches one of the entries in this flag. 287 * 288 * <p>Entries in this set should be strings of the form "safety_source_id/issue_type_id". 289 */ getImmediateNotificationBehaviorIssues()290 public static ArraySet<String> getImmediateNotificationBehaviorIssues() { 291 return getCommaSeparatedStrings(PROPERTY_NOTIFICATIONS_IMMEDIATE_BEHAVIOR_ISSUES); 292 } 293 294 /** 295 * Returns the minimum interval that must elapse before Safety Center can resurface a 296 * notification after it was dismissed, or {@code null} (the default) if dismissed notifications 297 * cannot resurface. 298 * 299 * <p>Returns {@code null} if the underlying device config flag is either unset or is set to a 300 * negative value. 301 * 302 * <p>There may be other conditions for resurfacing a notification and the actual delay may be 303 * longer than this. 304 */ 305 @Nullable getNotificationResurfaceInterval()306 public static Duration getNotificationResurfaceInterval() { 307 long millis = getLong(PROPERTY_NOTIFICATION_RESURFACE_INTERVAL, -1); 308 if (millis < 0) { 309 return null; 310 } else { 311 return Duration.ofMillis(millis); 312 } 313 } 314 315 /** 316 * Returns whether we should replace the lock screen source's {@link 317 * android.safetycenter.SafetySourceStatus.IconAction}. 318 */ getReplaceLockScreenIconAction()319 public static boolean getReplaceLockScreenIconAction() { 320 return getBoolean(PROPERTY_REPLACE_LOCK_SCREEN_ICON_ACTION, true); 321 } 322 323 /** 324 * Returns the time for which Safety Center will wait for a source to respond to a resolving 325 * action before timing out. 326 */ getResolvingActionTimeout()327 static Duration getResolvingActionTimeout() { 328 return getDuration( 329 PROPERTY_RESOLVING_ACTION_TIMEOUT_MILLIS, 330 RESOLVING_ACTION_TIMEOUT_DEFAULT_DURATION); 331 } 332 333 /** 334 * Returns the time for which an app, upon receiving a Safety Center refresh broadcast, will be 335 * placed on a temporary power allowlist allowing it to start a foreground service from the 336 * background. 337 */ getFgsAllowlistDuration()338 static Duration getFgsAllowlistDuration() { 339 return getDuration(PROPERTY_FGS_ALLOWLIST_DURATION_MILLIS, FGS_ALLOWLIST_DEFAULT_DURATION); 340 } 341 342 /** 343 * Returns the IDs of sources that should not be tracked, for example because they are 344 * mid-rollout. Broadcasts are still sent to these sources. 345 */ getUntrackedSourceIds()346 static ArraySet<String> getUntrackedSourceIds() { 347 return getCommaSeparatedStrings(PROPERTY_UNTRACKED_SOURCES, sUntrackedSourcesDefault); 348 } 349 350 /** 351 * Returns the IDs of sources that should only be refreshed when Safety Center is on screen. We 352 * will refresh these sources only on page open and when the scan button is clicked. 353 */ getBackgroundRefreshDeniedSourceIds()354 static ArraySet<String> getBackgroundRefreshDeniedSourceIds() { 355 return getCommaSeparatedStrings( 356 PROPERTY_BACKGROUND_REFRESH_DENIED_SOURCES, sBackgroundRefreshDenyDefault); 357 } 358 359 /** 360 * Returns the time for which a Safety Center refresh is allowed to wait for a source to respond 361 * to a refresh request before timing out and marking the refresh as completed, based on the 362 * reason for the refresh. 363 */ getRefreshSourcesTimeout(@efreshReason int refreshReason)364 static Duration getRefreshSourcesTimeout(@RefreshReason int refreshReason) { 365 String refreshSourcesTimeouts = getRefreshSourcesTimeoutsMillis(); 366 Long timeout = getLongValueFromStringMapping(refreshSourcesTimeouts, refreshReason); 367 if (timeout != null) { 368 return Duration.ofMillis(timeout); 369 } 370 return REFRESH_SOURCES_TIMEOUT_DEFAULT_DURATION; 371 } 372 373 /** 374 * Returns a comma-delimited list of colon-delimited pairs where the left value is a {@link 375 * RefreshReason} and the right value is the refresh timeout applied for each source in case of 376 * a refresh. 377 */ getRefreshSourcesTimeoutsMillis()378 private static String getRefreshSourcesTimeoutsMillis() { 379 return getString(PROPERTY_REFRESH_SOURCES_TIMEOUTS_MILLIS, REFRESH_SOURCES_TIMEOUT_DEFAULT); 380 } 381 382 /** 383 * Returns the number of times an issue of the given {@link SafetySourceData.SeverityLevel} 384 * should be resurfaced. 385 */ getResurfaceIssueMaxCount( @afetySourceData.SeverityLevel int severityLevel)386 public static long getResurfaceIssueMaxCount( 387 @SafetySourceData.SeverityLevel int severityLevel) { 388 String maxCountsConfigString = getResurfaceIssueMaxCounts(); 389 Long maxCount = getLongValueFromStringMapping(maxCountsConfigString, severityLevel); 390 if (maxCount != null) { 391 return maxCount; 392 } 393 return RESURFACE_ISSUE_MAX_COUNT_DEFAULT_COUNT; 394 } 395 396 /** 397 * Returns a comma-delimited list of colon-delimited pairs where the left value is an issue 398 * {@link SafetySourceData.SeverityLevel} and the right value is the number of times an issue of 399 * this {@link SafetySourceData.SeverityLevel} should be resurfaced. 400 */ getResurfaceIssueMaxCounts()401 private static String getResurfaceIssueMaxCounts() { 402 return getString(PROPERTY_RESURFACE_ISSUE_MAX_COUNTS, RESURFACE_ISSUE_MAX_COUNT_DEFAULT); 403 } 404 405 /** 406 * Returns the time after which a dismissed issue of the given {@link 407 * SafetySourceData.SeverityLevel} will resurface if it has not reached the maximum count for 408 * which a dismissed issue of the given {@link SafetySourceData.SeverityLevel} should be 409 * resurfaced. 410 */ getResurfaceIssueDelay( @afetySourceData.SeverityLevel int severityLevel)411 public static Duration getResurfaceIssueDelay( 412 @SafetySourceData.SeverityLevel int severityLevel) { 413 String delaysConfigString = getResurfaceIssueDelaysMillis(); 414 Long delayMillis = getLongValueFromStringMapping(delaysConfigString, severityLevel); 415 if (delayMillis != null) { 416 return Duration.ofMillis(delayMillis); 417 } 418 return RESURFACE_ISSUE_DELAYS_DEFAULT_DURATION; 419 } 420 421 /** 422 * Returns a comma-delimited list of colon-delimited pairs where the left value is an issue 423 * {@link SafetySourceData.SeverityLevel} and the right value is the time after which a 424 * dismissed issue of this safety source severity level will resurface if it has not reached the 425 * maximum count for which a dismissed issue of this {@link SafetySourceData.SeverityLevel} 426 * should be resurfaced. 427 */ getResurfaceIssueDelaysMillis()428 private static String getResurfaceIssueDelaysMillis() { 429 return getString(PROPERTY_RESURFACE_ISSUE_DELAYS_MILLIS, RESURFACE_ISSUE_DELAYS_DEFAULT); 430 } 431 432 /** 433 * Returns a comma-delimited list of colon-delimited pairs of SourceId:ActionId. The action IDs 434 * listed by this flag should have their {@code PendingIntent}s overridden with the source's 435 * default intent drawn from Safety Center's config file, if available. 436 */ getActionsToOverrideWithDefaultIntent()437 private static String getActionsToOverrideWithDefaultIntent() { 438 return getString( 439 PROPERTY_ACTIONS_TO_OVERRIDE_WITH_DEFAULT_INTENT, 440 sActionsToOverrideWithDefaultIntentDefault); 441 } 442 443 /** Returns a duration after which a temporarily hidden issue will resurface. */ getTemporarilyHiddenIssueResurfaceDelay()444 public static Duration getTemporarilyHiddenIssueResurfaceDelay() { 445 return getDuration( 446 PROPERTY_TEMP_HIDDEN_ISSUE_RESURFACE_DELAY_MILLIS, 447 TEMP_HIDDEN_ISSUE_RESURFACE_DELAY_DEFAULT_DURATION); 448 } 449 450 /** 451 * Returns whether a safety source is allowed to send issues for the given {@link 452 * SafetySourceIssue.IssueCategory}. 453 */ isIssueCategoryAllowedForSource( @afetySourceIssue.IssueCategory int issueCategory, String safetySourceId)454 public static boolean isIssueCategoryAllowedForSource( 455 @SafetySourceIssue.IssueCategory int issueCategory, String safetySourceId) { 456 List<String> allowlist = 457 getStringListValueFromStringMapping( 458 getIssueCategoryAllowlists(), Integer.toString(issueCategory)); 459 return allowlist.isEmpty() || allowlist.contains(safetySourceId); 460 } 461 462 /** Returns a set of package certificates allowlisted for the given package name. */ getAdditionalAllowedPackageCerts(String packageName)463 public static ArraySet<String> getAdditionalAllowedPackageCerts(String packageName) { 464 String property = getAdditionalAllowedPackageCertsString(); 465 String allowlistedCertString = getStringValueFromStringMapping(property, packageName); 466 if (allowlistedCertString == null) { 467 return new ArraySet<>(); 468 } 469 return new ArraySet<>(allowlistedCertString.split("\\|")); 470 } 471 472 /** 473 * Returns a comma-delimited list of colon-delimited pairs where the left value is an issue 474 * {@link SafetySourceIssue.IssueCategory} and the right value is a vertical-bar-delimited list 475 * of IDs of safety sources that are allowed to send issues with this category. 476 */ getIssueCategoryAllowlists()477 private static String getIssueCategoryAllowlists() { 478 return getString(PROPERTY_ISSUE_CATEGORY_ALLOWLISTS, sIssueCategoryAllowlistDefault); 479 } 480 getAdditionalAllowedPackageCertsString()481 private static String getAdditionalAllowedPackageCertsString() { 482 return getString(PROPERTY_ADDITIONAL_ALLOW_PACKAGE_CERTS, ""); 483 } 484 485 /** Returns whether we allow statsd logging. */ getAllowStatsdLogging()486 public static boolean getAllowStatsdLogging() { 487 return getBoolean(PROPERTY_ALLOW_STATSD_LOGGING, true); 488 } 489 490 /** 491 * Returns a list of action IDs that should be overridden with the source's default intent drawn 492 * from the config for a given source. 493 */ getActionsToOverrideWithDefaultIntentForSource( String safetySourceId)494 public static List<String> getActionsToOverrideWithDefaultIntentForSource( 495 String safetySourceId) { 496 return getStringListValueFromStringMapping( 497 getActionsToOverrideWithDefaultIntent(), safetySourceId); 498 } 499 500 /** 501 * Returns whether to show subpages in the Safety Center UI for Android-U instead of the 502 * expand-and-collapse list implementation. 503 */ getShowSubpages()504 static boolean getShowSubpages() { 505 return SdkLevel.isAtLeastU() && getBoolean(PROPERTY_SHOW_SUBPAGES, true); 506 } 507 508 /** 509 * Returns an array of safety source Ids that will be refreshed on page open, even if 510 * refreshOnPageOpenAllowed is false (the default) in the XML config. 511 */ getOverrideRefreshOnPageOpenSourceIds()512 static ArraySet<String> getOverrideRefreshOnPageOpenSourceIds() { 513 return getCommaSeparatedStrings( 514 PROPERTY_OVERRIDE_REFRESH_ON_PAGE_OPEN_SOURCES, sRefreshOnPageOpenSourcesDefault); 515 } 516 getDuration(String property, Duration defaultValue)517 private static Duration getDuration(String property, Duration defaultValue) { 518 return Duration.ofMillis(getLong(property, defaultValue.toMillis())); 519 } 520 getBoolean(String property, boolean defaultValue)521 private static boolean getBoolean(String property, boolean defaultValue) { 522 // This call requires the READ_DEVICE_CONFIG permission. 523 final long callingId = Binder.clearCallingIdentity(); 524 try { 525 return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, property, defaultValue); 526 } finally { 527 Binder.restoreCallingIdentity(callingId); 528 } 529 } 530 getLong(String property, long defaultValue)531 private static long getLong(String property, long defaultValue) { 532 // This call requires the READ_DEVICE_CONFIG permission. 533 final long callingId = Binder.clearCallingIdentity(); 534 try { 535 return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY, property, defaultValue); 536 } finally { 537 Binder.restoreCallingIdentity(callingId); 538 } 539 } 540 getCommaSeparatedStrings(String property)541 private static ArraySet<String> getCommaSeparatedStrings(String property) { 542 return getCommaSeparatedStrings(property, ""); 543 } 544 getCommaSeparatedStrings(String property, String defaultValue)545 private static ArraySet<String> getCommaSeparatedStrings(String property, String defaultValue) { 546 return new ArraySet<>(getString(property, defaultValue).split(",")); 547 } 548 getString(String property, String defaultValue)549 private static String getString(String property, String defaultValue) { 550 // This call requires the READ_DEVICE_CONFIG permission. 551 final long callingId = Binder.clearCallingIdentity(); 552 try { 553 return DeviceConfig.getString(DeviceConfig.NAMESPACE_PRIVACY, property, defaultValue); 554 } finally { 555 Binder.restoreCallingIdentity(callingId); 556 } 557 } 558 559 /** 560 * Gets a long value for the provided integer key in a comma separated list of colon separated 561 * pairs of integers and longs. 562 */ 563 @Nullable getLongValueFromStringMapping(String mapping, int key)564 private static Long getLongValueFromStringMapping(String mapping, int key) { 565 String valueString = getStringValueFromStringMapping(mapping, key); 566 if (valueString == null) { 567 return null; 568 } 569 try { 570 return Long.parseLong(valueString); 571 } catch (NumberFormatException e) { 572 Log.w(TAG, "Badly formatted string mapping: " + mapping, e); 573 return null; 574 } 575 } 576 577 /** 578 * Gets a value for the provided integer key in a comma separated list of colon separated pairs 579 * of integers and strings. 580 */ 581 @Nullable getStringValueFromStringMapping(String mapping, int key)582 private static String getStringValueFromStringMapping(String mapping, int key) { 583 return getStringValueFromStringMapping(mapping, Integer.toString(key)); 584 } 585 586 /** 587 * Gets a value for the provided key in a comma separated list of colon separated key-value 588 * string pairs. 589 */ 590 @Nullable getStringValueFromStringMapping(String mapping, String key)591 private static String getStringValueFromStringMapping(String mapping, String key) { 592 if (mapping.isEmpty()) { 593 return null; 594 } 595 String[] pairsList = mapping.split(","); 596 for (int i = 0; i < pairsList.length; i++) { 597 String[] pair = pairsList[i].split(":", -1 /* allow trailing empty strings */); 598 if (pair.length != 2) { 599 Log.w(TAG, "Badly formatted string mapping: " + mapping); 600 continue; 601 } 602 if (pair[0].equals(key)) { 603 return pair[1]; 604 } 605 } 606 return null; 607 } 608 getStringListValueFromStringMapping(String mapping, String key)609 private static List<String> getStringListValueFromStringMapping(String mapping, String key) { 610 String value = getStringValueFromStringMapping(mapping, key); 611 if (value == null) { 612 return Collections.emptyList(); 613 } 614 615 return Arrays.asList(value.split("\\|")); 616 } 617 SafetyCenterFlags()618 private SafetyCenterFlags() {} 619 } 620