1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.healthconnect.permission; 18 19 import static com.android.server.healthconnect.permission.FirstGrantTimeDatastore.DATA_TYPE_CURRENT; 20 import static com.android.server.healthconnect.permission.FirstGrantTimeDatastore.DATA_TYPE_STAGED; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.health.connect.Constants; 28 import android.os.UserHandle; 29 import android.util.ArrayMap; 30 import android.util.ArraySet; 31 import android.util.Log; 32 33 import com.android.internal.annotations.GuardedBy; 34 import com.android.server.healthconnect.HealthConnectThreadScheduler; 35 import com.android.server.healthconnect.migration.MigrationStateManager; 36 import com.android.server.healthconnect.storage.datatypehelpers.HealthDataCategoryPriorityHelper; 37 38 import java.io.File; 39 import java.time.Instant; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Set; 43 import java.util.concurrent.locks.ReentrantReadWriteLock; 44 45 /** 46 * Manager class of the health permissions first grant time. 47 * 48 * @hide 49 */ 50 public class FirstGrantTimeManager implements PackageManager.OnPermissionsChangedListener { 51 private static final String TAG = "HealthFirstGrantTimeMan"; 52 private static final int CURRENT_VERSION = 1; 53 54 private final PackageManager mPackageManager; 55 private final HealthPermissionIntentAppsTracker mTracker; 56 57 private final ReentrantReadWriteLock mGrantTimeLock = new ReentrantReadWriteLock(); 58 59 @GuardedBy("mGrantTimeLock") 60 private final FirstGrantTimeDatastore mDatastore; 61 62 @GuardedBy("mGrantTimeLock") 63 private final UidToGrantTimeCache mUidToGrantTimeCache; 64 65 @GuardedBy("mGrantTimeLock") 66 private final Set<Integer> mRestoredAndValidatedUsers = new ArraySet<>(); 67 68 private final PackageInfoUtils mPackageInfoHelper; 69 FirstGrantTimeManager( @onNull Context context, @NonNull HealthPermissionIntentAppsTracker tracker, @NonNull FirstGrantTimeDatastore datastore)70 public FirstGrantTimeManager( 71 @NonNull Context context, 72 @NonNull HealthPermissionIntentAppsTracker tracker, 73 @NonNull FirstGrantTimeDatastore datastore) { 74 mTracker = tracker; 75 mDatastore = datastore; 76 mPackageManager = context.getPackageManager(); 77 mUidToGrantTimeCache = new UidToGrantTimeCache(); 78 mPackageInfoHelper = new PackageInfoUtils(context); 79 mPackageManager.addOnPermissionsChangeListener(this); 80 } 81 82 /** Get the date when the first health permission was granted. */ 83 @Nullable getFirstGrantTime(@onNull String packageName, @NonNull UserHandle user)84 public Instant getFirstGrantTime(@NonNull String packageName, @NonNull UserHandle user) 85 throws IllegalArgumentException { 86 87 Integer uid = mPackageInfoHelper.getPackageUid(packageName, user); 88 if (uid == null) { 89 throw new IllegalArgumentException( 90 "Package name " 91 + packageName 92 + " of user " 93 + user.getIdentifier() 94 + " not found."); 95 } 96 initAndValidateUserStateIfNeedLocked(user); 97 98 Instant grantTimeDate = getGrantTimeReadLocked(uid); 99 if (grantTimeDate == null) { 100 // Check and update the state in case health permission has been granted before 101 // onPermissionsChanged callback was propagated. 102 onPermissionsChanged(mPackageInfoHelper.getPackageUid(packageName, user)); 103 grantTimeDate = getGrantTimeReadLocked(uid); 104 } 105 106 return grantTimeDate; 107 } 108 109 /** Sets the provided first grant time for the given {@code packageName}. */ setFirstGrantTime( @onNull String packageName, @NonNull Instant time, @NonNull UserHandle user)110 public void setFirstGrantTime( 111 @NonNull String packageName, @NonNull Instant time, @NonNull UserHandle user) { 112 final Integer uid = mPackageInfoHelper.getPackageUid(packageName, user); 113 if (uid == null) { 114 throw new IllegalArgumentException( 115 "Package name " 116 + packageName 117 + " of user " 118 + user.getIdentifier() 119 + " not found."); 120 } 121 122 mGrantTimeLock.writeLock().lock(); 123 try { 124 mUidToGrantTimeCache.put(uid, time); 125 mDatastore.writeForUser( 126 mUidToGrantTimeCache.extractUserGrantTimeState(user), user, DATA_TYPE_CURRENT); 127 } finally { 128 mGrantTimeLock.writeLock().unlock(); 129 } 130 } 131 132 @Override onPermissionsChanged(int uid)133 public void onPermissionsChanged(int uid) { 134 String[] packageNames = mPackageManager.getPackagesForUid(uid); 135 if (packageNames == null) { 136 Log.w(TAG, "onPermissionsChanged: no known packages for UID: " + uid); 137 return; 138 } 139 140 UserHandle user = UserHandle.getUserHandleForUid(uid); 141 initAndValidateUserStateIfNeedLocked(user); 142 143 if (!checkSupportPermissionsUsageIntent(packageNames, user)) { 144 logIfInDebugMode("Can find health intent declaration in ", packageNames[0]); 145 return; 146 } 147 148 mGrantTimeLock.writeLock().lock(); 149 try { 150 boolean anyHealthPermissionGranted = 151 mPackageInfoHelper.hasGrantedHealthPermissions(packageNames, user); 152 153 boolean grantTimeRecorded = (getGrantTimeReadLocked(uid) != null); 154 if (grantTimeRecorded != anyHealthPermissionGranted) { 155 if (grantTimeRecorded) { 156 // An app doesn't have health permissions anymore, reset its grant time. 157 mUidToGrantTimeCache.remove(uid); 158 // Update priority table only if migration is not in progress as it should 159 // already take care of merging permissions. 160 if (!MigrationStateManager.getInitialisedInstance().isMigrationInProgress()) { 161 HealthConnectThreadScheduler.scheduleInternalTask( 162 () -> removeAppFromPriorityList(packageNames)); 163 } 164 } else { 165 // An app got new health permission, set current time as it's first grant 166 // time if we can't update state from the staged data. 167 if (!tryUpdateGrantTimeFromStagedDataLocked(user, uid)) { 168 mUidToGrantTimeCache.put(uid, Instant.now()); 169 } 170 } 171 172 UserGrantTimeState updatedState = 173 mUidToGrantTimeCache.extractUserGrantTimeState(user); 174 logIfInDebugMode("State after onPermissionsChanged :", updatedState); 175 mDatastore.writeForUser(updatedState, user, DATA_TYPE_CURRENT); 176 } else { 177 // Update priority table only if migration is not in progress as it should already 178 // take care of merging permissions 179 if (!MigrationStateManager.getInitialisedInstance().isMigrationInProgress()) { 180 HealthConnectThreadScheduler.scheduleInternalTask( 181 () -> mPackageInfoHelper.updateHealthDataPriority(packageNames, user)); 182 } 183 } 184 } finally { 185 mGrantTimeLock.writeLock().unlock(); 186 } 187 } 188 189 // TODO(b/277063776): move two methods below to b&r classes. 190 /** Returns the state which should be backed up. */ createBackupState(UserHandle user)191 public UserGrantTimeState createBackupState(UserHandle user) { 192 initAndValidateUserStateIfNeedLocked(user); 193 return mUidToGrantTimeCache.extractUserBackupGrantTimeState(user); 194 } 195 196 /** 197 * Callback which should be called when backup grant time data is available. Triggers merge of 198 * current and backup grant time data. All grant times from backup state which are not merged 199 * with the current state (e.g. because an app is not installed) will be staged until app gets 200 * health permission. 201 * 202 * @param userId user for which the data is available. 203 * @param state backup state to apply. 204 */ applyAndStageBackupDataForUser(UserHandle userId, UserGrantTimeState state)205 public void applyAndStageBackupDataForUser(UserHandle userId, UserGrantTimeState state) { 206 initAndValidateUserStateIfNeedLocked(userId); 207 208 mGrantTimeLock.writeLock().lock(); 209 try { 210 // Write the state into the disk as staged data so that it can be merged. 211 mDatastore.writeForUser(state, userId, DATA_TYPE_STAGED); 212 updateGrantTimesWithStagedDataLocked(userId); 213 } finally { 214 mGrantTimeLock.writeLock().unlock(); 215 } 216 } 217 218 /** Returns file with grant times data. */ getFile(UserHandle userHandle)219 public File getFile(UserHandle userHandle) { 220 return mDatastore.getFile(userHandle, DATA_TYPE_CURRENT); 221 } 222 onPackageRemoved( @onNull String packageName, int removedPackageUid, @NonNull UserHandle userHandle)223 void onPackageRemoved( 224 @NonNull String packageName, int removedPackageUid, @NonNull UserHandle userHandle) { 225 String[] leftSharedUidPackages = 226 mPackageInfoHelper.getPackagesForUid(removedPackageUid, userHandle); 227 if (leftSharedUidPackages != null && leftSharedUidPackages.length > 0) { 228 // There are installed packages left with given UID, 229 // don't need to update grant time state. 230 return; 231 } 232 233 initAndValidateUserStateIfNeedLocked(userHandle); 234 235 if (getGrantTimeReadLocked(removedPackageUid) != null) { 236 mGrantTimeLock.writeLock().lock(); 237 try { 238 mUidToGrantTimeCache.remove(removedPackageUid); 239 UserGrantTimeState updatedState = 240 mUidToGrantTimeCache.extractUserGrantTimeState(userHandle); 241 logIfInDebugMode("State after package " + packageName + " removed: ", updatedState); 242 mDatastore.writeForUser(updatedState, userHandle, DATA_TYPE_CURRENT); 243 } finally { 244 mGrantTimeLock.writeLock().unlock(); 245 } 246 } 247 } 248 249 @GuardedBy("mGrantTimeLock") getGrantTimeReadLocked(Integer uid)250 private Instant getGrantTimeReadLocked(Integer uid) { 251 mGrantTimeLock.readLock().lock(); 252 try { 253 return mUidToGrantTimeCache.get(uid); 254 } finally { 255 mGrantTimeLock.readLock().unlock(); 256 } 257 } 258 259 @GuardedBy("mGrantTimeLock") updateGrantTimesWithStagedDataLocked(UserHandle user)260 private void updateGrantTimesWithStagedDataLocked(UserHandle user) { 261 boolean stateChanged = false; 262 for (Integer uid : mUidToGrantTimeCache.mUidToGrantTime.keySet()) { 263 if (!UserHandle.getUserHandleForUid(uid).equals(user)) { 264 continue; 265 } 266 267 stateChanged |= tryUpdateGrantTimeFromStagedDataLocked(user, uid); 268 } 269 270 if (stateChanged) { 271 mDatastore.writeForUser( 272 mUidToGrantTimeCache.extractUserGrantTimeState(user), user, DATA_TYPE_CURRENT); 273 } 274 } 275 276 @GuardedBy("mGrantTimeLock") tryUpdateGrantTimeFromStagedDataLocked(UserHandle user, Integer uid)277 private boolean tryUpdateGrantTimeFromStagedDataLocked(UserHandle user, Integer uid) { 278 UserGrantTimeState backupState = mDatastore.readForUser(user, DATA_TYPE_STAGED); 279 if (backupState == null) { 280 return false; 281 } 282 283 Instant stagedTime = null; 284 for (String packageName : mPackageInfoHelper.getPackageNamesForUid(uid)) { 285 if (stagedTime == null) { 286 stagedTime = backupState.getPackageGrantTimes().get(packageName); 287 } 288 } 289 290 if (stagedTime == null) { 291 return false; 292 } 293 if (mUidToGrantTimeCache.containsKey(uid) 294 && mUidToGrantTimeCache.get(uid).isBefore(stagedTime)) { 295 Log.w( 296 TAG, 297 "Backup grant time is later than currently stored grant time, " 298 + "skip restoring grant time for" 299 + " uid " 300 + uid); 301 return false; 302 } 303 304 mUidToGrantTimeCache.put(uid, stagedTime); 305 for (String packageName : mPackageInfoHelper.getPackageNamesForUid(uid)) { 306 backupState.getPackageGrantTimes().remove(packageName); 307 } 308 mDatastore.writeForUser(backupState, user, DATA_TYPE_STAGED); 309 return true; 310 } 311 312 /** Initialize first grant time state for given user. */ initAndValidateUserStateIfNeedLocked(UserHandle user)313 private void initAndValidateUserStateIfNeedLocked(UserHandle user) { 314 if (userStateIsInitializedReadLocked(user)) { 315 // This user state is already inited and validated 316 return; 317 } 318 319 mGrantTimeLock.writeLock().lock(); 320 try { 321 Log.i( 322 TAG, 323 "State for user: " 324 + user.getIdentifier() 325 + " has not been restored and validated."); 326 UserGrantTimeState restoredState = restoreCurrentUserStateLocked(user); 327 328 List<PackageInfo> validHealthApps = 329 mPackageInfoHelper.getPackagesHoldingHealthPermissions(user); 330 logIfInDebugMode( 331 "Packages holding health perms of user " + user + " :", validHealthApps); 332 333 validateAndCorrectRecordedStateForUser(restoredState, validHealthApps, user); 334 335 // TODO(b/260691599): consider removing mapping when getUidForSharedUser is 336 Map<String, Set<Integer>> sharedUserNamesToUid = 337 mPackageInfoHelper.collectSharedUserNameToUidsMappingForUser( 338 validHealthApps, user); 339 340 mUidToGrantTimeCache.populateFromUserGrantTimeState( 341 restoredState, sharedUserNamesToUid, user); 342 343 mRestoredAndValidatedUsers.add(user.getIdentifier()); 344 logIfInDebugMode("State after init: ", restoredState); 345 logIfInDebugMode("Cache after init: ", mUidToGrantTimeCache); 346 } finally { 347 mGrantTimeLock.writeLock().unlock(); 348 } 349 } 350 userStateIsInitializedReadLocked(UserHandle user)351 private boolean userStateIsInitializedReadLocked(UserHandle user) { 352 mGrantTimeLock.readLock().lock(); 353 try { 354 return mRestoredAndValidatedUsers.contains(user.getIdentifier()); 355 } finally { 356 mGrantTimeLock.readLock().unlock(); 357 } 358 } 359 360 @GuardedBy("mGrantTimeLock") restoreCurrentUserStateLocked(UserHandle userHandle)361 private UserGrantTimeState restoreCurrentUserStateLocked(UserHandle userHandle) { 362 try { 363 UserGrantTimeState restoredState = 364 mDatastore.readForUser(userHandle, DATA_TYPE_CURRENT); 365 if (restoredState == null) { 366 restoredState = new UserGrantTimeState(CURRENT_VERSION); 367 } 368 return restoredState; 369 } catch (Exception e) { 370 Log.e(TAG, "Error while reading from datastore: " + e); 371 return new UserGrantTimeState(CURRENT_VERSION); 372 } 373 } 374 375 /** 376 * Validate current state and remove apps which are not present / hold health permissions, set 377 * new grant time to apps which doesn't have grant time but installed and hold health 378 * permissions. It should mitigate situation e.g. when permission mainline module did roll-back 379 * and some health permissions got granted/revoked without onPermissionsChanged callback. 380 * 381 * @param recordedState restored state 382 * @param healthPackagesInfos packageInfos of apps which currently hold health permissions 383 * @param user UserHandle for whom to perform validation 384 */ 385 @GuardedBy("mGrantTimeLock") validateAndCorrectRecordedStateForUser( @onNull UserGrantTimeState recordedState, @NonNull List<PackageInfo> healthPackagesInfos, @NonNull UserHandle user)386 private void validateAndCorrectRecordedStateForUser( 387 @NonNull UserGrantTimeState recordedState, 388 @NonNull List<PackageInfo> healthPackagesInfos, 389 @NonNull UserHandle user) { 390 Set<String> validPackagesPerUser = new ArraySet<>(); 391 Set<String> validSharedUsersPerUser = new ArraySet<>(); 392 393 boolean stateChanged = false; 394 logIfInDebugMode("Valid apps for " + user + ": ", healthPackagesInfos); 395 396 // If package holds health permissions and supports health permission intent 397 // but doesn't have recorded grant time (e.g. because of permissions rollback), 398 // set current time as the first grant time. 399 for (PackageInfo info : healthPackagesInfos) { 400 if (!mTracker.supportsPermissionUsageIntent(info.packageName, user)) { 401 continue; 402 } 403 404 if (info.sharedUserId == null) { 405 stateChanged |= setPackageGrantTimeIfNotRecorded(recordedState, info.packageName); 406 validPackagesPerUser.add(info.packageName); 407 } else { 408 stateChanged |= 409 setSharedUserGrantTimeIfNotRecorded(recordedState, info.sharedUserId); 410 validSharedUsersPerUser.add(info.sharedUserId); 411 } 412 } 413 414 // If package is not installed / doesn't hold health permissions 415 // but has recorded first grant time, remove it from grant time state. 416 stateChanged |= 417 removeInvalidPackagesFromGrantTimeStateForUser(recordedState, validPackagesPerUser); 418 419 stateChanged |= 420 removeInvalidSharedUsersFromGrantTimeStateForUser( 421 recordedState, validSharedUsersPerUser); 422 423 if (stateChanged) { 424 logIfInDebugMode("Changed state after validation for " + user + ": ", recordedState); 425 mDatastore.writeForUser(recordedState, user, DATA_TYPE_CURRENT); 426 } 427 } 428 429 @GuardedBy("mGrantTimeLock") setPackageGrantTimeIfNotRecorded( @onNull UserGrantTimeState grantTimeState, @NonNull String packageName)430 private boolean setPackageGrantTimeIfNotRecorded( 431 @NonNull UserGrantTimeState grantTimeState, @NonNull String packageName) { 432 if (!grantTimeState.containsPackageGrantTime(packageName)) { 433 Log.w( 434 TAG, 435 "No recorded grant time for package:" 436 + packageName 437 + ". Assigning current time as the first grant time."); 438 grantTimeState.setPackageGrantTime(packageName, Instant.now()); 439 return true; 440 } 441 return false; 442 } 443 444 @GuardedBy("mGrantTimeLock") setSharedUserGrantTimeIfNotRecorded( @onNull UserGrantTimeState grantTimeState, @NonNull String sharedUserIdName)445 private boolean setSharedUserGrantTimeIfNotRecorded( 446 @NonNull UserGrantTimeState grantTimeState, @NonNull String sharedUserIdName) { 447 if (!grantTimeState.containsSharedUserGrantTime(sharedUserIdName)) { 448 Log.w( 449 TAG, 450 "No recorded grant time for shared user:" 451 + sharedUserIdName 452 + ". Assigning current time as first grant time."); 453 grantTimeState.setSharedUserGrantTime(sharedUserIdName, Instant.now()); 454 return true; 455 } 456 return false; 457 } 458 459 @GuardedBy("mGrantTimeLock") removeInvalidPackagesFromGrantTimeStateForUser( @onNull UserGrantTimeState recordedState, @NonNull Set<String> validApps)460 private boolean removeInvalidPackagesFromGrantTimeStateForUser( 461 @NonNull UserGrantTimeState recordedState, @NonNull Set<String> validApps) { 462 Set<String> recordedButNotValid = 463 new ArraySet<>(recordedState.getPackageGrantTimes().keySet()); 464 if (validApps != null) { 465 recordedButNotValid.removeAll(validApps); 466 } 467 468 if (!recordedButNotValid.isEmpty()) { 469 Log.w( 470 TAG, 471 "Packages " 472 + recordedButNotValid 473 + " have recorded grant times, but not installed or hold health " 474 + "permissions anymore. Removing them from the grant time state."); 475 recordedState.getPackageGrantTimes().keySet().removeAll(recordedButNotValid); 476 return true; 477 } 478 return false; 479 } 480 481 @GuardedBy("mGrantTimeLock") removeInvalidSharedUsersFromGrantTimeStateForUser( @onNull UserGrantTimeState recordedState, @NonNull Set<String> validSharedUsers)482 private boolean removeInvalidSharedUsersFromGrantTimeStateForUser( 483 @NonNull UserGrantTimeState recordedState, @NonNull Set<String> validSharedUsers) { 484 Set<String> recordedButNotValid = 485 new ArraySet<>(recordedState.getSharedUserGrantTimes().keySet()); 486 if (validSharedUsers != null) { 487 recordedButNotValid.removeAll(validSharedUsers); 488 } 489 490 if (!recordedButNotValid.isEmpty()) { 491 Log.w( 492 TAG, 493 "Shared users " 494 + recordedButNotValid 495 + " have recorded grant times, but not installed or hold health " 496 + "permissions anymore. Removing them from the grant time state."); 497 recordedState.getSharedUserGrantTimes().keySet().removeAll(recordedButNotValid); 498 return true; 499 } 500 return false; 501 } 502 checkSupportPermissionsUsageIntent( @onNull String[] names, @NonNull UserHandle user)503 private boolean checkSupportPermissionsUsageIntent( 504 @NonNull String[] names, @NonNull UserHandle user) { 505 for (String packageName : names) { 506 if (mTracker.supportsPermissionUsageIntent(packageName, user)) { 507 return true; 508 } 509 } 510 return false; 511 } 512 logIfInDebugMode(@onNull String prefixMessage, @NonNull Object objectToLog)513 private void logIfInDebugMode(@NonNull String prefixMessage, @NonNull Object objectToLog) { 514 if (Constants.DEBUG) { 515 Log.d(TAG, prefixMessage + objectToLog.toString()); 516 } 517 } 518 519 private class UidToGrantTimeCache { 520 private final Map<Integer, Instant> mUidToGrantTime; 521 UidToGrantTimeCache()522 UidToGrantTimeCache() { 523 mUidToGrantTime = new ArrayMap<>(); 524 } 525 526 @Override toString()527 public String toString() { 528 return mUidToGrantTime.toString(); 529 } 530 531 @Nullable remove(@ullable Integer uid)532 Instant remove(@Nullable Integer uid) { 533 if (uid == null) { 534 return null; 535 } 536 return mUidToGrantTime.remove(uid); 537 } 538 539 @Nullable get(Integer uid)540 Instant get(Integer uid) { 541 return mUidToGrantTime.get(uid); 542 } 543 containsKey(@ullable Integer uid)544 boolean containsKey(@Nullable Integer uid) { 545 if (uid == null) { 546 return false; 547 } 548 return mUidToGrantTime.containsKey(uid); 549 } 550 551 @Nullable put(@onNull Integer uid, @NonNull Instant time)552 Instant put(@NonNull Integer uid, @NonNull Instant time) { 553 return mUidToGrantTime.put(uid, time); 554 } 555 556 @NonNull extractUserGrantTimeState(@onNull UserHandle user)557 UserGrantTimeState extractUserGrantTimeState(@NonNull UserHandle user) { 558 Map<String, Instant> sharedUserToGrantTime = new ArrayMap<>(); 559 Map<String, Instant> packageNameToGrantTime = new ArrayMap<>(); 560 561 for (Map.Entry<Integer, Instant> entry : mUidToGrantTime.entrySet()) { 562 Integer uid = entry.getKey(); 563 Instant time = entry.getValue(); 564 565 if (!UserHandle.getUserHandleForUid(uid).equals(user)) { 566 continue; 567 } 568 569 String sharedUserName = mPackageInfoHelper.getSharedUserNameFromUid(uid); 570 if (sharedUserName != null) { 571 sharedUserToGrantTime.put(sharedUserName, time); 572 } else { 573 String packageName = mPackageInfoHelper.getPackageNameFromUid(uid); 574 if (packageName != null) { 575 packageNameToGrantTime.put(packageName, time); 576 } 577 } 578 } 579 580 return new UserGrantTimeState( 581 packageNameToGrantTime, sharedUserToGrantTime, CURRENT_VERSION); 582 } 583 584 @NonNull extractUserBackupGrantTimeState(@onNull UserHandle user)585 UserGrantTimeState extractUserBackupGrantTimeState(@NonNull UserHandle user) { 586 Map<String, Instant> sharedUserToGrantTime = new ArrayMap<>(); 587 Map<String, Instant> packageNameToGrantTime = new ArrayMap<>(); 588 589 for (Map.Entry<Integer, Instant> entry : mUidToGrantTime.entrySet()) { 590 Integer uid = entry.getKey(); 591 Instant time = entry.getValue(); 592 593 if (!UserHandle.getUserHandleForUid(uid).equals(user)) { 594 continue; 595 } 596 597 for (String packageName : mPackageInfoHelper.getPackageNamesForUid(uid)) { 598 packageNameToGrantTime.put(packageName, time); 599 } 600 } 601 602 return new UserGrantTimeState( 603 packageNameToGrantTime, sharedUserToGrantTime, CURRENT_VERSION); 604 } 605 populateFromUserGrantTimeState( @ullable UserGrantTimeState grantTimeState, @NonNull Map<String, Set<Integer>> sharedUserNameToUids, @NonNull UserHandle user)606 void populateFromUserGrantTimeState( 607 @Nullable UserGrantTimeState grantTimeState, 608 @NonNull Map<String, Set<Integer>> sharedUserNameToUids, 609 @NonNull UserHandle user) { 610 if (grantTimeState == null) { 611 return; 612 } 613 614 for (Map.Entry<String, Instant> entry : 615 grantTimeState.getSharedUserGrantTimes().entrySet()) { 616 String sharedUserName = entry.getKey(); 617 Instant time = entry.getValue(); 618 619 if (sharedUserNameToUids.get(sharedUserName) == null) { 620 continue; 621 } 622 623 for (Integer uid : sharedUserNameToUids.get(sharedUserName)) { 624 put(uid, time); 625 } 626 } 627 628 for (Map.Entry<String, Instant> entry : 629 grantTimeState.getPackageGrantTimes().entrySet()) { 630 String packageName = entry.getKey(); 631 Instant time = entry.getValue(); 632 633 Integer uid = mPackageInfoHelper.getPackageUid(packageName, user); 634 if (uid != null) { 635 put(uid, time); 636 } 637 } 638 } 639 } 640 removeAppFromPriorityList(String[] packageNames)641 private void removeAppFromPriorityList(String[] packageNames) { 642 for (String packageName : packageNames) { 643 HealthDataCategoryPriorityHelper.getInstance().removeAppFromPriorityList(packageName); 644 } 645 } 646 } 647