1 /* 2 * Copyright (C) 2021 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.Manifest.permission.MANAGE_SAFETY_CENTER; 20 import static android.Manifest.permission.READ_SAFETY_CENTER_STATUS; 21 import static android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE; 22 import static android.Manifest.permission.START_TASKS_FROM_RECENTS; 23 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 24 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 25 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_LOCALE_CHANGE; 26 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER; 27 import static android.safetycenter.SafetyCenterManager.RefreshReason; 28 import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED; 29 import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED; 30 31 import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__TIMEOUT; 32 import static com.android.permission.PermissionStatsLog.SAFETY_STATE; 33 import static com.android.safetycenter.SafetyCenterFlags.PROPERTY_SAFETY_CENTER_ENABLED; 34 import static com.android.safetycenter.internaldata.SafetyCenterIds.toUserFriendlyString; 35 36 import static java.util.Objects.requireNonNull; 37 38 import android.annotation.UserIdInt; 39 import android.app.ActivityManager; 40 import android.app.PendingIntent; 41 import android.app.StatsManager; 42 import android.content.BroadcastReceiver; 43 import android.content.Context; 44 import android.content.Intent; 45 import android.content.IntentFilter; 46 import android.content.pm.PackageManager; 47 import android.content.pm.PackageManager.NameNotFoundException; 48 import android.content.pm.PackageManager.PackageInfoFlags; 49 import android.content.res.Resources; 50 import android.os.Binder; 51 import android.os.ParcelFileDescriptor; 52 import android.os.Process; 53 import android.os.UserHandle; 54 import android.provider.DeviceConfig; 55 import android.provider.DeviceConfig.OnPropertiesChangedListener; 56 import android.safetycenter.IOnSafetyCenterDataChangedListener; 57 import android.safetycenter.ISafetyCenterManager; 58 import android.safetycenter.SafetyCenterData; 59 import android.safetycenter.SafetyCenterErrorDetails; 60 import android.safetycenter.SafetyCenterManager; 61 import android.safetycenter.SafetyEvent; 62 import android.safetycenter.SafetySourceData; 63 import android.safetycenter.SafetySourceErrorDetails; 64 import android.safetycenter.SafetySourceIssue; 65 import android.safetycenter.config.SafetyCenterConfig; 66 import android.text.TextUtils; 67 import android.util.ArraySet; 68 import android.util.Log; 69 70 import androidx.annotation.Keep; 71 import androidx.annotation.Nullable; 72 import androidx.annotation.RequiresApi; 73 74 import com.android.internal.annotations.GuardedBy; 75 import com.android.modules.utils.BackgroundThread; 76 import com.android.modules.utils.build.SdkLevel; 77 import com.android.permission.flags.Flags; 78 import com.android.permission.util.ForegroundThread; 79 import com.android.permission.util.UserUtils; 80 import com.android.safetycenter.data.SafetyCenterDataManager; 81 import com.android.safetycenter.data.SafetyEventFix; 82 import com.android.safetycenter.data.SafetySourceDataFix; 83 import com.android.safetycenter.internaldata.SafetyCenterIds; 84 import com.android.safetycenter.internaldata.SafetyCenterIssueActionId; 85 import com.android.safetycenter.internaldata.SafetyCenterIssueId; 86 import com.android.safetycenter.internaldata.SafetyCenterIssueKey; 87 import com.android.safetycenter.logging.SafetyCenterPullAtomCallback; 88 import com.android.safetycenter.notifications.SafetyCenterNotificationChannels; 89 import com.android.safetycenter.notifications.SafetyCenterNotificationReceiver; 90 import com.android.safetycenter.notifications.SafetyCenterNotificationSender; 91 import com.android.safetycenter.pendingintents.PendingIntentSender; 92 import com.android.safetycenter.resources.SafetyCenterResourcesApk; 93 import com.android.server.SystemService; 94 95 import java.io.FileDescriptor; 96 import java.io.PrintWriter; 97 import java.util.Arrays; 98 import java.util.List; 99 100 /** 101 * Service for the safety center. 102 * 103 * @hide 104 */ 105 @Keep 106 public final class SafetyCenterService extends SystemService { 107 108 private static final String TAG = "SafetyCenterService"; 109 110 private final ApiLock mApiLock = new ApiLock(); 111 112 @GuardedBy("mApiLock") 113 private final SafetyCenterTimeouts mSafetyCenterTimeouts = new SafetyCenterTimeouts(); 114 115 @GuardedBy("mApiLock") 116 private final SafetyCenterResourcesApk mSafetyCenterResourcesApk; 117 118 @GuardedBy("mApiLock") 119 private final SafetyCenterConfigReader mSafetyCenterConfigReader; 120 121 @GuardedBy("mApiLock") 122 private final SafetyCenterRefreshTracker mSafetyCenterRefreshTracker; 123 124 private final SafetySourceDataFix mSafetySourceDataFix; 125 126 @GuardedBy("mApiLock") 127 private final SafetyCenterDataManager mSafetyCenterDataManager; 128 129 @GuardedBy("mApiLock") 130 private final SafetyCenterDataFactory mSafetyCenterDataFactory; 131 132 @GuardedBy("mApiLock") 133 private final SafetyCenterListeners mSafetyCenterListeners; 134 135 @GuardedBy("mApiLock") 136 private final SafetyCenterNotificationChannels mNotificationChannels; 137 138 @GuardedBy("mApiLock") 139 private final SafetyCenterNotificationSender mNotificationSender; 140 141 @GuardedBy("mApiLock") 142 private final SafetyCenterBroadcastDispatcher mSafetyCenterBroadcastDispatcher; 143 144 @GuardedBy("mApiLock") 145 private final SafetyCenterDataChangeNotifier mSafetyCenterDataChangeNotifier; 146 147 private final boolean mDeviceSupportsSafetyCenter; 148 149 /** Whether the {@link SafetyCenterConfig} was successfully loaded. */ 150 private volatile boolean mConfigAvailable = false; 151 SafetyCenterService(Context context)152 public SafetyCenterService(Context context) { 153 super(context); 154 mSafetyCenterResourcesApk = new SafetyCenterResourcesApk(context); 155 mSafetyCenterConfigReader = new SafetyCenterConfigReader(mSafetyCenterResourcesApk); 156 mSafetyCenterRefreshTracker = new SafetyCenterRefreshTracker(context); 157 PendingIntentFactory pendingIntentFactory = 158 new PendingIntentFactory(context, mSafetyCenterResourcesApk); 159 mSafetySourceDataFix = 160 new SafetySourceDataFix(context, pendingIntentFactory, mSafetyCenterConfigReader); 161 mSafetyCenterDataManager = 162 new SafetyCenterDataManager( 163 context, mSafetyCenterConfigReader, mSafetyCenterRefreshTracker, mApiLock); 164 mSafetyCenterDataFactory = 165 new SafetyCenterDataFactory( 166 context, 167 mSafetyCenterResourcesApk, 168 mSafetyCenterConfigReader, 169 mSafetyCenterRefreshTracker, 170 pendingIntentFactory, 171 mSafetyCenterDataManager); 172 mSafetyCenterListeners = new SafetyCenterListeners(mSafetyCenterDataFactory); 173 mNotificationChannels = new SafetyCenterNotificationChannels(mSafetyCenterResourcesApk); 174 mNotificationSender = 175 SafetyCenterNotificationSender.newInstance( 176 context, 177 mSafetyCenterResourcesApk, 178 mNotificationChannels, 179 mSafetyCenterDataManager); 180 mSafetyCenterBroadcastDispatcher = 181 new SafetyCenterBroadcastDispatcher( 182 context, 183 mSafetyCenterConfigReader, 184 mSafetyCenterRefreshTracker, 185 mSafetyCenterDataManager); 186 mSafetyCenterDataChangeNotifier = 187 new SafetyCenterDataChangeNotifier(mNotificationSender, mSafetyCenterListeners); 188 mDeviceSupportsSafetyCenter = 189 context.getResources() 190 .getBoolean( 191 Resources.getSystem() 192 .getIdentifier( 193 "config_enableSafetyCenter", "bool", "android")); 194 } 195 196 @Override onStart()197 public void onStart() { 198 publishBinderService(Context.SAFETY_CENTER_SERVICE, new Stub()); 199 if (!mDeviceSupportsSafetyCenter) { 200 Log.i(TAG, "Device does not support Safety Center, it will be disabled"); 201 return; 202 } 203 204 synchronized (mApiLock) { 205 boolean safetyCenterResourcesInitialized = mSafetyCenterResourcesApk.init(); 206 if (!safetyCenterResourcesInitialized) { 207 Log.e(TAG, "Cannot init Safety Center resources, Safety Center will be disabled"); 208 return; 209 } 210 211 SafetyCenterFlags.init(mSafetyCenterResourcesApk); 212 213 if (!mSafetyCenterConfigReader.loadConfig()) { 214 Log.e(TAG, "Cannot init Safety Center config, Safety Center will be disabled"); 215 return; 216 } 217 218 mConfigAvailable = true; 219 mSafetyCenterDataManager.loadPersistableDataStateFromFile(); 220 new UserBroadcastReceiver().register(getContext()); 221 new SafetyCenterNotificationReceiver( 222 /* service= */ this, 223 mSafetyCenterDataManager, 224 mSafetyCenterDataChangeNotifier, 225 mApiLock) 226 .register(getContext()); 227 new LocaleBroadcastReceiver().register(getContext()); 228 } 229 } 230 231 @Override onBootPhase(int phase)232 public void onBootPhase(int phase) { 233 if (phase != SystemService.PHASE_BOOT_COMPLETED || !canUseSafetyCenter()) { 234 return; 235 } 236 237 SafetyCenterPullAtomCallback pullAtomCallback; 238 synchronized (mApiLock) { 239 registerSafetyCenterEnabledListenerLocked(); 240 pullAtomCallback = newSafetyCenterPullAtomCallbackLocked(); 241 } 242 registerSafetyCenterPullAtomCallback(pullAtomCallback); 243 } 244 245 @GuardedBy("mApiLock") registerSafetyCenterEnabledListenerLocked()246 private void registerSafetyCenterEnabledListenerLocked() { 247 if (Flags.safetyCenterEnabledNoDeviceConfig() && SdkLevel.isAtLeastU()) { 248 return; 249 } 250 SafetyCenterEnabledListener safetyCenterEnabledListener = new SafetyCenterEnabledListener(); 251 DeviceConfig.addOnPropertiesChangedListener( 252 DeviceConfig.NAMESPACE_PRIVACY, 253 ForegroundThread.getExecutor(), 254 safetyCenterEnabledListener); 255 // Set the initial state *after* registering the listener, in the unlikely event that the 256 // flag changes between creating the listener and registering it (in which case we could 257 // miss an update and end up with an inconsistent state). 258 setInitialStateLocked(safetyCenterEnabledListener); 259 } 260 261 @GuardedBy("mApiLock") 262 @SuppressWarnings("GuardedBy") 263 // @GuardedBy is unable to infer that the `SafetyCenterService.this.mApiLock` in 264 // `SafetyCenterService` is the same as the one in `SafetyCenterEnabledListener` here, so it 265 // has to be suppressed. setInitialStateLocked(SafetyCenterEnabledListener safetyCenterEnabledListener)266 private void setInitialStateLocked(SafetyCenterEnabledListener safetyCenterEnabledListener) { 267 safetyCenterEnabledListener.setInitialStateLocked(); 268 } 269 270 @GuardedBy("mApiLock") newSafetyCenterPullAtomCallbackLocked()271 private SafetyCenterPullAtomCallback newSafetyCenterPullAtomCallbackLocked() { 272 return new SafetyCenterPullAtomCallback( 273 getContext(), 274 mApiLock, 275 mSafetyCenterConfigReader, 276 mSafetyCenterDataFactory, 277 mSafetyCenterDataManager); 278 } 279 registerSafetyCenterPullAtomCallback( SafetyCenterPullAtomCallback pullAtomCallback)280 private void registerSafetyCenterPullAtomCallback( 281 SafetyCenterPullAtomCallback pullAtomCallback) { 282 StatsManager statsManager = 283 requireNonNull(getContext().getSystemService(StatsManager.class)); 284 statsManager.setPullAtomCallback( 285 SAFETY_STATE, 286 /* metadata= */ null, 287 BackgroundThread.getExecutor(), 288 pullAtomCallback); 289 } 290 291 /** Service implementation of {@link ISafetyCenterManager.Stub}. */ 292 private final class Stub extends ISafetyCenterManager.Stub { 293 @Override isSafetyCenterEnabled()294 public boolean isSafetyCenterEnabled() { 295 enforceAnyCallingOrSelfPermissions( 296 "isSafetyCenterEnabled", READ_SAFETY_CENTER_STATUS, SEND_SAFETY_CENTER_UPDATE); 297 298 return isApiEnabled(); 299 } 300 301 @Override setSafetySourceData( String safetySourceId, @Nullable SafetySourceData safetySourceData, SafetyEvent safetyEvent, String packageName, @UserIdInt int userId)302 public void setSafetySourceData( 303 String safetySourceId, 304 @Nullable SafetySourceData safetySourceData, 305 SafetyEvent safetyEvent, 306 String packageName, 307 @UserIdInt int userId) { 308 requireNonNull(safetySourceId); 309 requireNonNull(safetyEvent); 310 requireNonNull(packageName); 311 getContext() 312 .enforceCallingOrSelfPermission( 313 SEND_SAFETY_CENTER_UPDATE, "setSafetySourceData"); 314 if (!enforceCrossUserPermission("setSafetySourceData", userId) 315 || !enforcePackage(Binder.getCallingUid(), packageName, userId) 316 || !checkApiEnabled("setSafetySourceData")) { 317 return; 318 } 319 320 UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(getContext(), userId); 321 synchronized (mApiLock) { 322 safetySourceData = 323 mSafetySourceDataFix.maybeOverrideSafetySourceData( 324 safetySourceId, safetySourceData, packageName, userId); 325 safetyEvent = 326 SafetyEventFix.maybeOverrideSafetyEvent( 327 mSafetyCenterDataManager, 328 safetySourceId, 329 safetySourceData, 330 safetyEvent, 331 userId); 332 boolean hasUpdate = 333 mSafetyCenterDataManager.setSafetySourceData( 334 safetySourceData, safetySourceId, safetyEvent, packageName, userId); 335 if (hasUpdate) { 336 // When an action is successfully resolved, call notifyActionSuccess before 337 // updateDataConsumers: Calling the former first will turn any notification for 338 // the resolved issue into a success notification, whereas calling the latter 339 // will simply clear any issue notification and no success message will show. 340 if (safetyEvent.getType() == SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED) { 341 mNotificationSender.notifyActionSuccess( 342 safetySourceId, safetyEvent, userId); 343 } 344 mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroup, userId); 345 } 346 } 347 } 348 349 @Override 350 @Nullable getSafetySourceData( String safetySourceId, String packageName, @UserIdInt int userId)351 public SafetySourceData getSafetySourceData( 352 String safetySourceId, String packageName, @UserIdInt int userId) { 353 requireNonNull(safetySourceId); 354 requireNonNull(packageName); 355 enforceAnyCallingOrSelfPermissions( 356 "getSafetySourceData", SEND_SAFETY_CENTER_UPDATE, MANAGE_SAFETY_CENTER); 357 if (!enforceCrossUserPermission("getSafetySourceData", userId) 358 || !enforcePackage(Binder.getCallingUid(), packageName, userId) 359 || !checkApiEnabled("getSafetySourceData")) { 360 return null; 361 } 362 363 synchronized (mApiLock) { 364 return mSafetyCenterDataManager.getSafetySourceData( 365 safetySourceId, packageName, userId); 366 } 367 } 368 369 @Override reportSafetySourceError( String safetySourceId, SafetySourceErrorDetails errorDetails, String packageName, @UserIdInt int userId)370 public void reportSafetySourceError( 371 String safetySourceId, 372 SafetySourceErrorDetails errorDetails, 373 String packageName, 374 @UserIdInt int userId) { 375 requireNonNull(safetySourceId); 376 requireNonNull(errorDetails); 377 requireNonNull(packageName); 378 getContext() 379 .enforceCallingOrSelfPermission( 380 SEND_SAFETY_CENTER_UPDATE, "reportSafetySourceError"); 381 if (!enforceCrossUserPermission("reportSafetySourceError", userId) 382 || !enforcePackage(Binder.getCallingUid(), packageName, userId) 383 || !checkApiEnabled("reportSafetySourceError")) { 384 return; 385 } 386 387 UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(getContext(), userId); 388 synchronized (mApiLock) { 389 boolean hasUpdate = 390 mSafetyCenterDataManager.reportSafetySourceError( 391 errorDetails, safetySourceId, packageName, userId); 392 SafetyCenterErrorDetails safetyCenterErrorDetails = null; 393 if (hasUpdate 394 && errorDetails.getSafetyEvent().getType() 395 == SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) { 396 safetyCenterErrorDetails = 397 new SafetyCenterErrorDetails( 398 mSafetyCenterResourcesApk.getStringByName( 399 "resolving_action_error")); 400 } 401 if (hasUpdate) { 402 mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroup, userId); 403 } 404 if (safetyCenterErrorDetails != null) { 405 mSafetyCenterListeners.deliverErrorForUserProfileGroup( 406 userProfileGroup, safetyCenterErrorDetails); 407 } 408 } 409 } 410 411 @Override refreshSafetySources(@efreshReason int refreshReason, @UserIdInt int userId)412 public void refreshSafetySources(@RefreshReason int refreshReason, @UserIdInt int userId) { 413 RefreshReasons.validate(refreshReason); 414 getContext().enforceCallingPermission(MANAGE_SAFETY_CENTER, "refreshSafetySources"); 415 if (!enforceCrossUserPermission("refreshSafetySources", userId) 416 || !checkApiEnabled("refreshSafetySources")) { 417 return; 418 } 419 420 synchronized (mApiLock) { 421 startRefreshingSafetySourcesLocked(refreshReason, userId); 422 } 423 } 424 425 @Override 426 @RequiresApi(UPSIDE_DOWN_CAKE) refreshSpecificSafetySources( @efreshReason int refreshReason, @UserIdInt int userId, List<String> safetySourceIds)427 public void refreshSpecificSafetySources( 428 @RefreshReason int refreshReason, 429 @UserIdInt int userId, 430 List<String> safetySourceIds) { 431 requireNonNull(safetySourceIds, "safetySourceIds cannot be null"); 432 RefreshReasons.validate(refreshReason); 433 getContext() 434 .enforceCallingPermission(MANAGE_SAFETY_CENTER, "refreshSpecificSafetySources"); 435 if (!enforceCrossUserPermission("refreshSpecificSafetySources", userId) 436 || !checkApiEnabled("refreshSpecificSafetySources")) { 437 return; 438 } 439 440 synchronized (mApiLock) { 441 startRefreshingSafetySourcesLocked(refreshReason, userId, safetySourceIds); 442 } 443 } 444 445 @Override 446 @Nullable getSafetyCenterConfig()447 public SafetyCenterConfig getSafetyCenterConfig() { 448 getContext() 449 .enforceCallingOrSelfPermission(MANAGE_SAFETY_CENTER, "getSafetyCenterConfig"); 450 // We still return the SafetyCenterConfig object when the API is disabled, as Settings 451 // search works by adding all the entries very rarely (and relies on filtering them out 452 // instead). 453 if (!canUseSafetyCenter()) { 454 Log.i(TAG, "Called getSafetyCenterConfig, but Safety Center is not supported"); 455 return null; 456 } 457 458 synchronized (mApiLock) { 459 return mSafetyCenterConfigReader.getSafetyCenterConfig(); 460 } 461 } 462 463 @Override getSafetyCenterData(String packageName, @UserIdInt int userId)464 public SafetyCenterData getSafetyCenterData(String packageName, @UserIdInt int userId) { 465 requireNonNull(packageName); 466 getContext() 467 .enforceCallingOrSelfPermission(MANAGE_SAFETY_CENTER, "getSafetyCenterData"); 468 if (!enforceCrossUserPermission("getSafetyCenterData", userId) 469 || !enforcePackage(Binder.getCallingUid(), packageName, userId) 470 || !checkApiEnabled("getSafetyCenterData")) { 471 return SafetyCenterDataFactory.getDefaultSafetyCenterData(); 472 } 473 474 UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(getContext(), userId); 475 synchronized (mApiLock) { 476 return mSafetyCenterDataFactory.assembleSafetyCenterData( 477 packageName, userProfileGroup); 478 } 479 } 480 481 @Override addOnSafetyCenterDataChangedListener( IOnSafetyCenterDataChangedListener listener, String packageName, @UserIdInt int userId)482 public void addOnSafetyCenterDataChangedListener( 483 IOnSafetyCenterDataChangedListener listener, 484 String packageName, 485 @UserIdInt int userId) { 486 requireNonNull(listener); 487 requireNonNull(packageName); 488 getContext() 489 .enforceCallingOrSelfPermission( 490 MANAGE_SAFETY_CENTER, "addOnSafetyCenterDataChangedListener"); 491 if (!enforceCrossUserPermission("addOnSafetyCenterDataChangedListener", userId) 492 || !enforcePackage(Binder.getCallingUid(), packageName, userId) 493 || !checkApiEnabled("addOnSafetyCenterDataChangedListener")) { 494 return; 495 } 496 497 UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(getContext(), userId); 498 synchronized (mApiLock) { 499 IOnSafetyCenterDataChangedListener registeredListener = 500 mSafetyCenterListeners.addListener(listener, packageName, userId); 501 if (registeredListener == null) { 502 return; 503 } 504 SafetyCenterListeners.deliverDataForListener( 505 registeredListener, 506 mSafetyCenterDataFactory.assembleSafetyCenterData( 507 packageName, userProfileGroup)); 508 } 509 } 510 511 @Override removeOnSafetyCenterDataChangedListener( IOnSafetyCenterDataChangedListener listener, @UserIdInt int userId)512 public void removeOnSafetyCenterDataChangedListener( 513 IOnSafetyCenterDataChangedListener listener, @UserIdInt int userId) { 514 requireNonNull(listener); 515 getContext() 516 .enforceCallingOrSelfPermission( 517 MANAGE_SAFETY_CENTER, "removeOnSafetyCenterDataChangedListener"); 518 if (!enforceCrossUserPermission("removeOnSafetyCenterDataChangedListener", userId) 519 || !checkApiEnabled("removeOnSafetyCenterDataChangedListener")) { 520 return; 521 } 522 523 synchronized (mApiLock) { 524 mSafetyCenterListeners.removeListener(listener, userId); 525 } 526 } 527 528 @Override dismissSafetyCenterIssue(String issueId, @UserIdInt int userId)529 public void dismissSafetyCenterIssue(String issueId, @UserIdInt int userId) { 530 requireNonNull(issueId); 531 getContext() 532 .enforceCallingOrSelfPermission( 533 MANAGE_SAFETY_CENTER, "dismissSafetyCenterIssue"); 534 if (!enforceCrossUserPermission("dismissSafetyCenterIssue", userId) 535 || !checkApiEnabled("dismissSafetyCenterIssue")) { 536 return; 537 } 538 539 SafetyCenterIssueId safetyCenterIssueId = SafetyCenterIds.issueIdFromString(issueId); 540 SafetyCenterIssueKey safetyCenterIssueKey = 541 safetyCenterIssueId.getSafetyCenterIssueKey(); 542 UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(getContext(), userId); 543 enforceSameUserProfileGroup( 544 "dismissSafetyCenterIssue", userProfileGroup, safetyCenterIssueKey.getUserId()); 545 synchronized (mApiLock) { 546 SafetySourceIssue safetySourceIssue = 547 mSafetyCenterDataManager.getSafetySourceIssue(safetyCenterIssueKey); 548 if (safetySourceIssue == null) { 549 Log.w(TAG, "Attempt to dismiss an issue that is not provided by the source"); 550 // Don't send the error to the UI here, since it could happen when clicking the 551 // button multiple times in a row (e.g. if the source is clearing the issue as a 552 // result of the onDismissPendingIntent). 553 return; 554 } 555 if (mSafetyCenterDataManager.isIssueDismissed( 556 safetyCenterIssueKey, safetySourceIssue.getSeverityLevel())) { 557 Log.w(TAG, "Attempt to dismiss an issue that is already dismissed"); 558 // Don't send the error to the UI here, since it could happen when clicking the 559 // button multiple times in a row. 560 return; 561 } 562 mSafetyCenterDataManager.dismissSafetyCenterIssue(safetyCenterIssueKey); 563 PendingIntent onDismissPendingIntent = 564 safetySourceIssue.getOnDismissPendingIntent(); 565 if (onDismissPendingIntent != null 566 && !dispatchPendingIntent(onDismissPendingIntent)) { 567 Log.w( 568 TAG, 569 "Error dispatching dismissal for issue: " 570 + safetyCenterIssueKey.getSafetySourceIssueId() 571 + ", of source: " 572 + safetyCenterIssueKey.getSafetySourceId()); 573 // We still consider the dismissal a success if there is an error dispatching 574 // the dismissal PendingIntent, since SafetyCenter won't surface this warning 575 // anymore. 576 } 577 mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroup, userId); 578 } 579 } 580 581 @Override executeSafetyCenterIssueAction( String issueId, String issueActionId, @UserIdInt int userId)582 public void executeSafetyCenterIssueAction( 583 String issueId, String issueActionId, @UserIdInt int userId) { 584 requireNonNull(issueId); 585 requireNonNull(issueActionId); 586 getContext() 587 .enforceCallingOrSelfPermission( 588 MANAGE_SAFETY_CENTER, "executeSafetyCenterIssueAction"); 589 if (!enforceCrossUserPermission("executeSafetyCenterIssueAction", userId) 590 || !checkApiEnabled("executeSafetyCenterIssueAction")) { 591 return; 592 } 593 594 SafetyCenterIssueId safetyCenterIssueId = SafetyCenterIds.issueIdFromString(issueId); 595 SafetyCenterIssueKey safetyCenterIssueKey = 596 safetyCenterIssueId.getSafetyCenterIssueKey(); 597 SafetyCenterIssueActionId safetyCenterIssueActionId = 598 SafetyCenterIds.issueActionIdFromString(issueActionId); 599 if (!safetyCenterIssueActionId.getSafetyCenterIssueKey().equals(safetyCenterIssueKey)) { 600 throw new IllegalArgumentException( 601 toUserFriendlyString(safetyCenterIssueId) 602 + " and " 603 + toUserFriendlyString(safetyCenterIssueActionId) 604 + " do not match"); 605 } 606 UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(getContext(), userId); 607 enforceSameUserProfileGroup( 608 "executeSafetyCenterIssueAction", 609 userProfileGroup, 610 safetyCenterIssueKey.getUserId()); 611 Integer taskId = 612 safetyCenterIssueId.hasTaskId() ? safetyCenterIssueId.getTaskId() : null; 613 executeIssueActionInternal(safetyCenterIssueActionId, userProfileGroup, taskId); 614 } 615 616 @Override clearAllSafetySourceDataForTests()617 public void clearAllSafetySourceDataForTests() { 618 getContext() 619 .enforceCallingOrSelfPermission( 620 MANAGE_SAFETY_CENTER, "clearAllSafetySourceDataForTests"); 621 if (!checkApiEnabled("clearAllSafetySourceDataForTests")) { 622 return; 623 } 624 625 List<UserProfileGroup> userProfileGroups = 626 UserProfileGroup.getAllUserProfileGroups(getContext()); 627 synchronized (mApiLock) { 628 // TODO(b/236693607): Should tests leave real data untouched? 629 clearDataLocked(); 630 mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroups); 631 } 632 } 633 634 @Override setSafetyCenterConfigForTests(SafetyCenterConfig safetyCenterConfig)635 public void setSafetyCenterConfigForTests(SafetyCenterConfig safetyCenterConfig) { 636 requireNonNull(safetyCenterConfig); 637 getContext() 638 .enforceCallingOrSelfPermission( 639 MANAGE_SAFETY_CENTER, "setSafetyCenterConfigForTests"); 640 if (!checkApiEnabled("setSafetyCenterConfigForTests")) { 641 return; 642 } 643 644 List<UserProfileGroup> userProfileGroups = 645 UserProfileGroup.getAllUserProfileGroups(getContext()); 646 synchronized (mApiLock) { 647 mSafetyCenterConfigReader.setConfigOverrideForTests(safetyCenterConfig); 648 // TODO(b/236693607): Should tests leave real data untouched? 649 clearDataLocked(); 650 mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroups); 651 } 652 } 653 654 @Override clearSafetyCenterConfigForTests()655 public void clearSafetyCenterConfigForTests() { 656 getContext() 657 .enforceCallingOrSelfPermission( 658 MANAGE_SAFETY_CENTER, "clearSafetyCenterConfigForTests"); 659 if (!checkApiEnabled("clearSafetyCenterConfigForTests")) { 660 return; 661 } 662 663 List<UserProfileGroup> userProfileGroups = 664 UserProfileGroup.getAllUserProfileGroups(getContext()); 665 synchronized (mApiLock) { 666 mSafetyCenterConfigReader.clearConfigOverrideForTests(); 667 // TODO(b/236693607): Should tests leave real data untouched? 668 clearDataLocked(); 669 mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroups); 670 } 671 } 672 isApiEnabled()673 private boolean isApiEnabled() { 674 return canUseSafetyCenter() && SafetyCenterFlags.getSafetyCenterEnabled(); 675 } 676 enforceAnyCallingOrSelfPermissions(String message, String... permissions)677 private void enforceAnyCallingOrSelfPermissions(String message, String... permissions) { 678 if (permissions.length == 0) { 679 throw new IllegalArgumentException("Must check at least one permission"); 680 } 681 for (int i = 0; i < permissions.length; i++) { 682 if (getContext().checkCallingOrSelfPermission(permissions[i]) 683 == PERMISSION_GRANTED) { 684 return; 685 } 686 } 687 throw new SecurityException( 688 message 689 + " requires any of: " 690 + Arrays.toString(permissions) 691 + ", but none were granted"); 692 } 693 694 /** Enforces cross user permission and returns whether the user is valid. */ enforceCrossUserPermission(String message, @UserIdInt int userId)695 private boolean enforceCrossUserPermission(String message, @UserIdInt int userId) { 696 UserUtils.enforceCrossUserPermission( 697 userId, /* allowAll= */ false, /* enforceForProfileGroup= */ false, message, 698 getContext()); 699 if (!UserUtils.isUserExistent(userId, getContext())) { 700 Log.w( 701 TAG, 702 "Called " 703 + message 704 + " with user id: " 705 + userId 706 + ", which does not correspond to an existing user"); 707 return false; 708 } 709 if (!UserProfileGroup.isSupported(userId, getContext())) { 710 Log.w( 711 TAG, 712 "Called " 713 + message 714 + " with user id: " 715 + userId 716 + ", which is an unsupported user"); 717 return false; 718 } 719 return true; 720 } 721 722 /** 723 * Returns {@code true} if the {@code packageName} exists and it belongs to the {@code 724 * callingUid}. 725 * 726 * <p>Throws a {@link SecurityException} if the {@code packageName} does not belong to the 727 * {@code callingUid}. 728 */ enforcePackage(int callingUid, String packageName, @UserIdInt int userId)729 private boolean enforcePackage(int callingUid, String packageName, @UserIdInt int userId) { 730 if (TextUtils.isEmpty(packageName)) { 731 throw new IllegalArgumentException("packageName may not be empty"); 732 } 733 int actualUid; 734 PackageManager packageManager = getContext().getPackageManager(); 735 try { 736 actualUid = 737 packageManager.getPackageUidAsUser( 738 packageName, PackageInfoFlags.of(0), userId); 739 } catch (NameNotFoundException e) { 740 Log.w(TAG, "Package: " + packageName + ", not found for user id: " + userId, e); 741 return false; 742 } 743 if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) { 744 return true; 745 } 746 if (UserHandle.getAppId(callingUid) != UserHandle.getAppId(actualUid)) { 747 throw new SecurityException( 748 "Package: " 749 + packageName 750 + ", does not belong to calling uid: " 751 + callingUid); 752 } 753 return true; 754 } 755 checkApiEnabled(String message)756 private boolean checkApiEnabled(String message) { 757 if (!isApiEnabled()) { 758 Log.w(TAG, "Called " + message + ", but Safety Center is disabled"); 759 return false; 760 } 761 return true; 762 } 763 enforceSameUserProfileGroup( String message, UserProfileGroup userProfileGroup, @UserIdInt int userId)764 private void enforceSameUserProfileGroup( 765 String message, UserProfileGroup userProfileGroup, @UserIdInt int userId) { 766 if (!userProfileGroup.contains(userId)) { 767 throw new SecurityException( 768 message 769 + " requires target user id " 770 + userId 771 + " to be within the same profile group of the caller: " 772 + userProfileGroup); 773 } 774 } 775 776 @Override handleShellCommand( ParcelFileDescriptor in, ParcelFileDescriptor out, ParcelFileDescriptor err, String[] args)777 public int handleShellCommand( 778 ParcelFileDescriptor in, 779 ParcelFileDescriptor out, 780 ParcelFileDescriptor err, 781 String[] args) { 782 return new SafetyCenterShellCommandHandler( 783 getContext(), 784 /* safetyCenterManager= */ this, 785 mDeviceSupportsSafetyCenter) 786 .exec( 787 /* target= */ this, 788 in.getFileDescriptor(), 789 out.getFileDescriptor(), 790 err.getFileDescriptor(), 791 args); 792 } 793 794 /** Dumps state for debugging purposes. */ 795 @Override dump(FileDescriptor fd, PrintWriter fout, @Nullable String[] args)796 protected void dump(FileDescriptor fd, PrintWriter fout, @Nullable String[] args) { 797 if (!checkDumpPermission(fout)) { 798 return; 799 } 800 List<String> subjects = Arrays.asList(args); 801 boolean all = subjects.isEmpty(); 802 synchronized (mApiLock) { 803 if (all || subjects.contains("service")) { 804 SafetyCenterService.this.dumpLocked(fout); 805 } 806 if (all || subjects.contains("flags")) { 807 SafetyCenterFlags.dump(fout); 808 } 809 if (all || subjects.contains("config")) { 810 mSafetyCenterConfigReader.dump(fout); 811 } 812 if (all || subjects.contains("data")) { 813 mSafetyCenterDataManager.dump(fd, fout); 814 } 815 if (all || subjects.contains("refresh")) { 816 mSafetyCenterRefreshTracker.dump(fout); 817 } 818 if (all || subjects.contains("timeouts")) { 819 mSafetyCenterTimeouts.dump(fout); 820 } 821 if (all || subjects.contains("listeners")) { 822 mSafetyCenterListeners.dump(fout); 823 } 824 if (all || subjects.contains("notifications")) { 825 mNotificationSender.dump(fout); 826 } 827 } 828 } 829 checkDumpPermission(PrintWriter writer)830 private boolean checkDumpPermission(PrintWriter writer) { 831 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 832 != PERMISSION_GRANTED) { 833 writer.println( 834 "Permission Denial: can't dump " 835 + "safety_center" 836 + " from from pid=" 837 + Binder.getCallingPid() 838 + ", uid=" 839 + Binder.getCallingUid() 840 + " due to missing " 841 + android.Manifest.permission.DUMP 842 + " permission"); 843 return false; 844 } else { 845 return true; 846 } 847 } 848 } 849 850 /** 851 * An {@link OnPropertiesChangedListener} for {@link 852 * SafetyCenterFlags#PROPERTY_SAFETY_CENTER_ENABLED} that sends broadcasts when the SafetyCenter 853 * property is enabled or disabled. 854 * 855 * <p>This listener assumes that the {@link SafetyCenterFlags#PROPERTY_SAFETY_CENTER_ENABLED} 856 * value maps to {@link SafetyCenterManager#isSafetyCenterEnabled()}. It should only be 857 * registered if the device supports SafetyCenter and the {@link SafetyCenterConfig} was loaded 858 * successfully. 859 */ 860 private final class SafetyCenterEnabledListener implements OnPropertiesChangedListener { 861 862 @GuardedBy("mApiLock") 863 private boolean mSafetyCenterEnabled; 864 865 @Override onPropertiesChanged(DeviceConfig.Properties properties)866 public void onPropertiesChanged(DeviceConfig.Properties properties) { 867 if (!properties.getKeyset().contains(PROPERTY_SAFETY_CENTER_ENABLED)) { 868 return; 869 } 870 boolean safetyCenterEnabled = 871 properties.getBoolean(PROPERTY_SAFETY_CENTER_ENABLED, SdkLevel.isAtLeastU()); 872 synchronized (mApiLock) { 873 if (mSafetyCenterEnabled == safetyCenterEnabled) { 874 Log.i( 875 TAG, 876 "Safety Center is already " 877 + (mSafetyCenterEnabled ? "enabled" : "disabled") 878 + ", ignoring change"); 879 return; 880 } 881 onSafetyCenterEnabledChangedLocked(safetyCenterEnabled); 882 } 883 } 884 885 @GuardedBy("mApiLock") setInitialStateLocked()886 private void setInitialStateLocked() { 887 mSafetyCenterEnabled = SafetyCenterFlags.getSafetyCenterEnabled(); 888 if (mSafetyCenterEnabled) { 889 onApiInitEnabledLocked(); 890 } 891 Log.i(TAG, "Safety Center is " + (mSafetyCenterEnabled ? "enabled" : "disabled")); 892 } 893 894 @GuardedBy("mApiLock") onSafetyCenterEnabledChangedLocked(boolean safetyCenterEnabled)895 private void onSafetyCenterEnabledChangedLocked(boolean safetyCenterEnabled) { 896 if (safetyCenterEnabled) { 897 onApiEnabledLocked(); 898 } else { 899 onApiDisabledLocked(); 900 } 901 902 mSafetyCenterEnabled = safetyCenterEnabled; 903 Log.i(TAG, "Safety Center is now " + (mSafetyCenterEnabled ? "enabled" : "disabled")); 904 } 905 906 @GuardedBy("mApiLock") onApiInitEnabledLocked()907 private void onApiInitEnabledLocked() { 908 mNotificationChannels.createAllChannelsForAllUsers(getContext()); 909 } 910 911 @GuardedBy("mApiLock") onApiEnabledLocked()912 private void onApiEnabledLocked() { 913 mNotificationChannels.createAllChannelsForAllUsers(getContext()); 914 mSafetyCenterBroadcastDispatcher.sendEnabledChanged(); 915 } 916 917 @GuardedBy("mApiLock") onApiDisabledLocked()918 private void onApiDisabledLocked() { 919 // We're not clearing the Safety Center notification channels here. The reason for this 920 // is that the NotificationManager will post a runnable to cancel all associated 921 // notifications when clearing the channels. Given this happens asynchronously, this can 922 // leak between test cases and cause notifications that should be active to be cleared 923 // inadvertently. We're ok with the inconsistency because the channels are hidden 924 // somewhat deeply under Settings anyway, and we're unlikely to turn off Safety Center 925 // in production. 926 clearDataLocked(); 927 mSafetyCenterListeners.clear(); 928 mSafetyCenterBroadcastDispatcher.sendEnabledChanged(); 929 } 930 } 931 932 /** A {@link Runnable} that is called to signal a refresh timeout. */ 933 private final class RefreshTimeout implements Runnable { 934 935 private final String mRefreshBroadcastId; 936 @RefreshReason private final int mRefreshReason; 937 private final UserProfileGroup mUserProfileGroup; 938 RefreshTimeout( String refreshBroadcastId, @RefreshReason int refreshReason, UserProfileGroup userProfileGroup)939 RefreshTimeout( 940 String refreshBroadcastId, 941 @RefreshReason int refreshReason, 942 UserProfileGroup userProfileGroup) { 943 mRefreshBroadcastId = refreshBroadcastId; 944 mRefreshReason = refreshReason; 945 mUserProfileGroup = userProfileGroup; 946 } 947 948 @Override run()949 public void run() { 950 synchronized (mApiLock) { 951 mSafetyCenterTimeouts.remove(this); 952 ArraySet<SafetySourceKey> stillInFlight = 953 mSafetyCenterRefreshTracker.timeoutRefresh(mRefreshBroadcastId); 954 if (stillInFlight == null) { 955 return; 956 } 957 boolean setError = !RefreshReasons.isBackgroundRefresh(mRefreshReason); 958 for (int i = 0; i < stillInFlight.size(); i++) { 959 mSafetyCenterDataManager.markSafetySourceRefreshTimedOut( 960 stillInFlight.valueAt(i), setError); 961 } 962 mSafetyCenterDataChangeNotifier.updateDataConsumers(mUserProfileGroup); 963 } 964 } 965 966 @Override toString()967 public String toString() { 968 return "RefreshTimeout{" 969 + "mRefreshBroadcastId='" 970 + mRefreshBroadcastId 971 + '\'' 972 + ", mUserProfileGroup=" 973 + mUserProfileGroup 974 + '}'; 975 } 976 } 977 978 /** A {@link Runnable} that is called to signal a resolving action timeout. */ 979 private final class ResolvingActionTimeout implements Runnable { 980 981 private final SafetyCenterIssueActionId mSafetyCenterIssueActionId; 982 private final UserProfileGroup mUserProfileGroup; 983 ResolvingActionTimeout( SafetyCenterIssueActionId safetyCenterIssueActionId, UserProfileGroup userProfileGroup)984 ResolvingActionTimeout( 985 SafetyCenterIssueActionId safetyCenterIssueActionId, 986 UserProfileGroup userProfileGroup) { 987 mSafetyCenterIssueActionId = safetyCenterIssueActionId; 988 mUserProfileGroup = userProfileGroup; 989 } 990 991 @Override run()992 public void run() { 993 synchronized (mApiLock) { 994 mSafetyCenterTimeouts.remove(this); 995 SafetySourceIssue safetySourceIssue = 996 mSafetyCenterDataManager.getSafetySourceIssue( 997 mSafetyCenterIssueActionId.getSafetyCenterIssueKey()); 998 boolean safetyCenterDataHasChanged = 999 mSafetyCenterDataManager.unmarkSafetyCenterIssueActionInFlight( 1000 mSafetyCenterIssueActionId, 1001 safetySourceIssue, 1002 SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__TIMEOUT); 1003 if (!safetyCenterDataHasChanged) { 1004 return; 1005 } 1006 mSafetyCenterDataChangeNotifier.updateDataConsumers(mUserProfileGroup); 1007 mSafetyCenterListeners.deliverErrorForUserProfileGroup( 1008 mUserProfileGroup, 1009 new SafetyCenterErrorDetails( 1010 mSafetyCenterResourcesApk.getStringByName( 1011 "resolving_action_error"))); 1012 Log.w( 1013 TAG, 1014 "Resolving action timed out for: " 1015 + toUserFriendlyString(mSafetyCenterIssueActionId)); 1016 } 1017 } 1018 1019 @Override toString()1020 public String toString() { 1021 return "ResolvingActionTimeout{" 1022 + "mSafetyCenterIssueActionId=" 1023 + toUserFriendlyString(mSafetyCenterIssueActionId) 1024 + ", mUserProfileGroup=" 1025 + mUserProfileGroup 1026 + '}'; 1027 } 1028 } 1029 canUseSafetyCenter()1030 private boolean canUseSafetyCenter() { 1031 return mDeviceSupportsSafetyCenter && mConfigAvailable; 1032 } 1033 1034 /** {@link BroadcastReceiver} which handles Locale changes. */ 1035 private final class LocaleBroadcastReceiver extends BroadcastReceiver { 1036 1037 private static final String TAG = "SafetyCenterLocaleBroad"; 1038 register(Context context)1039 void register(Context context) { 1040 IntentFilter filter = new IntentFilter(); 1041 filter.addAction(Intent.ACTION_LOCALE_CHANGED); 1042 context.registerReceiverForAllUsers( 1043 /* receiver= */ this, 1044 filter, 1045 /* broadcastPermission= */ null, 1046 /* scheduler= */ null); 1047 } 1048 1049 @Override onReceive(Context context, Intent intent)1050 public void onReceive(Context context, Intent intent) { 1051 if (!SafetyCenterFlags.getSafetyCenterEnabled()) { 1052 Log.i(TAG, "Safety Center is disabled, ignoring intent: " + intent); 1053 return; 1054 } 1055 1056 String action = intent.getAction(); 1057 if (!TextUtils.equals(action, Intent.ACTION_LOCALE_CHANGED)) { 1058 Log.w(TAG, "Received unexpected action: " + action); 1059 return; 1060 } 1061 1062 Log.d(TAG, "Locale changed broadcast received"); 1063 1064 int userId = ActivityManager.getCurrentUser(); 1065 synchronized (mApiLock) { 1066 startRefreshingSafetySourcesLocked(REFRESH_REASON_DEVICE_LOCALE_CHANGE, userId); 1067 mNotificationChannels.createAllChannelsForUser(getContext(), UserHandle.of(userId)); 1068 } 1069 } 1070 } 1071 1072 /** 1073 * {@link BroadcastReceiver} which handles user and work profile related broadcasts that Safety 1074 * Center is interested including quiet mode turning on/off and accounts being added/removed. 1075 */ 1076 private final class UserBroadcastReceiver extends BroadcastReceiver { 1077 1078 private static final String TAG = "SafetyCenterUserBroadca"; 1079 register(Context context)1080 void register(Context context) { 1081 IntentFilter filter = new IntentFilter(); 1082 filter.addAction(Intent.ACTION_USER_SWITCHED); 1083 filter.addAction(Intent.ACTION_USER_REMOVED); 1084 if (SdkLevel.isAtLeastV() && Flags.privateProfileSupported()) { 1085 // These intents are available on V+ only, and are called for managed and other 1086 // profile(s). 1087 filter.addAction(Intent.ACTION_PROFILE_ADDED); 1088 filter.addAction(Intent.ACTION_PROFILE_REMOVED); 1089 filter.addAction(Intent.ACTION_PROFILE_AVAILABLE); 1090 filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE); 1091 } else { 1092 // Only these intents are available in T and U, but that's okay because only managed 1093 // profiles are supported by Safety Center on these SDK versions. 1094 filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); 1095 filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); 1096 filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); 1097 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); 1098 } 1099 context.registerReceiverForAllUsers( 1100 /* receiver= */ this, 1101 filter, 1102 /* broadcastPermission= */ null, 1103 /* scheduler= */ null); 1104 } 1105 1106 @Override onReceive(Context context, Intent intent)1107 public void onReceive(Context context, Intent intent) { 1108 if (!SafetyCenterFlags.getSafetyCenterEnabled()) { 1109 Log.i(TAG, "Safety Center is disabled, ignoring intent: " + intent); 1110 return; 1111 } 1112 1113 String action = intent.getAction(); 1114 if (action == null) { 1115 Log.w(TAG, "Received broadcast with null action"); 1116 return; 1117 } 1118 1119 UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class); 1120 if (userHandle == null) { 1121 Log.w(TAG, "Received action: " + action + ", but missing user extra"); 1122 return; 1123 } 1124 1125 int userId = userHandle.getIdentifier(); 1126 Log.d(TAG, "Received action: " + action + ", for user id: " + userId); 1127 1128 if (!isUserIdValidForAction(action, userId, context)) { 1129 return; 1130 } 1131 1132 if (isUserOrProfileRemoved(action)) { 1133 removeUserAndData(userId); 1134 return; 1135 } 1136 1137 if (isProfileUnavailable(action)) { 1138 removeUser(userId); 1139 return; 1140 } 1141 1142 if (Intent.ACTION_USER_SWITCHED.equals(action) || isProfileAddedOrAvailable(action)) { 1143 synchronized (mApiLock) { 1144 startRefreshingSafetySourcesLocked(REFRESH_REASON_OTHER, userId); 1145 mNotificationChannels.createAllChannelsForUser(getContext(), userHandle); 1146 } 1147 return; 1148 } 1149 Log.w(TAG, "Received unexpected broadcast with action: " + action); 1150 } 1151 } 1152 isUserIdValidForAction( String action, @UserIdInt int userId, Context context)1153 private static boolean isUserIdValidForAction( 1154 String action, @UserIdInt int userId, Context context) { 1155 if (!UserProfileGroup.isSupported(userId, context)) { 1156 Log.i( 1157 TAG, 1158 "Received broadcast for user id: " + userId + ", which is an unsupported user"); 1159 return false; 1160 } 1161 if (Intent.ACTION_USER_SWITCHED.equals(action) 1162 && userId != ActivityManager.getCurrentUser()) { 1163 Log.w( 1164 TAG, 1165 "Received broadcast for user id: " 1166 + userId 1167 + ", which is not the current user"); 1168 return false; 1169 } 1170 if (isProfileAddedOrAvailable(action) && !UserUtils.isUserExistent(userId, context)) { 1171 Log.w(TAG, "Received broadcast for user id: " + userId + ", which does not exist"); 1172 return false; 1173 } 1174 return true; 1175 } 1176 isUserOrProfileRemoved(String action)1177 private static boolean isUserOrProfileRemoved(String action) { 1178 if (Intent.ACTION_USER_REMOVED.equals(action)) { 1179 return true; 1180 } 1181 if (SdkLevel.isAtLeastV() && Flags.privateProfileSupported()) { 1182 return Intent.ACTION_PROFILE_REMOVED.equals(action); 1183 } 1184 return Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action); 1185 } 1186 isProfileUnavailable(String action)1187 private static boolean isProfileUnavailable(String action) { 1188 if (SdkLevel.isAtLeastV() && Flags.privateProfileSupported()) { 1189 return Intent.ACTION_PROFILE_UNAVAILABLE.equals(action); 1190 } 1191 return Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action); 1192 } 1193 isProfileAddedOrAvailable(String action)1194 private static boolean isProfileAddedOrAvailable(String action) { 1195 if (SdkLevel.isAtLeastV() && Flags.privateProfileSupported()) { 1196 return Intent.ACTION_PROFILE_AVAILABLE.equals(action) 1197 || Intent.ACTION_PROFILE_ADDED.equals(action); 1198 } 1199 return Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action) 1200 || Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action); 1201 } 1202 removeUserAndData(@serIdInt int userId)1203 private void removeUserAndData(@UserIdInt int userId) { 1204 removeUser(userId, /* clearDataPermanently= */ true); 1205 } 1206 removeUser(@serIdInt int userId)1207 private void removeUser(@UserIdInt int userId) { 1208 removeUser(userId, /* clearDataPermanently= */ false); 1209 } 1210 removeUser(@serIdInt int userId, boolean clearDataPermanently)1211 private void removeUser(@UserIdInt int userId, boolean clearDataPermanently) { 1212 UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(getContext(), userId); 1213 synchronized (mApiLock) { 1214 mSafetyCenterListeners.clearForUser(userId); 1215 mSafetyCenterRefreshTracker.clearRefreshForUser(userId); 1216 1217 if (clearDataPermanently) { 1218 mSafetyCenterDataManager.clearForUser(userId); 1219 mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroup, userId); 1220 } else { 1221 mSafetyCenterListeners.deliverDataForUserProfileGroup(userProfileGroup); 1222 } 1223 } 1224 } 1225 1226 @GuardedBy("mApiLock") startRefreshingSafetySourcesLocked( @efreshReason int refreshReason, @UserIdInt int userId)1227 private void startRefreshingSafetySourcesLocked( 1228 @RefreshReason int refreshReason, @UserIdInt int userId) { 1229 startRefreshingSafetySourcesLocked( 1230 refreshReason, 1231 UserProfileGroup.fromUser(getContext(), userId), 1232 /* selectedSafetySourceIds= */ null); 1233 } 1234 1235 @GuardedBy("mApiLock") startRefreshingSafetySourcesLocked( @efreshReason int refreshReason, @UserIdInt int userId, List<String> selectedSafetySourceIds)1236 private void startRefreshingSafetySourcesLocked( 1237 @RefreshReason int refreshReason, 1238 @UserIdInt int userId, 1239 List<String> selectedSafetySourceIds) { 1240 startRefreshingSafetySourcesLocked( 1241 refreshReason, 1242 UserProfileGroup.fromUser(getContext(), userId), 1243 selectedSafetySourceIds); 1244 } 1245 1246 @GuardedBy("mApiLock") startRefreshingSafetySourcesLocked( @efreshReason int refreshReason, UserProfileGroup userProfileGroup, @Nullable List<String> selectedSafetySourceIds)1247 private void startRefreshingSafetySourcesLocked( 1248 @RefreshReason int refreshReason, 1249 UserProfileGroup userProfileGroup, 1250 @Nullable List<String> selectedSafetySourceIds) { 1251 String refreshBroadcastId = 1252 mSafetyCenterBroadcastDispatcher.sendRefreshSafetySources( 1253 refreshReason, userProfileGroup, selectedSafetySourceIds); 1254 if (refreshBroadcastId == null) { 1255 return; 1256 } 1257 1258 RefreshTimeout refreshTimeout = 1259 new RefreshTimeout(refreshBroadcastId, refreshReason, userProfileGroup); 1260 mSafetyCenterTimeouts.add( 1261 refreshTimeout, SafetyCenterFlags.getRefreshSourcesTimeout(refreshReason)); 1262 1263 mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroup); 1264 } 1265 1266 /** 1267 * Executes the {@link SafetySourceIssue.Action} specified by the given {@link 1268 * SafetyCenterIssueActionId}. 1269 * 1270 * <p>No validation is performed on the contents of the given ID. 1271 */ executeIssueActionInternal(SafetyCenterIssueActionId safetyCenterIssueActionId)1272 public void executeIssueActionInternal(SafetyCenterIssueActionId safetyCenterIssueActionId) { 1273 SafetyCenterIssueKey safetyCenterIssueKey = 1274 safetyCenterIssueActionId.getSafetyCenterIssueKey(); 1275 UserProfileGroup userProfileGroup = 1276 UserProfileGroup.fromUser(getContext(), safetyCenterIssueKey.getUserId()); 1277 executeIssueActionInternal(safetyCenterIssueActionId, userProfileGroup, /* taskId= */ null); 1278 } 1279 executeIssueActionInternal( SafetyCenterIssueActionId safetyCenterIssueActionId, UserProfileGroup userProfileGroup, @Nullable Integer taskId)1280 private void executeIssueActionInternal( 1281 SafetyCenterIssueActionId safetyCenterIssueActionId, 1282 UserProfileGroup userProfileGroup, 1283 @Nullable Integer taskId) { 1284 synchronized (mApiLock) { 1285 SafetySourceIssue.Action safetySourceIssueAction = 1286 mSafetyCenterDataManager.getSafetySourceIssueAction(safetyCenterIssueActionId); 1287 1288 if (safetySourceIssueAction == null) { 1289 Log.w( 1290 TAG, 1291 "Attempt to execute an issue action that is not provided by the source," 1292 + " that was dismissed, or is already in flight"); 1293 // Don't send the error to the UI here, since it could happen when clicking the 1294 // button multiple times in a row. 1295 return; 1296 } 1297 PendingIntent issueActionPendingIntent = safetySourceIssueAction.getPendingIntent(); 1298 if (!dispatchPendingIntent(issueActionPendingIntent, taskId)) { 1299 Log.w( 1300 TAG, 1301 "Error dispatching action: " 1302 + toUserFriendlyString(safetyCenterIssueActionId)); 1303 CharSequence errorMessage; 1304 if (safetySourceIssueAction.willResolve()) { 1305 errorMessage = 1306 mSafetyCenterResourcesApk.getStringByName("resolving_action_error"); 1307 } else { 1308 errorMessage = mSafetyCenterResourcesApk.getStringByName("redirecting_error"); 1309 } 1310 mSafetyCenterListeners.deliverErrorForUserProfileGroup( 1311 userProfileGroup, new SafetyCenterErrorDetails(errorMessage)); 1312 return; 1313 } 1314 if (safetySourceIssueAction.willResolve()) { 1315 Log.d( 1316 TAG, 1317 "Starting resolving action for: " 1318 + toUserFriendlyString(safetyCenterIssueActionId)); 1319 mSafetyCenterDataManager.markSafetyCenterIssueActionInFlight( 1320 safetyCenterIssueActionId); 1321 ResolvingActionTimeout resolvingActionTimeout = 1322 new ResolvingActionTimeout(safetyCenterIssueActionId, userProfileGroup); 1323 mSafetyCenterTimeouts.add( 1324 resolvingActionTimeout, SafetyCenterFlags.getResolvingActionTimeout()); 1325 mSafetyCenterDataChangeNotifier.updateDataConsumers(userProfileGroup); 1326 } 1327 } 1328 } 1329 dispatchPendingIntent(PendingIntent pendingIntent)1330 private boolean dispatchPendingIntent(PendingIntent pendingIntent) { 1331 return dispatchPendingIntent(pendingIntent, /* launchTaskId= */ null); 1332 } 1333 dispatchPendingIntent( PendingIntent pendingIntent, @Nullable Integer launchTaskId)1334 private boolean dispatchPendingIntent( 1335 PendingIntent pendingIntent, @Nullable Integer launchTaskId) { 1336 if (launchTaskId != null 1337 && getContext().checkCallingOrSelfPermission(START_TASKS_FROM_RECENTS) 1338 != PERMISSION_GRANTED) { 1339 launchTaskId = null; 1340 } 1341 return PendingIntentSender.trySend(pendingIntent, launchTaskId); 1342 } 1343 1344 @GuardedBy("mApiLock") clearDataLocked()1345 private void clearDataLocked() { 1346 mSafetyCenterDataManager.clear(); 1347 mSafetyCenterTimeouts.clear(); 1348 mSafetyCenterRefreshTracker.clearRefresh(); 1349 mNotificationSender.cancelAllNotifications(); 1350 } 1351 1352 /** Dumps state for debugging purposes. */ 1353 @GuardedBy("mApiLock") dumpLocked(PrintWriter fout)1354 private void dumpLocked(PrintWriter fout) { 1355 fout.println("SERVICE"); 1356 fout.println( 1357 "\tSafetyCenterService{" 1358 + "mDeviceSupportsSafetyCenter=" 1359 + mDeviceSupportsSafetyCenter 1360 + ", mConfigAvailable=" 1361 + mConfigAvailable 1362 + '}'); 1363 fout.println(); 1364 } 1365 } 1366