• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.SystemClock;
22 import android.util.ArrayMap;
23 import android.util.Pair;
24 
25 import com.android.internal.annotations.GuardedBy;
26 import com.android.internal.util.IndentingPrintWriter;
27 
28 import java.util.concurrent.TimeUnit;
29 
30 /**
31  * Tracks for the installer package name and installing app package name of silent updates.
32  * This class is used to throttle repeated silent updates of the same installer and application
33  * in the {@link PackageInstallerSession}.
34  */
35 public class SilentUpdatePolicy {
36     // The default throttle time to prevent the installer from silently updating the same app
37     // repeatedly.
38     private static final long SILENT_UPDATE_THROTTLE_TIME_MS = TimeUnit.SECONDS.toMillis(30);
39 
40     // Map to the uptime timestamp for each installer and app of the silent update.
41     @GuardedBy("mSilentUpdateInfos")
42     private final ArrayMap<Pair<String, String>, Long> mSilentUpdateInfos = new ArrayMap<>();
43 
44     // An installer allowed for the unlimited silent updates within the throttle time
45     @GuardedBy("mSilentUpdateInfos")
46     private String mAllowUnlimitedSilentUpdatesInstaller;
47 
48     @GuardedBy("mSilentUpdateInfos")
49     private long mSilentUpdateThrottleTimeMs = SILENT_UPDATE_THROTTLE_TIME_MS;
50 
51     /**
52      * Checks if the silent update is allowed by the given installer and app package name.
53      *
54      * @param installerPackageName The installer package name to check
55      * @param packageName The package name which is installing
56      * @return true if the silent update is allowed.
57      */
isSilentUpdateAllowed(@ullable String installerPackageName, @NonNull String packageName)58     public boolean isSilentUpdateAllowed(@Nullable String installerPackageName,
59             @NonNull String packageName) {
60         if (installerPackageName == null) {
61             // Always true for the installer from Shell
62             return true;
63         }
64         final long lastSilentUpdatedMs = getTimestampMs(installerPackageName, packageName);
65         final long throttleTimeMs;
66         synchronized (mSilentUpdateInfos) {
67             throttleTimeMs = mSilentUpdateThrottleTimeMs;
68         }
69         return SystemClock.uptimeMillis() - lastSilentUpdatedMs > throttleTimeMs;
70     }
71 
72     /**
73      * Adding track for the installer package name and installing app of a silent update. This is
74      * used to determine whether a silent update is allowed.
75      *
76      * @param installerPackageName The installer package name
77      * @param packageName The package name which is installing
78      */
track(@ullable String installerPackageName, @NonNull String packageName)79     public void track(@Nullable String installerPackageName, @NonNull String packageName) {
80         if (installerPackageName == null) {
81             // No need to track the installer from Shell.
82             return;
83         }
84         synchronized (mSilentUpdateInfos) {
85             if (mAllowUnlimitedSilentUpdatesInstaller != null
86                     && mAllowUnlimitedSilentUpdatesInstaller.equals(installerPackageName)) {
87                 return;
88             }
89             final long uptime = SystemClock.uptimeMillis();
90             pruneLocked(uptime);
91 
92             final Pair<String, String> key = Pair.create(installerPackageName, packageName);
93             mSilentUpdateInfos.put(key, uptime);
94         }
95     }
96 
97     /**
98      * Set an installer to allow for the unlimited silent updates. Reset the tracker if the
99      * installer package name is <code>null</code>.
100      */
setAllowUnlimitedSilentUpdates(@ullable String installerPackageName)101     void setAllowUnlimitedSilentUpdates(@Nullable String installerPackageName) {
102         synchronized (mSilentUpdateInfos) {
103             if (installerPackageName == null) {
104                 mSilentUpdateInfos.clear();
105             }
106             mAllowUnlimitedSilentUpdatesInstaller = installerPackageName;
107         }
108     }
109 
110     /**
111      * Set the silent updates throttle time in seconds.
112      *
113      * @param throttleTimeInSeconds The throttle time to set, or <code>-1</code> to restore the
114      *        value to the default.
115      */
setSilentUpdatesThrottleTime(long throttleTimeInSeconds)116     void setSilentUpdatesThrottleTime(long throttleTimeInSeconds) {
117         synchronized (mSilentUpdateInfos) {
118             mSilentUpdateThrottleTimeMs = throttleTimeInSeconds >= 0
119                     ? TimeUnit.SECONDS.toMillis(throttleTimeInSeconds)
120                     : SILENT_UPDATE_THROTTLE_TIME_MS;
121         }
122     }
123 
pruneLocked(long uptime)124     private void pruneLocked(long uptime) {
125         final int size = mSilentUpdateInfos.size();
126         for (int i = size - 1; i >= 0; i--) {
127             final long lastSilentUpdatedMs = mSilentUpdateInfos.valueAt(i);
128             if (uptime - lastSilentUpdatedMs > mSilentUpdateThrottleTimeMs) {
129                 mSilentUpdateInfos.removeAt(i);
130             }
131         }
132     }
133 
134     /**
135      * Get the timestamp by the given installer and app package name. {@code -1} is returned if not
136      * exist.
137      */
getTimestampMs(@onNull String installerPackageName, @NonNull String packageName)138     private long getTimestampMs(@NonNull String installerPackageName, @NonNull String packageName) {
139         final Pair<String, String> key = Pair.create(installerPackageName, packageName);
140         final Long timestampMs;
141         synchronized (mSilentUpdateInfos) {
142             timestampMs = mSilentUpdateInfos.get(key);
143         }
144         return timestampMs != null ? timestampMs : -1;
145     }
146 
dump(IndentingPrintWriter pw)147     void dump(IndentingPrintWriter pw) {
148         synchronized (mSilentUpdateInfos) {
149             if (mSilentUpdateInfos.isEmpty()) {
150                 return;
151             }
152             pw.println("Last silent updated Infos:");
153             pw.increaseIndent();
154             final int size = mSilentUpdateInfos.size();
155             for (int i = 0; i < size; i++) {
156                 final Pair<String, String> key = mSilentUpdateInfos.keyAt(i);
157                 if (key == null) {
158                     continue;
159                 }
160                 pw.printPair("installerPackageName", key.first);
161                 pw.printPair("packageName", key.second);
162                 pw.printPair("silentUpdatedMillis", mSilentUpdateInfos.valueAt(i));
163                 pw.println();
164             }
165             pw.decreaseIndent();
166         }
167     }
168 }
169