blob: 956161b74b4de22db405d1c5f5fb4682d1d46f95 [file] [log] [blame]
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001/*
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
17package com.android.car.watchdog;
18
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070019import static android.automotive.watchdog.internal.ResourceOveruseActionType.KILLED;
20import static android.automotive.watchdog.internal.ResourceOveruseActionType.KILLED_RECURRING_OVERUSE;
21import static android.automotive.watchdog.internal.ResourceOveruseActionType.NOT_KILLED;
22import static android.automotive.watchdog.internal.ResourceOveruseActionType.NOT_KILLED_USER_OPTED;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070023import static android.car.watchdog.CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -070024import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_CURRENT_DAY;
25import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_15_DAYS;
26import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_30_DAYS;
27import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_3_DAYS;
28import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070029import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NEVER;
30import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NO;
31import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_YES;
32import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
33import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
34import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
35
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070036import static com.android.car.watchdog.CarWatchdogService.DEBUG;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -070037import static com.android.car.watchdog.CarWatchdogService.SYSTEM_INSTANCE;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070038import static com.android.car.watchdog.CarWatchdogService.TAG;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070039import static com.android.car.watchdog.PackageInfoHandler.SHARED_PACKAGE_PREFIX;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -070040import static com.android.car.watchdog.WatchdogStorage.STATS_TEMPORAL_UNIT;
41import static com.android.car.watchdog.WatchdogStorage.ZONE_OFFSET;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070042
43import android.annotation.NonNull;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070044import android.annotation.UserIdInt;
45import android.app.ActivityThread;
46import android.automotive.watchdog.ResourceType;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070047import android.automotive.watchdog.internal.ApplicationCategoryType;
48import android.automotive.watchdog.internal.ComponentType;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070049import android.automotive.watchdog.internal.PackageIdentifier;
50import android.automotive.watchdog.internal.PackageIoOveruseStats;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070051import android.automotive.watchdog.internal.PackageMetadata;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070052import android.automotive.watchdog.internal.PackageResourceOveruseAction;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070053import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
54import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070055import android.car.watchdog.CarWatchdogManager;
56import android.car.watchdog.IResourceOveruseListener;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070057import android.car.watchdog.IoOveruseAlertThreshold;
58import android.car.watchdog.IoOveruseConfiguration;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070059import android.car.watchdog.IoOveruseStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070060import android.car.watchdog.PackageKillableState;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070061import android.car.watchdog.PackageKillableState.KillableState;
62import android.car.watchdog.PerStateBytes;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070063import android.car.watchdog.ResourceOveruseConfiguration;
64import android.car.watchdog.ResourceOveruseStats;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070065import android.car.watchdoglib.CarWatchdogDaemonHelper;
66import android.content.Context;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070067import android.content.pm.ApplicationInfo;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070068import android.content.pm.IPackageManager;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070069import android.content.pm.PackageInfo;
70import android.content.pm.PackageManager;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070071import android.os.Binder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070072import android.os.Handler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070073import android.os.IBinder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070074import android.os.Looper;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070075import android.os.RemoteException;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070076import android.os.SystemClock;
77import android.os.TransactionTooLargeException;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070078import android.os.UserHandle;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070079import android.os.UserManager;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070080import android.util.ArrayMap;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070081import android.util.ArraySet;
82import android.util.IndentingPrintWriter;
83import android.util.SparseArray;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +000084import android.util.SparseBooleanArray;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070085
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -070086import com.android.car.CarServiceUtils;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070087import com.android.internal.annotations.GuardedBy;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070088import com.android.internal.annotations.VisibleForTesting;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070089import com.android.internal.util.Preconditions;
90import com.android.server.utils.Slogf;
91
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070092import java.time.ZoneOffset;
93import java.time.ZonedDateTime;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070094import java.util.ArrayList;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070095import java.util.Collections;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070096import java.util.List;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070097import java.util.Map;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070098import java.util.Objects;
99import java.util.Set;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700100import java.util.function.BiFunction;
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700101import java.util.function.Consumer;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700102
103/**
104 * Handles system resource performance monitoring module.
105 */
106public final class WatchdogPerfHandler {
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700107 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS = "MAPS";
108 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA";
109 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN";
110
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000111 static final long RESOURCE_OVERUSE_KILLING_DELAY_MILLS = 10_000;
112
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700113 private static final long MAX_WAIT_TIME_MILLS = 3_000;
114
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700115 private final Context mContext;
116 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
117 private final PackageInfoHandler mPackageInfoHandler;
118 private final Handler mMainHandler;
Keun young Park5dc5a0c2021-09-29 14:44:27 -0700119 private final Handler mServiceHandler;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700120 private final WatchdogStorage mWatchdogStorage;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700121 private final Object mLock = new Object();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700122 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700123 private final ArrayMap<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700124 @GuardedBy("mLock")
125 private final List<PackageResourceOveruseAction> mOveruseActionsByUserPackage =
126 new ArrayList<>();
127 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700128 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>> mOveruseListenerInfosByUid =
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700129 new SparseArray<>();
130 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700131 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>>
132 mOveruseSystemListenerInfosByUid = new SparseArray<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700133 /* Set of safe-to-kill system and vendor packages. */
134 @GuardedBy("mLock")
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000135 public final ArraySet<String> mSafeToKillSystemPackages = new ArraySet<>();
136 @GuardedBy("mLock")
137 public final ArraySet<String> mSafeToKillVendorPackages = new ArraySet<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700138 /* Default killable state for packages when not updated by the user. */
139 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700140 public final ArraySet<String> mDefaultNotKillableGenericPackages = new ArraySet<>();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700141 @GuardedBy("mLock")
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700142 private ZonedDateTime mLatestStatsReportDate;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700143 @GuardedBy("mLock")
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700144 private List<android.automotive.watchdog.internal.ResourceOveruseConfiguration>
145 mPendingSetResourceOveruseConfigurationsRequest = null;
146 @GuardedBy("mLock")
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700147 boolean mIsConnectedToDaemon;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000148 @GuardedBy("mLock")
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700149 boolean mIsWrittenToDatabase;
150 @GuardedBy("mLock")
151 private TimeSourceInterface mTimeSource;
152 @GuardedBy("mLock")
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000153 long mResourceOveruseKillingDelayMills;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700154
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700155 public WatchdogPerfHandler(Context context, CarWatchdogDaemonHelper daemonHelper,
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700156 PackageInfoHandler packageInfoHandler, WatchdogStorage watchdogStorage) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700157 mContext = context;
158 mCarWatchdogDaemonHelper = daemonHelper;
159 mPackageInfoHandler = packageInfoHandler;
160 mMainHandler = new Handler(Looper.getMainLooper());
Keun young Park5dc5a0c2021-09-29 14:44:27 -0700161 mServiceHandler = new Handler(CarServiceUtils.getHandlerThread(
162 CarWatchdogService.class.getSimpleName()).getLooper());
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700163 mWatchdogStorage = watchdogStorage;
164 mTimeSource = SYSTEM_INSTANCE;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000165 mResourceOveruseKillingDelayMills = RESOURCE_OVERUSE_KILLING_DELAY_MILLS;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700166 }
167
168 /** Initializes the handler. */
169 public void init() {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700170 /* First database read is expensive, so post it on a separate handler thread. */
Keun young Park5dc5a0c2021-09-29 14:44:27 -0700171 mServiceHandler.post(() -> {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700172 readFromDatabase();
173 synchronized (mLock) {
174 checkAndHandleDateChangeLocked();
175 mIsWrittenToDatabase = false;
176 }});
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700177 if (DEBUG) {
178 Slogf.d(TAG, "WatchdogPerfHandler is initialized");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700179 }
180 }
181
182 /** Dumps its state. */
183 public void dump(IndentingPrintWriter writer) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700184 /*
185 * TODO(b/183436216): Implement this method.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700186 */
187 }
188
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700189 /** Retries any pending requests on re-connecting to the daemon */
190 public void onDaemonConnectionChange(boolean isConnected) {
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700191 boolean hasPendingRequest;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700192 synchronized (mLock) {
193 mIsConnectedToDaemon = isConnected;
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700194 hasPendingRequest = mPendingSetResourceOveruseConfigurationsRequest != null;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700195 }
196 if (isConnected) {
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700197 if (hasPendingRequest) {
198 /*
199 * Retry pending set resource overuse configuration request before processing any
200 * new set/get requests. Thus notify the waiting requests only after the retry
201 * completes.
202 */
203 retryPendingSetResourceOveruseConfigurations();
204 } else {
205 /* Start fetch/sync configs only when there are no pending set requests because the
206 * above retry starts fetch/sync configs on success. If the retry fails, the daemon
207 * has crashed and shouldn't start fetchAndSyncResourceOveruseConfigurations.
208 */
209 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
210 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700211 }
212 synchronized (mLock) {
213 mLock.notifyAll();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700214 }
215 }
216
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700217 /** Returns resource overuse stats for the calling package. */
218 @NonNull
219 public ResourceOveruseStats getResourceOveruseStats(
220 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
221 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
222 Preconditions.checkArgument((resourceOveruseFlag > 0),
223 "Must provide valid resource overuse flag");
224 Preconditions.checkArgument((maxStatsPeriod > 0),
225 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700226 // When more resource stats are added, make this as optional.
227 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
228 "Must provide resource I/O overuse flag");
229 int callingUid = Binder.getCallingUid();
230 int callingUserId = UserHandle.getUserId(callingUid);
231 UserHandle callingUserHandle = UserHandle.of(callingUserId);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700232 String genericPackageName =
233 mPackageInfoHandler.getNamesForUids(new int[]{callingUid})
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700234 .get(callingUid, null);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700235 if (genericPackageName == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700236 Slogf.w(TAG, "Failed to fetch package info for uid %d", callingUid);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700237 return new ResourceOveruseStats.Builder("", callingUserHandle).build();
238 }
239 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700240 new ResourceOveruseStats.Builder(genericPackageName, callingUserHandle);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700241 statsBuilder.setIoOveruseStats(
242 getIoOveruseStatsForPeriod(callingUserId, genericPackageName, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700243 if (DEBUG) {
244 Slogf.d(TAG, "Returning all resource overuse stats for calling uid %d [user %d and "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700245 + "package '%s']", callingUid, callingUserId, genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700246 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700247 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700248 }
249
250 /** Returns resource overuse stats for all packages. */
251 @NonNull
252 public List<ResourceOveruseStats> getAllResourceOveruseStats(
253 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
254 @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag,
255 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
256 Preconditions.checkArgument((resourceOveruseFlag > 0),
257 "Must provide valid resource overuse flag");
258 Preconditions.checkArgument((maxStatsPeriod > 0),
259 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700260 // When more resource types are added, make this as optional.
261 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
262 "Must provide resource I/O overuse flag");
263 long minimumBytesWritten = getMinimumBytesWritten(minimumStatsFlag);
264 List<ResourceOveruseStats> allStats = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700265 synchronized (mLock) {
266 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
267 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
268 ResourceOveruseStats.Builder statsBuilder = usage.getResourceOveruseStatsBuilder();
269 IoOveruseStats ioOveruseStats =
270 getIoOveruseStatsLocked(usage, minimumBytesWritten, maxStatsPeriod);
271 if (ioOveruseStats == null) {
272 continue;
273 }
274 allStats.add(statsBuilder.setIoOveruseStats(ioOveruseStats).build());
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700275 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700276 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700277 if (DEBUG) {
278 Slogf.d(TAG, "Returning all resource overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700279 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700280 return allStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700281 }
282
283 /** Returns resource overuse stats for the specified user package. */
284 @NonNull
285 public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
286 @NonNull String packageName, @NonNull UserHandle userHandle,
287 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
288 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
289 Objects.requireNonNull(packageName, "Package name must be non-null");
290 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700291 Preconditions.checkArgument(!userHandle.equals(UserHandle.ALL),
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700292 "Must provide the user handle for a specific user");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700293 Preconditions.checkArgument((resourceOveruseFlag > 0),
294 "Must provide valid resource overuse flag");
295 Preconditions.checkArgument((maxStatsPeriod > 0),
296 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700297 // When more resource types are added, make this as optional.
298 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
299 "Must provide resource I/O overuse flag");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700300 String genericPackageName =
301 mPackageInfoHandler.getNameForUserPackage(packageName, userHandle.getIdentifier());
302 if (genericPackageName == null) {
303 throw new IllegalArgumentException("Package '" + packageName + "' not found");
304 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700305 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700306 new ResourceOveruseStats.Builder(genericPackageName, userHandle);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700307 statsBuilder.setIoOveruseStats(getIoOveruseStatsForPeriod(userHandle.getIdentifier(),
308 genericPackageName, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700309 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700310 Slogf.d(TAG, "Returning resource overuse stats for user %d, package '%s', "
311 + "generic package '%s'", userHandle.getIdentifier(), packageName,
312 genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700313 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700314 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700315 }
316
317 /** Adds the resource overuse listener. */
318 public void addResourceOveruseListener(
319 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
320 @NonNull IResourceOveruseListener listener) {
321 Objects.requireNonNull(listener, "Listener must be non-null");
322 Preconditions.checkArgument((resourceOveruseFlag > 0),
323 "Must provide valid resource overuse flag");
324 synchronized (mLock) {
325 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
326 mOveruseListenerInfosByUid);
327 }
328 }
329
330 /** Removes the previously added resource overuse listener. */
331 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
332 Objects.requireNonNull(listener, "Listener must be non-null");
333 synchronized (mLock) {
334 removeResourceOveruseListenerLocked(listener, mOveruseListenerInfosByUid);
335 }
336 }
337
338 /** Adds the resource overuse system listener. */
339 public void addResourceOveruseListenerForSystem(
340 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
341 @NonNull IResourceOveruseListener listener) {
342 Objects.requireNonNull(listener, "Listener must be non-null");
343 Preconditions.checkArgument((resourceOveruseFlag > 0),
344 "Must provide valid resource overuse flag");
345 synchronized (mLock) {
346 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
347 mOveruseSystemListenerInfosByUid);
348 }
349 }
350
351 /** Removes the previously added resource overuse system listener. */
352 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
353 Objects.requireNonNull(listener, "Listener must be non-null");
354 synchronized (mLock) {
355 removeResourceOveruseListenerLocked(listener, mOveruseSystemListenerInfosByUid);
356 }
357 }
358
359 /** Sets whether or not a package is killable on resource overuse. */
360 public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
361 boolean isKillable) {
362 Objects.requireNonNull(packageName, "Package name must be non-null");
363 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700364
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700365 if (userHandle.equals(UserHandle.ALL)) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700366 setPackageKillableStateForAllUsers(packageName, isKillable);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700367 return;
368 }
369 int userId = userHandle.getIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700370 String genericPackageName = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
371 if (genericPackageName == null) {
372 throw new IllegalArgumentException("Package '" + packageName + "' not found");
373 }
374 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700375 synchronized (mLock) {
376 /*
377 * When the queried package is not cached in {@link mUsageByUserPackage}, the set API
378 * will update the killable state even when the package should never be killed.
379 * But the get API will return the correct killable state. This behavior is tolerable
380 * because in production the set API should be called only after the get API.
381 * For instance, when this case happens by mistake and the package overuses resource
382 * between the set and the get API calls, the daemon will provide correct killable
383 * state when pushing the latest stats. Ergo, the invalid killable state doesn't have
384 * any effect.
385 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700386 PackageResourceUsage usage = mUsageByUserPackage.get(key);
387 if (usage == null) {
388 usage = new PackageResourceUsage(userId, genericPackageName);
389 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700390 if (!usage.verifyAndSetKillableStateLocked(isKillable)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700391 Slogf.e(TAG, "User %d cannot set killable state for package '%s'",
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700392 userHandle.getIdentifier(), genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700393 throw new IllegalArgumentException("Package killable state is not updatable");
394 }
395 mUsageByUserPackage.put(key, usage);
396 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700397 if (DEBUG) {
398 Slogf.d(TAG, "Successfully set killable package state for user %d", userId);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700399 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700400 }
401
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700402 private void setPackageKillableStateForAllUsers(String packageName, boolean isKillable) {
Jahdiel Alvarez9de7aa52021-09-10 04:39:23 +0000403 int[] userIds = getAliveUserIds();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700404 String genericPackageName = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700405 synchronized (mLock) {
Jahdiel Alvarez9de7aa52021-09-10 04:39:23 +0000406 for (int i = 0; i < userIds.length; ++i) {
407 int userId = userIds[i];
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700408 String name = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
409 if (name == null) {
410 continue;
411 }
412 genericPackageName = name;
413 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700414 PackageResourceUsage usage = mUsageByUserPackage.get(key);
415 if (usage == null) {
416 continue;
417 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700418 if (!usage.verifyAndSetKillableStateLocked(isKillable)) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700419 Slogf.e(TAG, "Cannot set killable state for package '%s'", packageName);
420 throw new IllegalArgumentException(
421 "Package killable state is not updatable");
422 }
423 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700424 if (genericPackageName != null) {
425 if (!isKillable) {
426 mDefaultNotKillableGenericPackages.add(genericPackageName);
427 } else {
428 mDefaultNotKillableGenericPackages.remove(genericPackageName);
429 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700430 }
431 }
432 if (DEBUG) {
433 Slogf.d(TAG, "Successfully set killable package state for all users");
434 }
435 }
436
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700437 /** Returns the list of package killable states on resource overuse for the user. */
438 @NonNull
439 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
440 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700441 PackageManager pm = mContext.getPackageManager();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700442 if (!userHandle.equals(UserHandle.ALL)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700443 if (DEBUG) {
444 Slogf.d(TAG, "Returning all package killable states for user %d",
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700445 userHandle.getIdentifier());
446 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700447 return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
448 }
449 List<PackageKillableState> packageKillableStates = new ArrayList<>();
Jahdiel Alvarez9de7aa52021-09-10 04:39:23 +0000450 int[] userIds = getAliveUserIds();
451 for (int i = 0; i < userIds.length; ++i) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700452 packageKillableStates.addAll(
Jahdiel Alvarez9de7aa52021-09-10 04:39:23 +0000453 getPackageKillableStatesForUserId(userIds[i], pm));
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700454 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700455 if (DEBUG) {
456 Slogf.d(TAG, "Returning all package killable states for all users");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700457 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700458 return packageKillableStates;
459 }
460
461 private List<PackageKillableState> getPackageKillableStatesForUserId(int userId,
462 PackageManager pm) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700463 List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */ 0, userId);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700464 List<PackageKillableState> states = new ArrayList<>();
465 synchronized (mLock) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700466 ArrayMap<String, List<ApplicationInfo>> applicationInfosBySharedPackage =
467 new ArrayMap<>();
468 for (int i = 0; i < packageInfos.size(); ++i) {
469 PackageInfo packageInfo = packageInfos.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700470 String genericPackageName = mPackageInfoHandler.getNameForPackage(packageInfo);
471 if (packageInfo.sharedUserId == null) {
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000472 int componentType = mPackageInfoHandler.getComponentType(
473 packageInfo.applicationInfo);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700474 int killableState = getPackageKillableStateForUserPackageLocked(
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000475 userId, genericPackageName, componentType,
476 isSafeToKillLocked(genericPackageName, componentType, null));
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700477 states.add(new PackageKillableState(packageInfo.packageName, userId,
478 killableState));
479 continue;
480 }
481 List<ApplicationInfo> applicationInfos =
482 applicationInfosBySharedPackage.get(genericPackageName);
483 if (applicationInfos == null) {
484 applicationInfos = new ArrayList<>();
485 }
486 applicationInfos.add(packageInfo.applicationInfo);
487 applicationInfosBySharedPackage.put(genericPackageName, applicationInfos);
488 }
489 for (Map.Entry<String, List<ApplicationInfo>> entry :
490 applicationInfosBySharedPackage.entrySet()) {
491 String genericPackageName = entry.getKey();
492 List<ApplicationInfo> applicationInfos = entry.getValue();
493 int componentType = mPackageInfoHandler.getSharedComponentType(
494 applicationInfos, genericPackageName);
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000495 List<String> packageNames = new ArrayList<>(applicationInfos.size());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700496 for (int i = 0; i < applicationInfos.size(); ++i) {
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000497 packageNames.add(applicationInfos.get(i).packageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700498 }
499 int killableState = getPackageKillableStateForUserPackageLocked(
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000500 userId, genericPackageName, componentType,
501 isSafeToKillLocked(genericPackageName, componentType, packageNames));
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700502 for (int i = 0; i < applicationInfos.size(); ++i) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700503 states.add(new PackageKillableState(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700504 applicationInfos.get(i).packageName, userId, killableState));
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700505 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700506 }
507 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700508 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700509 Slogf.d(TAG, "Returning the package killable states for user packages");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700510 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700511 return states;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700512 }
513
514 /** Sets the given resource overuse configurations. */
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700515 @CarWatchdogManager.ReturnCode
516 public int setResourceOveruseConfigurations(
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700517 List<ResourceOveruseConfiguration> configurations,
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700518 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
519 throws RemoteException {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700520 Objects.requireNonNull(configurations, "Configurations must be non-null");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700521 Preconditions.checkArgument((configurations.size() > 0),
522 "Must provide at least one configuration");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700523 Preconditions.checkArgument((resourceOveruseFlag > 0),
524 "Must provide valid resource overuse flag");
Jahdiel Alvarez61481142021-07-31 00:13:19 +0000525 checkResourceOveruseConfigs(configurations, resourceOveruseFlag);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700526 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
527 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700528 for (int i = 0; i < configurations.size(); ++i) {
Jahdiel Alvarez61481142021-07-31 00:13:19 +0000529 internalConfigs.add(toInternalResourceOveruseConfiguration(configurations.get(i),
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700530 resourceOveruseFlag));
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700531 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700532 synchronized (mLock) {
533 if (!mIsConnectedToDaemon) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700534 setPendingSetResourceOveruseConfigurationsRequestLocked(internalConfigs);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700535 return CarWatchdogManager.RETURN_CODE_SUCCESS;
536 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700537 /* Verify no pending request in progress. */
538 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700539 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700540 return setResourceOveruseConfigurationsInternal(internalConfigs,
541 /* isPendingRequest= */ false);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700542 }
543
544 /** Returns the available resource overuse configurations. */
545 @NonNull
546 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
547 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
548 Preconditions.checkArgument((resourceOveruseFlag > 0),
549 "Must provide valid resource overuse flag");
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700550 if (!isConnectedToDaemon()) {
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700551 throw new IllegalStateException("Car watchdog daemon is not connected");
552 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700553 synchronized (mLock) {
554 /* Verify no pending request in progress. */
555 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
556 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700557 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
558 new ArrayList<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700559 try {
560 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
561 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700562 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700563 throw new IllegalStateException(e);
564 }
565 List<ResourceOveruseConfiguration> configs = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700566 for (int i = 0; i < internalConfigs.size(); ++i) {
567 configs.add(
568 toResourceOveruseConfiguration(internalConfigs.get(i), resourceOveruseFlag));
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700569 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700570 if (DEBUG) {
571 Slogf.d(TAG, "Returning the resource overuse configuration");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700572 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700573 return configs;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700574 }
575
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700576 /** Processes the latest I/O overuse stats */
577 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000578 SparseBooleanArray recurringIoOverusesByUid = new SparseBooleanArray();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700579 int[] uids = new int[packageIoOveruseStats.size()];
580 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
581 uids[i] = packageIoOveruseStats.get(i).uid;
582 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700583 SparseArray<String> genericPackageNamesByUid = mPackageInfoHandler.getNamesForUids(uids);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700584 synchronized (mLock) {
585 checkAndHandleDateChangeLocked();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700586 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
587 PackageIoOveruseStats stats = packageIoOveruseStats.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700588 String genericPackageName = genericPackageNamesByUid.get(stats.uid);
589 if (genericPackageName == null) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700590 continue;
591 }
592 int userId = UserHandle.getUserId(stats.uid);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700593 PackageResourceUsage usage = cacheAndFetchUsageLocked(userId, genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700594 stats.ioOveruseStats);
595 if (stats.shouldNotify) {
596 /*
597 * Packages that exceed the warn threshold percentage should be notified as well
598 * and only the daemon is aware of such packages. Thus the flag is used to
599 * indicate which packages should be notified.
600 */
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700601 ResourceOveruseStats resourceOveruseStats =
602 usage.getResourceOveruseStatsBuilder().setIoOveruseStats(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700603 usage.getIoOveruseStatsLocked()).build();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700604 notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700605 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700606
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700607 if (!usage.ioUsage.exceedsThreshold()) {
608 continue;
609 }
610 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
611 overuseAction.packageIdentifier = new PackageIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700612 overuseAction.packageIdentifier.name = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700613 overuseAction.packageIdentifier.uid = stats.uid;
614 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
615 overuseAction.resourceOveruseActionType = NOT_KILLED;
616 /*
617 * No action required on I/O overuse on one of the following cases:
618 * #1 The package is not safe to kill as it is critical for system stability.
619 * #2 The package has no recurring overuse behavior and the user opted to not
620 * kill the package so honor the user's decision.
621 */
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700622 int killableState = usage.getKillableStateLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700623 if (killableState == KILLABLE_STATE_NEVER) {
624 mOveruseActionsByUserPackage.add(overuseAction);
625 continue;
626 }
627 boolean hasRecurringOveruse = isRecurringOveruseLocked(usage);
628 if (!hasRecurringOveruse && killableState == KILLABLE_STATE_NO) {
629 overuseAction.resourceOveruseActionType = NOT_KILLED_USER_OPTED;
630 mOveruseActionsByUserPackage.add(overuseAction);
631 continue;
632 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000633 recurringIoOverusesByUid.put(stats.uid, hasRecurringOveruse);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700634 }
635 if (!mOveruseActionsByUserPackage.isEmpty()) {
Lakshman Annadoraia7189562021-08-17 16:36:16 -0700636 mMainHandler.post(this::notifyActionsTakenOnOveruse);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700637 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000638 if (recurringIoOverusesByUid.size() > 0) {
Lakshman Annadoraia7189562021-08-17 16:36:16 -0700639 mMainHandler.postDelayed(
640 () -> handleIoOveruseKilling(
641 recurringIoOverusesByUid, genericPackageNamesByUid),
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000642 mResourceOveruseKillingDelayMills);
643 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700644 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700645 if (DEBUG) {
646 Slogf.d(TAG, "Processed latest I/O overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700647 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700648 }
649
650 /** Notify daemon about the actions take on resource overuse */
651 public void notifyActionsTakenOnOveruse() {
652 List<PackageResourceOveruseAction> actions;
653 synchronized (mLock) {
654 if (mOveruseActionsByUserPackage.isEmpty()) {
655 return;
656 }
657 actions = new ArrayList<>(mOveruseActionsByUserPackage);
658 mOveruseActionsByUserPackage.clear();
659 }
660 try {
661 mCarWatchdogDaemonHelper.actionTakenOnResourceOveruse(actions);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700662 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700663 Slogf.w(TAG, e, "Failed to notify car watchdog daemon of actions taken on resource "
664 + "overuse");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700665 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700666 if (DEBUG) {
667 Slogf.d(TAG, "Notified car watchdog daemon of actions taken on resource overuse");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700668 }
669 }
670
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000671 /** Handle packages that exceed resource overuse thresholds */
672 private void handleIoOveruseKilling(SparseBooleanArray recurringIoOverusesByUid,
673 SparseArray<String> genericPackageNamesByUid) {
674 IPackageManager packageManager = ActivityThread.getPackageManager();
675 synchronized (mLock) {
676 for (int i = 0; i < recurringIoOverusesByUid.size(); i++) {
677 int uid = recurringIoOverusesByUid.keyAt(i);
678 boolean hasRecurringOveruse = recurringIoOverusesByUid.valueAt(i);
679 String genericPackageName = genericPackageNamesByUid.get(uid);
680 int userId = UserHandle.getUserId(uid);
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000681
682 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
683 overuseAction.packageIdentifier = new PackageIdentifier();
684 overuseAction.packageIdentifier.name = genericPackageName;
685 overuseAction.packageIdentifier.uid = uid;
686 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
687 overuseAction.resourceOveruseActionType = NOT_KILLED;
688
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700689 String key = getUserPackageUniqueId(userId, genericPackageName);
690 PackageResourceUsage usage = mUsageByUserPackage.get(key);
691 if (usage == null) {
692 /* This case shouldn't happen but placed here as a fail safe. */
693 mOveruseActionsByUserPackage.add(overuseAction);
694 continue;
695 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000696 List<String> packages = Collections.singletonList(genericPackageName);
697 if (usage.isSharedPackage()) {
698 packages = mPackageInfoHandler.getPackagesForUid(uid, genericPackageName);
699 }
700 for (int pkgIdx = 0; pkgIdx < packages.size(); pkgIdx++) {
701 String packageName = packages.get(pkgIdx);
702 try {
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000703 if (!hasRecurringOveruse) {
Lakshman Annadoraicda55ca2021-09-16 14:33:04 -0700704 int currentEnabledState = packageManager.getApplicationEnabledSetting(
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000705 packageName, userId);
Lakshman Annadoraicda55ca2021-09-16 14:33:04 -0700706 if (currentEnabledState == COMPONENT_ENABLED_STATE_DISABLED
707 || currentEnabledState == COMPONENT_ENABLED_STATE_DISABLED_USER
708 || currentEnabledState
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000709 == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
710 continue;
711 }
712 }
713 packageManager.setApplicationEnabledSetting(packageName,
714 COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId,
715 mContext.getPackageName());
716 overuseAction.resourceOveruseActionType = hasRecurringOveruse
717 ? KILLED_RECURRING_OVERUSE : KILLED;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000718 } catch (RemoteException e) {
719 Slogf.e(TAG, "Failed to disable application for user %d, package '%s'",
720 userId, packageName);
721 }
722 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700723 if (overuseAction.resourceOveruseActionType == KILLED
724 || overuseAction.resourceOveruseActionType == KILLED_RECURRING_OVERUSE) {
725 usage.ioUsage.killed();
726 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000727 mOveruseActionsByUserPackage.add(overuseAction);
728 }
729 if (!mOveruseActionsByUserPackage.isEmpty()) {
730 notifyActionsTakenOnOveruse();
731 }
732 }
733 }
734
Jahdiel Alvarezf91bf312021-09-04 01:03:50 +0000735 /** Resets the resource overuse settings and stats for the given generic package names. */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700736 public void resetResourceOveruseStats(Set<String> genericPackageNames) {
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700737 synchronized (mLock) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700738 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
739 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700740 if (genericPackageNames.contains(usage.genericPackageName)) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700741 usage.resetStatsLocked();
Jahdiel Alvarezf91bf312021-09-04 01:03:50 +0000742 usage.verifyAndSetKillableStateLocked(/* isKillable= */ true);
743 mWatchdogStorage.deleteUserPackage(usage.userId, usage.genericPackageName);
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700744 }
745 }
746 }
747 }
748
Jahdiel Alvarez9de7aa52021-09-10 04:39:23 +0000749 /** Deletes all data for specific user. */
750 public void deleteUser(@UserIdInt int userId) {
751 synchronized (mLock) {
752 for (int i = mUsageByUserPackage.size() - 1; i >= 0; --i) {
753 if (userId == mUsageByUserPackage.valueAt(i).userId) {
754 mUsageByUserPackage.removeAt(i);
755 }
756 }
757 mWatchdogStorage.syncUsers(getAliveUserIds());
758 }
759 if (DEBUG) {
760 Slogf.d(TAG, "Resource usage for user id: %d was deleted.", userId);
761 }
762 }
763
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700764 /** Sets the time source. */
765 public void setTimeSource(TimeSourceInterface timeSource) {
766 synchronized (mLock) {
767 mTimeSource = timeSource;
768 }
769 }
770
771 /** Sets the delay to kill a package after the package is notified of resource overuse. */
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000772 public void setResourceOveruseKillingDelay(long millis) {
773 synchronized (mLock) {
774 mResourceOveruseKillingDelayMills = millis;
775 }
776 }
777
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000778 /** Fetches and syncs the resource overuse configurations from watchdog daemon. */
779 private void fetchAndSyncResourceOveruseConfigurations() {
780 synchronized (mLock) {
781 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs;
782 try {
783 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
784 } catch (RemoteException | RuntimeException e) {
785 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
786 return;
787 }
788 if (internalConfigs.isEmpty()) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700789 Slogf.e(TAG, "Fetched resource overuse configurations are empty");
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000790 return;
791 }
792 mSafeToKillSystemPackages.clear();
793 mSafeToKillVendorPackages.clear();
794 for (int i = 0; i < internalConfigs.size(); i++) {
795 switch (internalConfigs.get(i).componentType) {
796 case ComponentType.SYSTEM:
797 mSafeToKillSystemPackages.addAll(internalConfigs.get(i).safeToKillPackages);
798 break;
799 case ComponentType.VENDOR:
800 mSafeToKillVendorPackages.addAll(internalConfigs.get(i).safeToKillPackages);
Jahdiel Alvarez5528c712021-08-10 21:07:40 +0000801 mPackageInfoHandler.setVendorPackagePrefixes(
802 internalConfigs.get(i).vendorPackagePrefixes);
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000803 break;
804 default:
805 // All third-party apps are killable.
806 break;
807 }
808 }
809 if (DEBUG) {
Jahdiel Alvarez5528c712021-08-10 21:07:40 +0000810 Slogf.d(TAG, "Fetched and synced resource overuse configs.");
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000811 }
812 }
813 }
814
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700815 private void readFromDatabase() {
Jahdiel Alvarez9de7aa52021-09-10 04:39:23 +0000816 mWatchdogStorage.syncUsers(getAliveUserIds());
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700817 List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries =
818 mWatchdogStorage.getUserPackageSettings();
819 Slogf.i(TAG, "Read %d user package settings from database", settingsEntries.size());
820 List<WatchdogStorage.IoUsageStatsEntry> ioStatsEntries =
821 mWatchdogStorage.getTodayIoUsageStats();
822 Slogf.i(TAG, "Read %d I/O usage stats from database", ioStatsEntries.size());
823 synchronized (mLock) {
824 for (int i = 0; i < settingsEntries.size(); ++i) {
825 WatchdogStorage.UserPackageSettingsEntry entry = settingsEntries.get(i);
826 if (entry.userId == UserHandle.USER_ALL) {
827 if (entry.killableState != KILLABLE_STATE_YES) {
828 mDefaultNotKillableGenericPackages.add(entry.packageName);
829 }
830 continue;
831 }
832 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
833 PackageResourceUsage usage = mUsageByUserPackage.get(key);
834 if (usage == null) {
835 usage = new PackageResourceUsage(entry.userId, entry.packageName);
836 }
837 usage.setKillableStateLocked(entry.killableState);
838 mUsageByUserPackage.put(key, usage);
839 }
840 ZonedDateTime curReportDate =
841 mTimeSource.now().atZone(ZONE_OFFSET).truncatedTo(STATS_TEMPORAL_UNIT);
842 for (int i = 0; i < ioStatsEntries.size(); ++i) {
843 WatchdogStorage.IoUsageStatsEntry entry = ioStatsEntries.get(i);
844 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
845 PackageResourceUsage usage = mUsageByUserPackage.get(key);
846 if (usage == null) {
847 usage = new PackageResourceUsage(entry.userId, entry.packageName);
848 }
849 /* Overwrite in memory cache as the stats will be merged on the daemon side and
850 * pushed on the next latestIoOveruseStats call. This is tolerable because the next
851 * push should happen soon.
852 */
853 usage.ioUsage.overwrite(entry.ioUsage);
854 mUsageByUserPackage.put(key, usage);
855 }
856 if (!ioStatsEntries.isEmpty()) {
857 /* When mLatestStatsReportDate is null, the latest stats push from daemon hasn't
858 * happened yet. Thus the cached stats contains only the stats read from database.
859 */
860 mIsWrittenToDatabase = mLatestStatsReportDate == null;
861 mLatestStatsReportDate = curReportDate;
862 }
863 }
864 }
865
866 /** Writes user package settings and stats to database. */
867 public void writeToDatabase() {
868 synchronized (mLock) {
869 if (mIsWrittenToDatabase) {
870 return;
871 }
872 List<WatchdogStorage.UserPackageSettingsEntry> entries =
873 new ArrayList<>(mUsageByUserPackage.size());
874 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
875 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
876 entries.add(new WatchdogStorage.UserPackageSettingsEntry(
877 usage.userId, usage.genericPackageName, usage.getKillableStateLocked()));
878 }
879 for (String packageName : mDefaultNotKillableGenericPackages) {
880 entries.add(new WatchdogStorage.UserPackageSettingsEntry(
881 UserHandle.USER_ALL, packageName, KILLABLE_STATE_NO));
882 }
883 if (!mWatchdogStorage.saveUserPackageSettings(entries)) {
884 Slogf.e(TAG, "Failed to write user package settings to database");
885 } else {
886 Slogf.i(TAG, "Successfully saved %d user package settings to database",
887 entries.size());
888 }
889 writeStatsLocked();
890 mIsWrittenToDatabase = true;
891 }
892 }
893
894 @GuardedBy("mLock")
895 private void writeStatsLocked() {
896 List<WatchdogStorage.IoUsageStatsEntry> entries =
897 new ArrayList<>(mUsageByUserPackage.size());
898 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
899 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
900 if (!usage.ioUsage.hasUsage()) {
901 continue;
902 }
903 entries.add(new WatchdogStorage.IoUsageStatsEntry(
904 usage.userId, usage.genericPackageName, usage.ioUsage));
905 }
906 if (!mWatchdogStorage.saveIoUsageStats(entries)) {
907 Slogf.e(TAG, "Failed to write %d I/O overuse stats to database", entries.size());
908 } else {
909 Slogf.i(TAG, "Successfully saved %d I/O overuse stats to database", entries.size());
910 }
911 }
912
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700913 @GuardedBy("mLock")
914 private int getPackageKillableStateForUserPackageLocked(
915 int userId, String genericPackageName, int componentType, boolean isSafeToKill) {
916 String key = getUserPackageUniqueId(userId, genericPackageName);
917 PackageResourceUsage usage = mUsageByUserPackage.get(key);
918 if (usage == null) {
919 usage = new PackageResourceUsage(userId, genericPackageName);
920 }
921 int killableState = usage.syncAndFetchKillableStateLocked(componentType, isSafeToKill);
922 mUsageByUserPackage.put(key, usage);
923 return killableState;
924 }
925
926 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700927 private void notifyResourceOveruseStatsLocked(int uid,
928 ResourceOveruseStats resourceOveruseStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700929 String genericPackageName = resourceOveruseStats.getPackageName();
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700930 ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid);
931 if (listenerInfos != null) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700932 for (int i = 0; i < listenerInfos.size(); ++i) {
933 listenerInfos.get(i).notifyListener(
934 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700935 }
936 }
937 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700938 ArrayList<ResourceOveruseListenerInfo> systemListenerInfos =
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700939 mOveruseSystemListenerInfosByUid.valueAt(i);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700940 for (int j = 0; j < systemListenerInfos.size(); ++j) {
941 systemListenerInfos.get(j).notifyListener(
942 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700943 }
944 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700945 if (DEBUG) {
946 Slogf.d(TAG, "Notified resource overuse stats to listening applications");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700947 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700948 }
949
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700950 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700951 private void checkAndHandleDateChangeLocked() {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700952 ZonedDateTime currentDate = mTimeSource.now().atZone(ZoneOffset.UTC)
953 .truncatedTo(STATS_TEMPORAL_UNIT);
954 if (currentDate.equals(mLatestStatsReportDate)) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700955 return;
956 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700957 /* After the first database read or on the first stats sync from the daemon, whichever
958 * happens first, the cached stats would either be empty or initialized from the database.
959 * In either case, don't write to database.
960 */
961 if (mLatestStatsReportDate != null && !mIsWrittenToDatabase) {
962 writeStatsLocked();
963 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700964 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
Lakshman Annadoraicda55ca2021-09-16 14:33:04 -0700965 mUsageByUserPackage.valueAt(i).resetStatsLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700966 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700967 mLatestStatsReportDate = currentDate;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700968 if (DEBUG) {
969 Slogf.d(TAG, "Handled date change successfully");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700970 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700971 }
972
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700973 @GuardedBy("mLock")
974 private PackageResourceUsage cacheAndFetchUsageLocked(
975 @UserIdInt int userId, String genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700976 android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700977 String key = getUserPackageUniqueId(userId, genericPackageName);
978 PackageResourceUsage usage = mUsageByUserPackage.get(key);
979 if (usage == null) {
980 usage = new PackageResourceUsage(userId, genericPackageName);
981 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700982 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700983 mUsageByUserPackage.put(key, usage);
984 return usage;
985 }
986
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700987 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700988 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
989 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700990 * TODO(b/192294393): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700991 * has recurring I/O overuse behavior.
992 */
993 return false;
994 }
995
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700996 private IoOveruseStats getIoOveruseStatsForPeriod(int userId, String genericPackageName,
997 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
998 synchronized (mLock) {
999 String key = getUserPackageUniqueId(userId, genericPackageName);
1000 PackageResourceUsage usage = mUsageByUserPackage.get(key);
1001 if (usage == null) {
1002 return null;
1003 }
1004 return getIoOveruseStatsLocked(usage, /* minimumBytesWritten= */ 0, maxStatsPeriod);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001005 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001006 }
1007
1008 @GuardedBy("mLock")
1009 private IoOveruseStats getIoOveruseStatsLocked(PackageResourceUsage usage,
1010 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001011 if (!usage.ioUsage.hasUsage()) {
1012 /* Return I/O overuse stats only when the package has usage for the current day.
1013 * Without the current day usage, the returned stats will contain zero remaining
1014 * bytes, which is incorrect.
1015 */
1016 return null;
1017 }
1018 IoOveruseStats currentStats = usage.getIoOveruseStatsLocked();
1019 long totalBytesWritten = currentStats.getTotalBytesWritten();
1020 int numDays = toNumDays(maxStatsPeriod);
1021 IoOveruseStats historyStats = null;
1022 if (numDays > 0) {
1023 historyStats = mWatchdogStorage.getHistoricalIoOveruseStats(
1024 usage.userId, usage.genericPackageName, numDays - 1);
1025 totalBytesWritten += historyStats != null ? historyStats.getTotalBytesWritten() : 0;
1026 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001027 if (totalBytesWritten < minimumBytesWritten) {
1028 return null;
1029 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001030 if (historyStats == null) {
1031 return currentStats;
1032 }
1033 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
1034 historyStats.getStartTime(),
1035 historyStats.getDurationInSeconds() + currentStats.getDurationInSeconds());
1036 statsBuilder.setTotalTimesKilled(
1037 historyStats.getTotalTimesKilled() + currentStats.getTotalTimesKilled());
1038 statsBuilder.setTotalOveruses(
1039 historyStats.getTotalOveruses() + currentStats.getTotalOveruses());
1040 statsBuilder.setTotalBytesWritten(
1041 historyStats.getTotalBytesWritten() + currentStats.getTotalBytesWritten());
1042 statsBuilder.setKillableOnOveruse(currentStats.isKillableOnOveruse());
1043 statsBuilder.setRemainingWriteBytes(currentStats.getRemainingWriteBytes());
1044 return statsBuilder.build();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001045 }
1046
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001047 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001048 private void addResourceOveruseListenerLocked(
1049 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
1050 @NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001051 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001052 int callingPid = Binder.getCallingPid();
1053 int callingUid = Binder.getCallingUid();
1054 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
1055 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
1056 "resource overuse listener";
1057
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001058 IBinder binder = listener.asBinder();
1059 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
1060 if (listenerInfos == null) {
1061 listenerInfos = new ArrayList<>();
1062 listenerInfosByUid.put(callingUid, listenerInfos);
1063 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001064 for (int i = 0; i < listenerInfos.size(); ++i) {
1065 if (listenerInfos.get(i).listener.asBinder() == binder) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001066 throw new IllegalStateException(
1067 "Cannot add " + listenerType + " as it is already added");
1068 }
1069 }
1070
1071 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
1072 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
1073 try {
1074 listenerInfo.linkToDeath();
1075 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001076 Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001077 return;
1078 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001079 listenerInfos.add(listenerInfo);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001080 if (DEBUG) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001081 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
1082 callingPid, callingUid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001083 }
1084 }
1085
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001086 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001087 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001088 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001089 int callingUid = Binder.getCallingUid();
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001090 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
1091 ? "resource overuse system listener" : "resource overuse listener";
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001092 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
1093 if (listenerInfos == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001094 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001095 return;
1096 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001097 IBinder binder = listener.asBinder();
1098 ResourceOveruseListenerInfo cachedListenerInfo = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001099 for (int i = 0; i < listenerInfos.size(); ++i) {
1100 if (listenerInfos.get(i).listener.asBinder() == binder) {
1101 cachedListenerInfo = listenerInfos.get(i);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001102 break;
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001103 }
1104 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001105 if (cachedListenerInfo == null) {
1106 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
1107 return;
1108 }
1109 cachedListenerInfo.unlinkToDeath();
1110 listenerInfos.remove(cachedListenerInfo);
1111 if (listenerInfos.isEmpty()) {
1112 listenerInfosByUid.remove(callingUid);
1113 }
1114 if (DEBUG) {
1115 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
1116 cachedListenerInfo.pid, cachedListenerInfo.uid);
1117 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001118 }
1119
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001120 @GuardedBy("mLock")
1121 private void setPendingSetResourceOveruseConfigurationsRequestLocked(
1122 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) {
1123 if (mPendingSetResourceOveruseConfigurationsRequest != null) {
1124 if (mPendingSetResourceOveruseConfigurationsRequest == configs) {
1125 return;
1126 }
1127 throw new IllegalStateException(
1128 "Pending setResourceOveruseConfigurations request in progress");
1129 }
1130 mPendingSetResourceOveruseConfigurationsRequest = configs;
1131 }
1132
1133 private void retryPendingSetResourceOveruseConfigurations() {
1134 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001135 synchronized (mLock) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001136 if (mPendingSetResourceOveruseConfigurationsRequest == null) {
1137 return;
1138 }
1139 configs = mPendingSetResourceOveruseConfigurationsRequest;
1140 }
1141 try {
1142 int result = setResourceOveruseConfigurationsInternal(configs,
1143 /* isPendingRequest= */ true);
1144 if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) {
1145 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code "
1146 + "%d", result);
1147 }
1148 } catch (Exception e) {
1149 Slogf.e(TAG, e, "Exception on set pending resource overuse configurations");
1150 }
1151 }
1152
1153 private int setResourceOveruseConfigurationsInternal(
1154 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs,
1155 boolean isPendingRequest) throws RemoteException {
1156 boolean doClearPendingRequest = isPendingRequest;
1157 try {
1158 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs);
Lakshman Annadoraia7189562021-08-17 16:36:16 -07001159 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001160 } catch (RemoteException e) {
1161 if (e instanceof TransactionTooLargeException) {
1162 throw e;
1163 }
1164 Slogf.e(TAG, e, "Remote exception on set resource overuse configuration");
1165 synchronized (mLock) {
1166 setPendingSetResourceOveruseConfigurationsRequestLocked(configs);
1167 }
1168 doClearPendingRequest = false;
1169 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1170 } finally {
1171 if (doClearPendingRequest) {
1172 synchronized (mLock) {
1173 mPendingSetResourceOveruseConfigurationsRequest = null;
1174 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001175 }
1176 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001177 if (DEBUG) {
1178 Slogf.d(TAG, "Set the resource overuse configuration successfully");
1179 }
1180 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1181 }
1182
1183 private boolean isConnectedToDaemon() {
1184 synchronized (mLock) {
1185 long startTimeMillis = SystemClock.uptimeMillis();
1186 long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1187 while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) {
1188 try {
1189 mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis);
1190 } catch (InterruptedException e) {
1191 Thread.currentThread().interrupt();
1192 continue;
1193 } finally {
1194 sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1195 }
1196 break;
1197 }
1198 return mIsConnectedToDaemon;
1199 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001200 }
1201
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +00001202 @GuardedBy("mLock")
1203 private boolean isSafeToKillLocked(String genericPackageName, int componentType,
1204 List<String> sharedPackages) {
1205 BiFunction<List<String>, Set<String>, Boolean> isSafeToKillAnyPackage =
1206 (packages, safeToKillPackages) -> {
1207 if (packages == null) {
1208 return false;
1209 }
1210 for (int i = 0; i < packages.size(); i++) {
1211 if (safeToKillPackages.contains(packages.get(i))) {
1212 return true;
1213 }
1214 }
1215 return false;
1216 };
1217
1218 switch (componentType) {
1219 case ComponentType.SYSTEM:
1220 if (mSafeToKillSystemPackages.contains(genericPackageName)) {
1221 return true;
1222 }
1223 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages);
1224 case ComponentType.VENDOR:
1225 if (mSafeToKillVendorPackages.contains(genericPackageName)) {
1226 return true;
1227 }
1228 /*
1229 * Packages under the vendor shared UID may contain system packages because when
1230 * CarWatchdogService derives the shared component type it attributes system
1231 * packages as vendor packages when there is at least one vendor package.
1232 */
1233 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages)
1234 || isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillVendorPackages);
1235 default:
1236 // Third-party apps are always killable
1237 return true;
1238 }
1239 }
1240
Jahdiel Alvarez9de7aa52021-09-10 04:39:23 +00001241 private int[] getAliveUserIds() {
1242 UserManager userManager = UserManager.get(mContext);
1243 List<UserHandle> aliveUsers = userManager.getUserHandles(/* excludeDying= */ true);
1244 int userSize = aliveUsers.size();
1245 int[] userIds = new int[userSize];
1246 for (int i = 0; i < userSize; ++i) {
1247 userIds[i] = aliveUsers.get(i).getIdentifier();
1248 }
1249 return userIds;
1250 }
1251
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001252 private static String getUserPackageUniqueId(int userId, String genericPackageName) {
1253 return String.valueOf(userId) + ":" + genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001254 }
1255
1256 @VisibleForTesting
1257 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001258 android.automotive.watchdog.IoOveruseStats internalStats,
1259 int totalTimesKilled, boolean isKillableOnOveruses) {
1260 return new IoOveruseStats.Builder(internalStats.startTime, internalStats.durationInSeconds)
1261 .setTotalOveruses(internalStats.totalOveruses)
1262 .setTotalTimesKilled(totalTimesKilled)
1263 .setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes))
1264 .setKillableOnOveruse(isKillableOnOveruses)
1265 .setRemainingWriteBytes(toPerStateBytes(internalStats.remainingWriteBytes));
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001266 }
1267
1268 private static PerStateBytes toPerStateBytes(
1269 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001270 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001271 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001272 }
1273
1274 private static long totalPerStateBytes(
1275 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
1276 BiFunction<Long, Long, Long> sum = (l, r) -> {
1277 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
1278 };
1279 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
1280 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
1281 }
1282
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001283 private static long getMinimumBytesWritten(
1284 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
1285 switch (minimumStatsIoFlag) {
1286 case 0:
1287 return 0;
1288 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
1289 return 1024 * 1024;
1290 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
1291 return 100 * 1024 * 1024;
1292 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
1293 return 1024 * 1024 * 1024;
1294 default:
1295 throw new IllegalArgumentException(
1296 "Must provide valid minimum stats flag for I/O resource");
1297 }
1298 }
1299
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001300 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
1301 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
1302 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1303 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
1304 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
1305 internalConfig.componentType = config.getComponentType();
1306 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
1307 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
1308 internalConfig.packageMetadata = new ArrayList<>();
1309 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
1310 if (entry.getKey().isEmpty()) {
1311 continue;
1312 }
1313 PackageMetadata metadata = new PackageMetadata();
1314 metadata.packageName = entry.getKey();
1315 switch(entry.getValue()) {
1316 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1317 metadata.appCategoryType = ApplicationCategoryType.MAPS;
1318 break;
1319 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1320 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
1321 break;
1322 default:
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001323 Slogf.i(TAG, "Invalid application category type: %s skipping package: %s",
1324 entry.getValue(), metadata.packageName);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001325 continue;
1326 }
1327 internalConfig.packageMetadata.add(metadata);
1328 }
1329 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
1330 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1331 && config.getIoOveruseConfiguration() != null) {
1332 internalConfig.resourceSpecificConfigurations.add(
1333 toResourceSpecificConfiguration(config.getComponentType(),
1334 config.getIoOveruseConfiguration()));
1335 }
1336 return internalConfig;
1337 }
1338
1339 private static ResourceSpecificConfiguration
1340 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
1341 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
1342 new android.automotive.watchdog.internal.IoOveruseConfiguration();
1343 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
1344 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
1345 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
1346 config.getPackageSpecificThresholds());
1347 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
1348 config.getAppCategorySpecificThresholds());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001349 for (int i = 0; i < internalConfig.categorySpecificThresholds.size(); ++i) {
1350 PerStateIoOveruseThreshold threshold = internalConfig.categorySpecificThresholds.get(i);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001351 switch(threshold.name) {
1352 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1353 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
1354 break;
1355 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1356 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
1357 break;
1358 default:
1359 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
1360 }
1361 }
1362 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
1363 config.getSystemWideThresholds());
1364
1365 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
1366 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
1367 return resourceSpecificConfig;
1368 }
1369
1370 @VisibleForTesting
1371 static String toComponentTypeStr(int componentType) {
1372 switch(componentType) {
1373 case ComponentType.SYSTEM:
1374 return "SYSTEM";
1375 case ComponentType.VENDOR:
1376 return "VENDOR";
1377 case ComponentType.THIRD_PARTY:
1378 return "THIRD_PARTY";
1379 default:
1380 return "UNKNOWN";
1381 }
1382 }
1383
1384 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
1385 Map<String, PerStateBytes> thresholds) {
1386 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
1387 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001388 if (!thresholds.isEmpty()) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001389 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
1390 entry.getValue()));
1391 }
1392 }
1393 return internalThresholds;
1394 }
1395
1396 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
1397 PerStateBytes perStateBytes) {
1398 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
1399 threshold.name = name;
1400 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
1401 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
1402 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
1403 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
1404 return threshold;
1405 }
1406
1407 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
1408 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
1409 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
1410 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001411 for (int i = 0; i < thresholds.size(); ++i) {
1412 if (thresholds.get(i).getDurationInSeconds() == 0
1413 || thresholds.get(i).getWrittenBytesPerSecond() == 0) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001414 continue;
1415 }
1416 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
1417 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001418 internalThreshold.durationInSeconds = thresholds.get(i).getDurationInSeconds();
1419 internalThreshold.writtenBytesPerSecond = thresholds.get(i).getWrittenBytesPerSecond();
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001420 internalThresholds.add(internalThreshold);
1421 }
1422 return internalThresholds;
1423 }
1424
1425 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
1426 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
1427 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001428 ArrayMap<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
1429 for (int i = 0; i < internalConfig.packageMetadata.size(); ++i) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001430 String categoryTypeStr;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001431 switch (internalConfig.packageMetadata.get(i).appCategoryType) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001432 case ApplicationCategoryType.MAPS:
1433 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
1434 break;
1435 case ApplicationCategoryType.MEDIA:
1436 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
1437 break;
1438 default:
1439 continue;
1440 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001441 packagesToAppCategoryTypes.put(
1442 internalConfig.packageMetadata.get(i).packageName, categoryTypeStr);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001443 }
1444 ResourceOveruseConfiguration.Builder configBuilder =
1445 new ResourceOveruseConfiguration.Builder(
1446 internalConfig.componentType,
1447 internalConfig.safeToKillPackages,
1448 internalConfig.vendorPackagePrefixes,
1449 packagesToAppCategoryTypes);
1450 for (ResourceSpecificConfiguration resourceSpecificConfig :
1451 internalConfig.resourceSpecificConfigurations) {
1452 if (resourceSpecificConfig.getTag()
1453 == ResourceSpecificConfiguration.ioOveruseConfiguration
1454 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
1455 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
1456 resourceSpecificConfig.getIoOveruseConfiguration()));
1457 }
1458 }
1459 return configBuilder.build();
1460 }
1461
1462 private static IoOveruseConfiguration toIoOveruseConfiguration(
1463 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
1464 PerStateBytes componentLevelThresholds =
1465 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001466 ArrayMap<String, PerStateBytes> packageSpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001467 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001468 ArrayMap<String, PerStateBytes> appCategorySpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001469 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
Lakshman Annadoraia55ac6d2021-09-27 11:52:09 -07001470 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001471 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
Lakshman Annadoraia55ac6d2021-09-27 11:52:09 -07001472 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001473 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
1474 List<IoOveruseAlertThreshold> systemWideThresholds =
1475 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
1476
1477 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1478 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1479 systemWideThresholds);
1480 return configBuilder.build();
1481 }
1482
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001483 private static ArrayMap<String, PerStateBytes> toPerStateBytesMap(
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001484 List<PerStateIoOveruseThreshold> thresholds) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001485 ArrayMap<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1486 for (int i = 0; i < thresholds.size(); ++i) {
1487 thresholdsMap.put(
1488 thresholds.get(i).name, toPerStateBytes(thresholds.get(i).perStateWriteBytes));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001489 }
1490 return thresholdsMap;
1491 }
1492
1493 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1494 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1495 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001496 for (int i = 0; i < internalThresholds.size(); ++i) {
1497 thresholds.add(new IoOveruseAlertThreshold(internalThresholds.get(i).durationInSeconds,
1498 internalThresholds.get(i).writtenBytesPerSecond));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001499 }
1500 return thresholds;
1501 }
1502
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001503 private static void checkResourceOveruseConfigs(
1504 List<ResourceOveruseConfiguration> configurations,
1505 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1506 ArraySet<Integer> seenComponentTypes = new ArraySet<>();
1507 for (int i = 0; i < configurations.size(); ++i) {
1508 ResourceOveruseConfiguration config = configurations.get(i);
1509 if (seenComponentTypes.contains(config.getComponentType())) {
1510 throw new IllegalArgumentException(
1511 "Cannot provide duplicate configurations for the same component type");
1512 }
1513 checkResourceOveruseConfig(config, resourceOveruseFlag);
1514 seenComponentTypes.add(config.getComponentType());
1515 }
1516 }
1517
1518 private static void checkResourceOveruseConfig(ResourceOveruseConfiguration config,
1519 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1520 int componentType = config.getComponentType();
1521 if (toComponentTypeStr(componentType).equals("UNKNOWN")) {
1522 throw new IllegalArgumentException(
1523 "Invalid component type in the configuration: " + componentType);
1524 }
1525 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1526 && config.getIoOveruseConfiguration() == null) {
1527 throw new IllegalArgumentException("Must provide I/O overuse configuration");
1528 }
1529 checkIoOveruseConfig(config.getIoOveruseConfiguration(), componentType);
1530 }
1531
1532 private static void checkIoOveruseConfig(IoOveruseConfiguration config, int componentType) {
1533 if (config.getComponentLevelThresholds().getBackgroundModeBytes() <= 0
1534 || config.getComponentLevelThresholds().getForegroundModeBytes() <= 0
1535 || config.getComponentLevelThresholds().getGarageModeBytes() <= 0) {
1536 throw new IllegalArgumentException(
1537 "For component: " + toComponentTypeStr(componentType)
1538 + " some thresholds are zero for: "
1539 + config.getComponentLevelThresholds().toString());
1540 }
1541 if (componentType == ComponentType.SYSTEM) {
1542 List<IoOveruseAlertThreshold> systemThresholds = config.getSystemWideThresholds();
1543 if (systemThresholds.isEmpty()) {
1544 throw new IllegalArgumentException(
1545 "Empty system-wide alert thresholds provided in "
1546 + toComponentTypeStr(componentType)
1547 + " config.");
1548 }
1549 for (int i = 0; i < systemThresholds.size(); i++) {
1550 checkIoOveruseAlertThreshold(systemThresholds.get(i));
1551 }
1552 }
1553 }
1554
1555 private static void checkIoOveruseAlertThreshold(
1556 IoOveruseAlertThreshold ioOveruseAlertThreshold) {
1557 if (ioOveruseAlertThreshold.getDurationInSeconds() <= 0) {
1558 throw new IllegalArgumentException(
1559 "System wide threshold duration must be greater than zero for: "
1560 + ioOveruseAlertThreshold);
1561 }
1562 if (ioOveruseAlertThreshold.getWrittenBytesPerSecond() <= 0) {
1563 throw new IllegalArgumentException(
1564 "System wide threshold written bytes per second must be greater than zero for: "
1565 + ioOveruseAlertThreshold);
1566 }
1567 }
1568
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001569 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1570 PerStateBytes perStateBytes = map.get(oldKey);
1571 if (perStateBytes != null) {
1572 map.put(newKey, perStateBytes);
1573 map.remove(oldKey);
1574 }
1575 }
1576
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001577 private static int toNumDays(@CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
1578 switch(maxStatsPeriod) {
1579 case STATS_PERIOD_CURRENT_DAY:
1580 return 0;
1581 case STATS_PERIOD_PAST_3_DAYS:
1582 return 3;
1583 case STATS_PERIOD_PAST_7_DAYS:
1584 return 7;
1585 case STATS_PERIOD_PAST_15_DAYS:
1586 return 15;
1587 case STATS_PERIOD_PAST_30_DAYS:
1588 return 30;
1589 default:
1590 throw new IllegalArgumentException(
1591 "Invalid max stats period provided: " + maxStatsPeriod);
1592 }
1593 }
1594
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001595 private final class PackageResourceUsage {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001596 public final String genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001597 public @UserIdInt final int userId;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001598 @GuardedBy("mLock")
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001599 public final PackageIoUsage ioUsage = new PackageIoUsage();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001600 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001601 private @KillableState int mKillableState;
1602
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001603 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001604 PackageResourceUsage(@UserIdInt int userId, String genericPackageName) {
1605 this.genericPackageName = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001606 this.userId = userId;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001607 this.mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001608 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001609 }
1610
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001611 public boolean isSharedPackage() {
1612 return this.genericPackageName.startsWith(SHARED_PACKAGE_PREFIX);
1613 }
1614
1615 @GuardedBy("mLock")
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001616 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001617 if (!internalStats.killableOnOveruse) {
1618 /*
1619 * Killable value specified in the internal stats is provided by the native daemon.
1620 * This value reflects whether or not an application is safe-to-kill on overuse.
1621 * This setting is from the I/O overuse configuration specified by the system and
1622 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1623 * specify the application is not killable, the application is not safe-to-kill.
1624 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001625 mKillableState = KILLABLE_STATE_NEVER;
1626 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1627 /*
1628 * This case happens when a previously unsafe to kill system/vendor package was
1629 * recently marked as safe-to-kill so update the old state to the default value.
1630 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001631 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001632 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001633 }
1634 ioUsage.update(internalStats);
1635 }
1636
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001637 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001638 return new ResourceOveruseStats.Builder(genericPackageName, UserHandle.of(userId));
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001639 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001640
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001641 @GuardedBy("mLock")
1642 public IoOveruseStats getIoOveruseStatsLocked() {
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001643 if (!ioUsage.hasUsage()) {
1644 return null;
1645 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001646 return ioUsage.getIoOveruseStats(mKillableState != KILLABLE_STATE_NEVER);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001647 }
1648
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001649 @GuardedBy("mLock")
1650 public @KillableState int getKillableStateLocked() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001651 return mKillableState;
1652 }
1653
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001654 @GuardedBy("mLock")
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001655 public void setKillableStateLocked(@KillableState int killableState) {
1656 mKillableState = killableState;
1657 }
1658
1659 @GuardedBy("mLock")
1660 public boolean verifyAndSetKillableStateLocked(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001661 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001662 return false;
1663 }
1664 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1665 return true;
1666 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001667
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001668 @GuardedBy("mLock")
1669 public int syncAndFetchKillableStateLocked(int myComponentType, boolean isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001670 /*
1671 * The killable state goes out-of-sync:
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001672 * 1. When the on-device safe-to-kill list was recently updated and the user package
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001673 * didn't have any resource usage so the native daemon didn't update the killable state.
1674 * 2. When a package has no resource usage and is initialized outside of processing the
1675 * latest resource usage stats.
1676 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001677 if (myComponentType != ComponentType.THIRD_PARTY && !isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001678 mKillableState = KILLABLE_STATE_NEVER;
1679 } else if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001680 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001681 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1682 }
1683 return mKillableState;
1684 }
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001685
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001686 @GuardedBy("mLock")
1687 public void resetStatsLocked() {
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001688 ioUsage.resetStats();
1689 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001690 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001691 /** Defines I/O usage fields for a package. */
1692 public static final class PackageIoUsage {
Jahdiel Alvarezc209a322021-08-31 23:52:43 +00001693 private static final android.automotive.watchdog.PerStateBytes DEFAULT_PER_STATE_BYTES =
1694 new android.automotive.watchdog.PerStateBytes();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001695 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1696 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001697 private int mTotalTimesKilled;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001698
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001699 private PackageIoUsage() {
Jahdiel Alvarezc209a322021-08-31 23:52:43 +00001700 mForgivenWriteBytes = DEFAULT_PER_STATE_BYTES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001701 mTotalTimesKilled = 0;
1702 }
1703
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001704 public PackageIoUsage(android.automotive.watchdog.IoOveruseStats ioOveruseStats,
1705 android.automotive.watchdog.PerStateBytes forgivenWriteBytes,
1706 int totalTimesKilled) {
1707 mIoOveruseStats = ioOveruseStats;
1708 mForgivenWriteBytes = forgivenWriteBytes;
1709 mTotalTimesKilled = totalTimesKilled;
1710 }
1711
1712 public android.automotive.watchdog.IoOveruseStats getInternalIoOveruseStats() {
1713 return mIoOveruseStats;
1714 }
1715
1716 public android.automotive.watchdog.PerStateBytes getForgivenWriteBytes() {
1717 return mForgivenWriteBytes;
1718 }
1719
1720 public int getTotalTimesKilled() {
1721 return mTotalTimesKilled;
1722 }
1723
1724 boolean hasUsage() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001725 return mIoOveruseStats != null;
1726 }
1727
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001728 void overwrite(PackageIoUsage ioUsage) {
1729 mIoOveruseStats = ioUsage.mIoOveruseStats;
1730 mForgivenWriteBytes = ioUsage.mForgivenWriteBytes;
1731 mTotalTimesKilled = ioUsage.mTotalTimesKilled;
1732 }
1733
1734 void update(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001735 mIoOveruseStats = internalStats;
1736 if (exceedsThreshold()) {
1737 /*
1738 * Forgive written bytes on overuse as the package is either forgiven or killed on
1739 * overuse. When the package is killed, the user may opt to open the corresponding
1740 * app and the package should be forgiven anyways.
1741 * NOTE: If this logic is updated, update the daemon side logic as well.
1742 */
1743 mForgivenWriteBytes = internalStats.writtenBytes;
1744 }
1745 }
1746
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001747 IoOveruseStats getIoOveruseStats(boolean isKillable) {
1748 return toIoOveruseStatsBuilder(mIoOveruseStats, mTotalTimesKilled, isKillable).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001749 }
1750
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001751 boolean exceedsThreshold() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001752 if (!hasUsage()) {
1753 return false;
1754 }
1755 android.automotive.watchdog.PerStateBytes remaining =
1756 mIoOveruseStats.remainingWriteBytes;
1757 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1758 || remaining.garageModeBytes == 0;
1759 }
1760
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001761 void killed() {
1762 ++mTotalTimesKilled;
1763 }
1764
1765 void resetStats() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001766 mIoOveruseStats = null;
Jahdiel Alvarezc209a322021-08-31 23:52:43 +00001767 mForgivenWriteBytes = DEFAULT_PER_STATE_BYTES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001768 mTotalTimesKilled = 0;
1769 }
1770 }
1771
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001772 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1773 public final IResourceOveruseListener listener;
1774 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1775 public final int pid;
1776 public final int uid;
1777 public final boolean isListenerForSystem;
1778
1779 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1780 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1781 boolean isListenerForSystem) {
1782 this.listener = listener;
1783 this.flag = flag;
1784 this.pid = pid;
1785 this.uid = uid;
1786 this.isListenerForSystem = isListenerForSystem;
1787 }
1788
1789 @Override
1790 public void binderDied() {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001791 Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001792 isListenerForSystem ? " for system" : "", pid);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001793 Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo =
1794 listenerInfosByUid -> {
1795 ArrayList<ResourceOveruseListenerInfo> listenerInfos =
1796 listenerInfosByUid.get(uid);
1797 if (listenerInfos == null) {
1798 return;
1799 }
1800 listenerInfos.remove(this);
1801 if (listenerInfos.isEmpty()) {
1802 listenerInfosByUid.remove(uid);
1803 }
1804 };
1805 if (isListenerForSystem) {
1806 removeListenerInfo.accept(mOveruseSystemListenerInfosByUid);
1807 } else {
1808 removeListenerInfo.accept(mOveruseListenerInfosByUid);
1809 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001810 unlinkToDeath();
1811 }
1812
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001813 public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001814 int overusingUid, String overusingGenericPackageName,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001815 ResourceOveruseStats resourceOveruseStats) {
1816 if ((flag & resourceType) == 0) {
1817 return;
1818 }
1819 try {
1820 listener.onOveruse(resourceOveruseStats);
1821 } catch (RemoteException e) {
1822 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001823 + "package(uid %d, generic package name '%s'): %s",
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001824 (isListenerForSystem ? "system listener" : "listener"), uid, pid,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001825 overusingUid, overusingGenericPackageName, e);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001826 }
1827 }
1828
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001829 private void linkToDeath() throws RemoteException {
1830 listener.asBinder().linkToDeath(this, 0);
1831 }
1832
1833 private void unlinkToDeath() {
1834 listener.asBinder().unlinkToDeath(this, 0);
1835 }
1836 }
1837}