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.server.pm;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UserIdInt;
22 import android.content.pm.ChangedPackages;
23 import android.util.SparseArray;
24 
25 import com.android.internal.annotations.GuardedBy;
26 
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.function.BiConsumer;
32 
33 class ChangedPackagesTracker {
34 
35     @NonNull
36     private final Object mLock = new Object();
37 
38     @GuardedBy("mLock")
39     @NonNull
40     private int mChangedPackagesSequenceNumber;
41     /**
42      * List of changed [installed, removed or updated] packages.
43      * mapping from user id -> sequence number -> package name
44      */
45     @GuardedBy("mLock")
46     @NonNull
47     private final SparseArray<SparseArray<String>> mUserIdToSequenceToPackage = new SparseArray<>();
48     /**
49      * The sequence number of the last change to a package.
50      * mapping from user id -> package name -> sequence number
51      */
52     @GuardedBy("mLock")
53     @NonNull
54     private final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers =
55             new SparseArray<>();
56 
57     @Nullable
getChangedPackages(int sequenceNumber, @UserIdInt int userId)58     public ChangedPackages getChangedPackages(int sequenceNumber, @UserIdInt int userId) {
59         synchronized (mLock) {
60             if (sequenceNumber >= mChangedPackagesSequenceNumber) {
61                 return null;
62             }
63             final SparseArray<String> changedPackages = mUserIdToSequenceToPackage.get(userId);
64             if (changedPackages == null) {
65                 return null;
66             }
67             final List<String> packageNames =
68                     new ArrayList<>(mChangedPackagesSequenceNumber - sequenceNumber);
69             for (int i = sequenceNumber; i < mChangedPackagesSequenceNumber; i++) {
70                 final String packageName = changedPackages.get(i);
71                 if (packageName != null) {
72                     packageNames.add(packageName);
73                 }
74             }
75             return packageNames.isEmpty()
76                     ? null : new ChangedPackages(mChangedPackagesSequenceNumber, packageNames);
77         }
78     }
79 
getSequenceNumber()80     int getSequenceNumber() {
81         return mChangedPackagesSequenceNumber;
82     }
83 
iterateAll(@onNull BiConsumer<Integer, SparseArray<SparseArray<String>>> sequenceNumberAndValues)84     void iterateAll(@NonNull BiConsumer<Integer, SparseArray<SparseArray<String>>>
85             sequenceNumberAndValues) {
86         synchronized (mLock) {
87             sequenceNumberAndValues.accept(mChangedPackagesSequenceNumber,
88                     mUserIdToSequenceToPackage);
89         }
90     }
91 
updateSequenceNumber(@onNull String packageName, int[] userList)92     void updateSequenceNumber(@NonNull String packageName, int[] userList) {
93         synchronized (mLock) {
94             for (int i = userList.length - 1; i >= 0; --i) {
95                 final int userId = userList[i];
96                 SparseArray<String> changedPackages = mUserIdToSequenceToPackage.get(userId);
97                 if (changedPackages == null) {
98                     changedPackages = new SparseArray<>();
99                     mUserIdToSequenceToPackage.put(userId, changedPackages);
100                 }
101                 Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId);
102                 if (sequenceNumbers == null) {
103                     sequenceNumbers = new HashMap<>();
104                     mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
105                 }
106                 final Integer sequenceNumber = sequenceNumbers.get(packageName);
107                 if (sequenceNumber != null) {
108                     changedPackages.remove(sequenceNumber);
109                 }
110                 changedPackages.put(mChangedPackagesSequenceNumber, packageName);
111                 sequenceNumbers.put(packageName, mChangedPackagesSequenceNumber);
112             }
113             mChangedPackagesSequenceNumber++;
114         }
115     }
116 }
117