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