• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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