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