blob: 8663c5dde54fb4df8a3e170cceccd70c7dbdc27d [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;
71import android.content.pm.UserInfo;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070072import android.os.Binder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070073import android.os.Handler;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -070074import android.os.HandlerThread;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070075import android.os.IBinder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070076import android.os.Looper;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070077import android.os.RemoteException;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070078import android.os.SystemClock;
79import android.os.TransactionTooLargeException;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070080import android.os.UserHandle;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070081import android.os.UserManager;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070082import android.util.ArrayMap;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070083import android.util.ArraySet;
84import android.util.IndentingPrintWriter;
85import android.util.SparseArray;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +000086import android.util.SparseBooleanArray;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070087
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -070088import com.android.car.CarServiceUtils;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070089import com.android.internal.annotations.GuardedBy;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070090import com.android.internal.annotations.VisibleForTesting;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070091import com.android.internal.util.Preconditions;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -070092import com.android.internal.util.function.TriConsumer;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070093import com.android.server.utils.Slogf;
94
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070095import java.time.ZoneOffset;
96import java.time.ZonedDateTime;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070097import java.util.ArrayList;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070098import java.util.Collections;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070099import java.util.List;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700100import java.util.Map;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700101import java.util.Objects;
102import java.util.Set;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700103import java.util.function.BiFunction;
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700104import java.util.function.Consumer;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700105
106/**
107 * Handles system resource performance monitoring module.
108 */
109public final class WatchdogPerfHandler {
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700110 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS = "MAPS";
111 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA";
112 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN";
113
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000114 static final long RESOURCE_OVERUSE_KILLING_DELAY_MILLS = 10_000;
115
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700116 private static final long MAX_WAIT_TIME_MILLS = 3_000;
117
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700118 private final Context mContext;
119 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
120 private final PackageInfoHandler mPackageInfoHandler;
121 private final Handler mMainHandler;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700122 private final HandlerThread mHandlerThread;
123 private final WatchdogStorage mWatchdogStorage;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700124 private final Object mLock = new Object();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700125 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700126 private final ArrayMap<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700127 @GuardedBy("mLock")
128 private final List<PackageResourceOveruseAction> mOveruseActionsByUserPackage =
129 new ArrayList<>();
130 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700131 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>> mOveruseListenerInfosByUid =
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700132 new SparseArray<>();
133 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700134 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>>
135 mOveruseSystemListenerInfosByUid = new SparseArray<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700136 /* Set of safe-to-kill system and vendor packages. */
137 @GuardedBy("mLock")
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000138 public final ArraySet<String> mSafeToKillSystemPackages = new ArraySet<>();
139 @GuardedBy("mLock")
140 public final ArraySet<String> mSafeToKillVendorPackages = new ArraySet<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700141 /* Default killable state for packages when not updated by the user. */
142 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700143 public final ArraySet<String> mDefaultNotKillableGenericPackages = new ArraySet<>();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700144 @GuardedBy("mLock")
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700145 private ZonedDateTime mLatestStatsReportDate;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700146 @GuardedBy("mLock")
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700147 private List<android.automotive.watchdog.internal.ResourceOveruseConfiguration>
148 mPendingSetResourceOveruseConfigurationsRequest = null;
149 @GuardedBy("mLock")
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700150 boolean mIsConnectedToDaemon;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000151 @GuardedBy("mLock")
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700152 boolean mIsWrittenToDatabase;
153 @GuardedBy("mLock")
154 private TimeSourceInterface mTimeSource;
155 @GuardedBy("mLock")
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000156 long mResourceOveruseKillingDelayMills;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700157
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700158 public WatchdogPerfHandler(Context context, CarWatchdogDaemonHelper daemonHelper,
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700159 PackageInfoHandler packageInfoHandler, WatchdogStorage watchdogStorage) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700160 mContext = context;
161 mCarWatchdogDaemonHelper = daemonHelper;
162 mPackageInfoHandler = packageInfoHandler;
163 mMainHandler = new Handler(Looper.getMainLooper());
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700164 mHandlerThread = CarServiceUtils.getHandlerThread(CarWatchdogService.class.getSimpleName());
165 mWatchdogStorage = watchdogStorage;
166 mTimeSource = SYSTEM_INSTANCE;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000167 mResourceOveruseKillingDelayMills = RESOURCE_OVERUSE_KILLING_DELAY_MILLS;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700168 }
169
170 /** Initializes the handler. */
171 public void init() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700172 /*
173 * TODO(b/183947162): Opt-in to receive package change broadcast and handle package enabled
174 * state changes.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700175 */
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700176 /* First database read is expensive, so post it on a separate handler thread. */
177 mHandlerThread.getThreadHandler().post(() -> {
178 readFromDatabase();
179 synchronized (mLock) {
180 checkAndHandleDateChangeLocked();
181 mIsWrittenToDatabase = false;
182 }});
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700183 if (DEBUG) {
184 Slogf.d(TAG, "WatchdogPerfHandler is initialized");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700185 }
186 }
187
188 /** Dumps its state. */
189 public void dump(IndentingPrintWriter writer) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700190 /*
191 * TODO(b/183436216): Implement this method.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700192 */
193 }
194
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700195 /** Retries any pending requests on re-connecting to the daemon */
196 public void onDaemonConnectionChange(boolean isConnected) {
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700197 boolean hasPendingRequest;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700198 synchronized (mLock) {
199 mIsConnectedToDaemon = isConnected;
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700200 hasPendingRequest = mPendingSetResourceOveruseConfigurationsRequest != null;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700201 }
202 if (isConnected) {
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700203 if (hasPendingRequest) {
204 /*
205 * Retry pending set resource overuse configuration request before processing any
206 * new set/get requests. Thus notify the waiting requests only after the retry
207 * completes.
208 */
209 retryPendingSetResourceOveruseConfigurations();
210 } else {
211 /* Start fetch/sync configs only when there are no pending set requests because the
212 * above retry starts fetch/sync configs on success. If the retry fails, the daemon
213 * has crashed and shouldn't start fetchAndSyncResourceOveruseConfigurations.
214 */
215 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
216 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700217 }
218 synchronized (mLock) {
219 mLock.notifyAll();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700220 }
221 }
222
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700223 /** Returns resource overuse stats for the calling package. */
224 @NonNull
225 public ResourceOveruseStats getResourceOveruseStats(
226 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
227 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
228 Preconditions.checkArgument((resourceOveruseFlag > 0),
229 "Must provide valid resource overuse flag");
230 Preconditions.checkArgument((maxStatsPeriod > 0),
231 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700232 // When more resource stats are added, make this as optional.
233 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
234 "Must provide resource I/O overuse flag");
235 int callingUid = Binder.getCallingUid();
236 int callingUserId = UserHandle.getUserId(callingUid);
237 UserHandle callingUserHandle = UserHandle.of(callingUserId);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700238 String genericPackageName =
239 mPackageInfoHandler.getNamesForUids(new int[]{callingUid})
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700240 .get(callingUid, null);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700241 if (genericPackageName == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700242 Slogf.w(TAG, "Failed to fetch package info for uid %d", callingUid);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700243 return new ResourceOveruseStats.Builder("", callingUserHandle).build();
244 }
245 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700246 new ResourceOveruseStats.Builder(genericPackageName, callingUserHandle);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700247 statsBuilder.setIoOveruseStats(
248 getIoOveruseStatsForPeriod(callingUserId, genericPackageName, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700249 if (DEBUG) {
250 Slogf.d(TAG, "Returning all resource overuse stats for calling uid %d [user %d and "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700251 + "package '%s']", callingUid, callingUserId, genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700252 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700253 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700254 }
255
256 /** Returns resource overuse stats for all packages. */
257 @NonNull
258 public List<ResourceOveruseStats> getAllResourceOveruseStats(
259 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
260 @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag,
261 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
262 Preconditions.checkArgument((resourceOveruseFlag > 0),
263 "Must provide valid resource overuse flag");
264 Preconditions.checkArgument((maxStatsPeriod > 0),
265 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700266 // When more resource types are added, make this as optional.
267 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
268 "Must provide resource I/O overuse flag");
269 long minimumBytesWritten = getMinimumBytesWritten(minimumStatsFlag);
270 List<ResourceOveruseStats> allStats = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700271 synchronized (mLock) {
272 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
273 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
274 ResourceOveruseStats.Builder statsBuilder = usage.getResourceOveruseStatsBuilder();
275 IoOveruseStats ioOveruseStats =
276 getIoOveruseStatsLocked(usage, minimumBytesWritten, maxStatsPeriod);
277 if (ioOveruseStats == null) {
278 continue;
279 }
280 allStats.add(statsBuilder.setIoOveruseStats(ioOveruseStats).build());
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700281 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700282 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700283 if (DEBUG) {
284 Slogf.d(TAG, "Returning all resource overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700285 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700286 return allStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700287 }
288
289 /** Returns resource overuse stats for the specified user package. */
290 @NonNull
291 public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
292 @NonNull String packageName, @NonNull UserHandle userHandle,
293 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
294 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
295 Objects.requireNonNull(packageName, "Package name must be non-null");
296 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700297 Preconditions.checkArgument(!userHandle.equals(UserHandle.ALL),
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700298 "Must provide the user handle for a specific user");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700299 Preconditions.checkArgument((resourceOveruseFlag > 0),
300 "Must provide valid resource overuse flag");
301 Preconditions.checkArgument((maxStatsPeriod > 0),
302 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700303 // When more resource types are added, make this as optional.
304 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
305 "Must provide resource I/O overuse flag");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700306 String genericPackageName =
307 mPackageInfoHandler.getNameForUserPackage(packageName, userHandle.getIdentifier());
308 if (genericPackageName == null) {
309 throw new IllegalArgumentException("Package '" + packageName + "' not found");
310 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700311 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700312 new ResourceOveruseStats.Builder(genericPackageName, userHandle);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700313 statsBuilder.setIoOveruseStats(getIoOveruseStatsForPeriod(userHandle.getIdentifier(),
314 genericPackageName, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700315 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700316 Slogf.d(TAG, "Returning resource overuse stats for user %d, package '%s', "
317 + "generic package '%s'", userHandle.getIdentifier(), packageName,
318 genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700319 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700320 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700321 }
322
323 /** Adds the resource overuse listener. */
324 public void addResourceOveruseListener(
325 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
326 @NonNull IResourceOveruseListener listener) {
327 Objects.requireNonNull(listener, "Listener must be non-null");
328 Preconditions.checkArgument((resourceOveruseFlag > 0),
329 "Must provide valid resource overuse flag");
330 synchronized (mLock) {
331 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
332 mOveruseListenerInfosByUid);
333 }
334 }
335
336 /** Removes the previously added resource overuse listener. */
337 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
338 Objects.requireNonNull(listener, "Listener must be non-null");
339 synchronized (mLock) {
340 removeResourceOveruseListenerLocked(listener, mOveruseListenerInfosByUid);
341 }
342 }
343
344 /** Adds the resource overuse system listener. */
345 public void addResourceOveruseListenerForSystem(
346 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
347 @NonNull IResourceOveruseListener listener) {
348 Objects.requireNonNull(listener, "Listener must be non-null");
349 Preconditions.checkArgument((resourceOveruseFlag > 0),
350 "Must provide valid resource overuse flag");
351 synchronized (mLock) {
352 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
353 mOveruseSystemListenerInfosByUid);
354 }
355 }
356
357 /** Removes the previously added resource overuse system listener. */
358 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
359 Objects.requireNonNull(listener, "Listener must be non-null");
360 synchronized (mLock) {
361 removeResourceOveruseListenerLocked(listener, mOveruseSystemListenerInfosByUid);
362 }
363 }
364
365 /** Sets whether or not a package is killable on resource overuse. */
366 public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
367 boolean isKillable) {
368 Objects.requireNonNull(packageName, "Package name must be non-null");
369 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700370
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700371 if (userHandle.equals(UserHandle.ALL)) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700372 setPackageKillableStateForAllUsers(packageName, isKillable);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700373 return;
374 }
375 int userId = userHandle.getIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700376 String genericPackageName = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
377 if (genericPackageName == null) {
378 throw new IllegalArgumentException("Package '" + packageName + "' not found");
379 }
380 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700381 synchronized (mLock) {
382 /*
383 * When the queried package is not cached in {@link mUsageByUserPackage}, the set API
384 * will update the killable state even when the package should never be killed.
385 * But the get API will return the correct killable state. This behavior is tolerable
386 * because in production the set API should be called only after the get API.
387 * For instance, when this case happens by mistake and the package overuses resource
388 * between the set and the get API calls, the daemon will provide correct killable
389 * state when pushing the latest stats. Ergo, the invalid killable state doesn't have
390 * any effect.
391 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700392 PackageResourceUsage usage = mUsageByUserPackage.get(key);
393 if (usage == null) {
394 usage = new PackageResourceUsage(userId, genericPackageName);
395 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700396 if (!usage.verifyAndSetKillableStateLocked(isKillable)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700397 Slogf.e(TAG, "User %d cannot set killable state for package '%s'",
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700398 userHandle.getIdentifier(), genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700399 throw new IllegalArgumentException("Package killable state is not updatable");
400 }
401 mUsageByUserPackage.put(key, usage);
402 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700403 if (DEBUG) {
404 Slogf.d(TAG, "Successfully set killable package state for user %d", userId);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700405 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700406 }
407
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700408 private void setPackageKillableStateForAllUsers(String packageName, boolean isKillable) {
409 UserManager userManager = UserManager.get(mContext);
410 List<UserInfo> userInfos = userManager.getAliveUsers();
411 String genericPackageName = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700412 synchronized (mLock) {
413 for (int i = 0; i < userInfos.size(); ++i) {
414 int userId = userInfos.get(i).id;
415 String name = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
416 if (name == null) {
417 continue;
418 }
419 genericPackageName = name;
420 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700421 PackageResourceUsage usage = mUsageByUserPackage.get(key);
422 if (usage == null) {
423 continue;
424 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700425 if (!usage.verifyAndSetKillableStateLocked(isKillable)) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700426 Slogf.e(TAG, "Cannot set killable state for package '%s'", packageName);
427 throw new IllegalArgumentException(
428 "Package killable state is not updatable");
429 }
430 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700431 if (genericPackageName != null) {
432 if (!isKillable) {
433 mDefaultNotKillableGenericPackages.add(genericPackageName);
434 } else {
435 mDefaultNotKillableGenericPackages.remove(genericPackageName);
436 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700437 }
438 }
439 if (DEBUG) {
440 Slogf.d(TAG, "Successfully set killable package state for all users");
441 }
442 }
443
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700444 /** Returns the list of package killable states on resource overuse for the user. */
445 @NonNull
446 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
447 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700448 PackageManager pm = mContext.getPackageManager();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700449 if (!userHandle.equals(UserHandle.ALL)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700450 if (DEBUG) {
451 Slogf.d(TAG, "Returning all package killable states for user %d",
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700452 userHandle.getIdentifier());
453 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700454 return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
455 }
456 List<PackageKillableState> packageKillableStates = new ArrayList<>();
457 UserManager userManager = UserManager.get(mContext);
458 List<UserInfo> userInfos = userManager.getAliveUsers();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700459 for (int i = 0; i < userInfos.size(); ++i) {
460 packageKillableStates.addAll(
461 getPackageKillableStatesForUserId(userInfos.get(i).id, pm));
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700462 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700463 if (DEBUG) {
464 Slogf.d(TAG, "Returning all package killable states for all users");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700465 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700466 return packageKillableStates;
467 }
468
469 private List<PackageKillableState> getPackageKillableStatesForUserId(int userId,
470 PackageManager pm) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700471 List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */ 0, userId);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700472 List<PackageKillableState> states = new ArrayList<>();
473 synchronized (mLock) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700474 ArrayMap<String, List<ApplicationInfo>> applicationInfosBySharedPackage =
475 new ArrayMap<>();
476 for (int i = 0; i < packageInfos.size(); ++i) {
477 PackageInfo packageInfo = packageInfos.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700478 String genericPackageName = mPackageInfoHandler.getNameForPackage(packageInfo);
479 if (packageInfo.sharedUserId == null) {
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000480 int componentType = mPackageInfoHandler.getComponentType(
481 packageInfo.applicationInfo);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700482 int killableState = getPackageKillableStateForUserPackageLocked(
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000483 userId, genericPackageName, componentType,
484 isSafeToKillLocked(genericPackageName, componentType, null));
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700485 states.add(new PackageKillableState(packageInfo.packageName, userId,
486 killableState));
487 continue;
488 }
489 List<ApplicationInfo> applicationInfos =
490 applicationInfosBySharedPackage.get(genericPackageName);
491 if (applicationInfos == null) {
492 applicationInfos = new ArrayList<>();
493 }
494 applicationInfos.add(packageInfo.applicationInfo);
495 applicationInfosBySharedPackage.put(genericPackageName, applicationInfos);
496 }
497 for (Map.Entry<String, List<ApplicationInfo>> entry :
498 applicationInfosBySharedPackage.entrySet()) {
499 String genericPackageName = entry.getKey();
500 List<ApplicationInfo> applicationInfos = entry.getValue();
501 int componentType = mPackageInfoHandler.getSharedComponentType(
502 applicationInfos, genericPackageName);
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000503 List<String> packageNames = new ArrayList<>(applicationInfos.size());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700504 for (int i = 0; i < applicationInfos.size(); ++i) {
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000505 packageNames.add(applicationInfos.get(i).packageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700506 }
507 int killableState = getPackageKillableStateForUserPackageLocked(
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000508 userId, genericPackageName, componentType,
509 isSafeToKillLocked(genericPackageName, componentType, packageNames));
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700510 for (int i = 0; i < applicationInfos.size(); ++i) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700511 states.add(new PackageKillableState(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700512 applicationInfos.get(i).packageName, userId, killableState));
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700513 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700514 }
515 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700516 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700517 Slogf.d(TAG, "Returning the package killable states for user packages");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700518 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700519 return states;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700520 }
521
522 /** Sets the given resource overuse configurations. */
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700523 @CarWatchdogManager.ReturnCode
524 public int setResourceOveruseConfigurations(
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700525 List<ResourceOveruseConfiguration> configurations,
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700526 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
527 throws RemoteException {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700528 Objects.requireNonNull(configurations, "Configurations must be non-null");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700529 Preconditions.checkArgument((configurations.size() > 0),
530 "Must provide at least one configuration");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700531 Preconditions.checkArgument((resourceOveruseFlag > 0),
532 "Must provide valid resource overuse flag");
Jahdiel Alvarez61481142021-07-31 00:13:19 +0000533 checkResourceOveruseConfigs(configurations, resourceOveruseFlag);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700534 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
535 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700536 for (int i = 0; i < configurations.size(); ++i) {
Jahdiel Alvarez61481142021-07-31 00:13:19 +0000537 internalConfigs.add(toInternalResourceOveruseConfiguration(configurations.get(i),
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700538 resourceOveruseFlag));
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700539 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700540 synchronized (mLock) {
541 if (!mIsConnectedToDaemon) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700542 setPendingSetResourceOveruseConfigurationsRequestLocked(internalConfigs);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700543 return CarWatchdogManager.RETURN_CODE_SUCCESS;
544 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700545 /* Verify no pending request in progress. */
546 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700547 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700548 return setResourceOveruseConfigurationsInternal(internalConfigs,
549 /* isPendingRequest= */ false);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700550 }
551
552 /** Returns the available resource overuse configurations. */
553 @NonNull
554 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
555 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
556 Preconditions.checkArgument((resourceOveruseFlag > 0),
557 "Must provide valid resource overuse flag");
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700558 if (!isConnectedToDaemon()) {
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700559 throw new IllegalStateException("Car watchdog daemon is not connected");
560 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700561 synchronized (mLock) {
562 /* Verify no pending request in progress. */
563 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
564 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700565 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
566 new ArrayList<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700567 try {
568 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
569 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700570 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700571 throw new IllegalStateException(e);
572 }
573 List<ResourceOveruseConfiguration> configs = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700574 for (int i = 0; i < internalConfigs.size(); ++i) {
575 configs.add(
576 toResourceOveruseConfiguration(internalConfigs.get(i), resourceOveruseFlag));
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700577 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700578 if (DEBUG) {
579 Slogf.d(TAG, "Returning the resource overuse configuration");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700580 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700581 return configs;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700582 }
583
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700584 /** Processes the latest I/O overuse stats */
585 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000586 SparseBooleanArray recurringIoOverusesByUid = new SparseBooleanArray();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700587 int[] uids = new int[packageIoOveruseStats.size()];
588 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
589 uids[i] = packageIoOveruseStats.get(i).uid;
590 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700591 SparseArray<String> genericPackageNamesByUid = mPackageInfoHandler.getNamesForUids(uids);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700592 synchronized (mLock) {
593 checkAndHandleDateChangeLocked();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700594 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
595 PackageIoOveruseStats stats = packageIoOveruseStats.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700596 String genericPackageName = genericPackageNamesByUid.get(stats.uid);
597 if (genericPackageName == null) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700598 continue;
599 }
600 int userId = UserHandle.getUserId(stats.uid);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700601 PackageResourceUsage usage = cacheAndFetchUsageLocked(userId, genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700602 stats.ioOveruseStats);
603 if (stats.shouldNotify) {
604 /*
605 * Packages that exceed the warn threshold percentage should be notified as well
606 * and only the daemon is aware of such packages. Thus the flag is used to
607 * indicate which packages should be notified.
608 */
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700609 ResourceOveruseStats resourceOveruseStats =
610 usage.getResourceOveruseStatsBuilder().setIoOveruseStats(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700611 usage.getIoOveruseStatsLocked()).build();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700612 notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700613 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700614
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700615 if (!usage.ioUsage.exceedsThreshold()) {
616 continue;
617 }
618 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
619 overuseAction.packageIdentifier = new PackageIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700620 overuseAction.packageIdentifier.name = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700621 overuseAction.packageIdentifier.uid = stats.uid;
622 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
623 overuseAction.resourceOveruseActionType = NOT_KILLED;
624 /*
625 * No action required on I/O overuse on one of the following cases:
626 * #1 The package is not safe to kill as it is critical for system stability.
627 * #2 The package has no recurring overuse behavior and the user opted to not
628 * kill the package so honor the user's decision.
629 */
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700630 int killableState = usage.getKillableStateLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700631 if (killableState == KILLABLE_STATE_NEVER) {
632 mOveruseActionsByUserPackage.add(overuseAction);
633 continue;
634 }
635 boolean hasRecurringOveruse = isRecurringOveruseLocked(usage);
636 if (!hasRecurringOveruse && killableState == KILLABLE_STATE_NO) {
637 overuseAction.resourceOveruseActionType = NOT_KILLED_USER_OPTED;
638 mOveruseActionsByUserPackage.add(overuseAction);
639 continue;
640 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000641 recurringIoOverusesByUid.put(stats.uid, hasRecurringOveruse);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700642 }
643 if (!mOveruseActionsByUserPackage.isEmpty()) {
Lakshman Annadoraia7189562021-08-17 16:36:16 -0700644 mMainHandler.post(this::notifyActionsTakenOnOveruse);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700645 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000646 if (recurringIoOverusesByUid.size() > 0) {
Lakshman Annadoraia7189562021-08-17 16:36:16 -0700647 mMainHandler.postDelayed(
648 () -> handleIoOveruseKilling(
649 recurringIoOverusesByUid, genericPackageNamesByUid),
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000650 mResourceOveruseKillingDelayMills);
651 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700652 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700653 if (DEBUG) {
654 Slogf.d(TAG, "Processed latest I/O overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700655 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700656 }
657
658 /** Notify daemon about the actions take on resource overuse */
659 public void notifyActionsTakenOnOveruse() {
660 List<PackageResourceOveruseAction> actions;
661 synchronized (mLock) {
662 if (mOveruseActionsByUserPackage.isEmpty()) {
663 return;
664 }
665 actions = new ArrayList<>(mOveruseActionsByUserPackage);
666 mOveruseActionsByUserPackage.clear();
667 }
668 try {
669 mCarWatchdogDaemonHelper.actionTakenOnResourceOveruse(actions);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700670 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700671 Slogf.w(TAG, e, "Failed to notify car watchdog daemon of actions taken on resource "
672 + "overuse");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700673 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700674 if (DEBUG) {
675 Slogf.d(TAG, "Notified car watchdog daemon of actions taken on resource overuse");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700676 }
677 }
678
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000679 /** Handle packages that exceed resource overuse thresholds */
680 private void handleIoOveruseKilling(SparseBooleanArray recurringIoOverusesByUid,
681 SparseArray<String> genericPackageNamesByUid) {
682 IPackageManager packageManager = ActivityThread.getPackageManager();
683 synchronized (mLock) {
684 for (int i = 0; i < recurringIoOverusesByUid.size(); i++) {
685 int uid = recurringIoOverusesByUid.keyAt(i);
686 boolean hasRecurringOveruse = recurringIoOverusesByUid.valueAt(i);
687 String genericPackageName = genericPackageNamesByUid.get(uid);
688 int userId = UserHandle.getUserId(uid);
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000689
690 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
691 overuseAction.packageIdentifier = new PackageIdentifier();
692 overuseAction.packageIdentifier.name = genericPackageName;
693 overuseAction.packageIdentifier.uid = uid;
694 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
695 overuseAction.resourceOveruseActionType = NOT_KILLED;
696
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700697 String key = getUserPackageUniqueId(userId, genericPackageName);
698 PackageResourceUsage usage = mUsageByUserPackage.get(key);
699 if (usage == null) {
700 /* This case shouldn't happen but placed here as a fail safe. */
701 mOveruseActionsByUserPackage.add(overuseAction);
702 continue;
703 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000704 List<String> packages = Collections.singletonList(genericPackageName);
705 if (usage.isSharedPackage()) {
706 packages = mPackageInfoHandler.getPackagesForUid(uid, genericPackageName);
707 }
708 for (int pkgIdx = 0; pkgIdx < packages.size(); pkgIdx++) {
709 String packageName = packages.get(pkgIdx);
710 try {
711 int oldEnabledState = -1;
712 if (!hasRecurringOveruse) {
713 oldEnabledState = packageManager.getApplicationEnabledSetting(
714 packageName, userId);
715 if (oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED
716 || oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED_USER
717 || oldEnabledState
718 == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
719 continue;
720 }
721 }
722 packageManager.setApplicationEnabledSetting(packageName,
723 COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId,
724 mContext.getPackageName());
725 overuseAction.resourceOveruseActionType = hasRecurringOveruse
726 ? KILLED_RECURRING_OVERUSE : KILLED;
727 if (oldEnabledState != -1) {
728 usage.oldEnabledStateByPackage.put(packageName, oldEnabledState);
729 }
730 } catch (RemoteException e) {
731 Slogf.e(TAG, "Failed to disable application for user %d, package '%s'",
732 userId, packageName);
733 }
734 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700735 if (overuseAction.resourceOveruseActionType == KILLED
736 || overuseAction.resourceOveruseActionType == KILLED_RECURRING_OVERUSE) {
737 usage.ioUsage.killed();
738 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000739 mOveruseActionsByUserPackage.add(overuseAction);
740 }
741 if (!mOveruseActionsByUserPackage.isEmpty()) {
742 notifyActionsTakenOnOveruse();
743 }
744 }
745 }
746
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700747 /** Resets the resource overuse stats for the given generic package names. */
748 public void resetResourceOveruseStats(Set<String> genericPackageNames) {
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700749 synchronized (mLock) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700750 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
751 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700752 if (genericPackageNames.contains(usage.genericPackageName)) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700753 usage.resetStatsLocked();
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700754 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700755 * TODO(b/192294393): When the stats are persisted in local DB, reset the stats
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700756 * for this package from local DB.
757 */
758 }
759 }
760 }
761 }
762
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700763 /** Sets the time source. */
764 public void setTimeSource(TimeSourceInterface timeSource) {
765 synchronized (mLock) {
766 mTimeSource = timeSource;
767 }
768 }
769
770 /** Sets the delay to kill a package after the package is notified of resource overuse. */
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000771 public void setResourceOveruseKillingDelay(long millis) {
772 synchronized (mLock) {
773 mResourceOveruseKillingDelayMills = millis;
774 }
775 }
776
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000777 /** Fetches and syncs the resource overuse configurations from watchdog daemon. */
778 private void fetchAndSyncResourceOveruseConfigurations() {
779 synchronized (mLock) {
780 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs;
781 try {
782 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
783 } catch (RemoteException | RuntimeException e) {
784 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
785 return;
786 }
787 if (internalConfigs.isEmpty()) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700788 Slogf.e(TAG, "Fetched resource overuse configurations are empty");
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000789 return;
790 }
791 mSafeToKillSystemPackages.clear();
792 mSafeToKillVendorPackages.clear();
793 for (int i = 0; i < internalConfigs.size(); i++) {
794 switch (internalConfigs.get(i).componentType) {
795 case ComponentType.SYSTEM:
796 mSafeToKillSystemPackages.addAll(internalConfigs.get(i).safeToKillPackages);
797 break;
798 case ComponentType.VENDOR:
799 mSafeToKillVendorPackages.addAll(internalConfigs.get(i).safeToKillPackages);
Jahdiel Alvarez5528c712021-08-10 21:07:40 +0000800 mPackageInfoHandler.setVendorPackagePrefixes(
801 internalConfigs.get(i).vendorPackagePrefixes);
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000802 break;
803 default:
804 // All third-party apps are killable.
805 break;
806 }
807 }
808 if (DEBUG) {
Jahdiel Alvarez5528c712021-08-10 21:07:40 +0000809 Slogf.d(TAG, "Fetched and synced resource overuse configs.");
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000810 }
811 }
812 }
813
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700814 private void readFromDatabase() {
815 List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries =
816 mWatchdogStorage.getUserPackageSettings();
817 Slogf.i(TAG, "Read %d user package settings from database", settingsEntries.size());
818 List<WatchdogStorage.IoUsageStatsEntry> ioStatsEntries =
819 mWatchdogStorage.getTodayIoUsageStats();
820 Slogf.i(TAG, "Read %d I/O usage stats from database", ioStatsEntries.size());
821 synchronized (mLock) {
822 for (int i = 0; i < settingsEntries.size(); ++i) {
823 WatchdogStorage.UserPackageSettingsEntry entry = settingsEntries.get(i);
824 if (entry.userId == UserHandle.USER_ALL) {
825 if (entry.killableState != KILLABLE_STATE_YES) {
826 mDefaultNotKillableGenericPackages.add(entry.packageName);
827 }
828 continue;
829 }
830 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
831 PackageResourceUsage usage = mUsageByUserPackage.get(key);
832 if (usage == null) {
833 usage = new PackageResourceUsage(entry.userId, entry.packageName);
834 }
835 usage.setKillableStateLocked(entry.killableState);
836 mUsageByUserPackage.put(key, usage);
837 }
838 ZonedDateTime curReportDate =
839 mTimeSource.now().atZone(ZONE_OFFSET).truncatedTo(STATS_TEMPORAL_UNIT);
840 for (int i = 0; i < ioStatsEntries.size(); ++i) {
841 WatchdogStorage.IoUsageStatsEntry entry = ioStatsEntries.get(i);
842 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
843 PackageResourceUsage usage = mUsageByUserPackage.get(key);
844 if (usage == null) {
845 usage = new PackageResourceUsage(entry.userId, entry.packageName);
846 }
847 /* Overwrite in memory cache as the stats will be merged on the daemon side and
848 * pushed on the next latestIoOveruseStats call. This is tolerable because the next
849 * push should happen soon.
850 */
851 usage.ioUsage.overwrite(entry.ioUsage);
852 mUsageByUserPackage.put(key, usage);
853 }
854 if (!ioStatsEntries.isEmpty()) {
855 /* When mLatestStatsReportDate is null, the latest stats push from daemon hasn't
856 * happened yet. Thus the cached stats contains only the stats read from database.
857 */
858 mIsWrittenToDatabase = mLatestStatsReportDate == null;
859 mLatestStatsReportDate = curReportDate;
860 }
861 }
862 }
863
864 /** Writes user package settings and stats to database. */
865 public void writeToDatabase() {
866 synchronized (mLock) {
867 if (mIsWrittenToDatabase) {
868 return;
869 }
870 List<WatchdogStorage.UserPackageSettingsEntry> entries =
871 new ArrayList<>(mUsageByUserPackage.size());
872 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
873 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
874 entries.add(new WatchdogStorage.UserPackageSettingsEntry(
875 usage.userId, usage.genericPackageName, usage.getKillableStateLocked()));
876 }
877 for (String packageName : mDefaultNotKillableGenericPackages) {
878 entries.add(new WatchdogStorage.UserPackageSettingsEntry(
879 UserHandle.USER_ALL, packageName, KILLABLE_STATE_NO));
880 }
881 if (!mWatchdogStorage.saveUserPackageSettings(entries)) {
882 Slogf.e(TAG, "Failed to write user package settings to database");
883 } else {
884 Slogf.i(TAG, "Successfully saved %d user package settings to database",
885 entries.size());
886 }
887 writeStatsLocked();
888 mIsWrittenToDatabase = true;
889 }
890 }
891
892 @GuardedBy("mLock")
893 private void writeStatsLocked() {
894 List<WatchdogStorage.IoUsageStatsEntry> entries =
895 new ArrayList<>(mUsageByUserPackage.size());
896 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
897 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
898 if (!usage.ioUsage.hasUsage()) {
899 continue;
900 }
901 entries.add(new WatchdogStorage.IoUsageStatsEntry(
902 usage.userId, usage.genericPackageName, usage.ioUsage));
903 }
904 if (!mWatchdogStorage.saveIoUsageStats(entries)) {
905 Slogf.e(TAG, "Failed to write %d I/O overuse stats to database", entries.size());
906 } else {
907 Slogf.i(TAG, "Successfully saved %d I/O overuse stats to database", entries.size());
908 }
909 }
910
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700911 @GuardedBy("mLock")
912 private int getPackageKillableStateForUserPackageLocked(
913 int userId, String genericPackageName, int componentType, boolean isSafeToKill) {
914 String key = getUserPackageUniqueId(userId, genericPackageName);
915 PackageResourceUsage usage = mUsageByUserPackage.get(key);
916 if (usage == null) {
917 usage = new PackageResourceUsage(userId, genericPackageName);
918 }
919 int killableState = usage.syncAndFetchKillableStateLocked(componentType, isSafeToKill);
920 mUsageByUserPackage.put(key, usage);
921 return killableState;
922 }
923
924 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700925 private void notifyResourceOveruseStatsLocked(int uid,
926 ResourceOveruseStats resourceOveruseStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700927 String genericPackageName = resourceOveruseStats.getPackageName();
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700928 ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid);
929 if (listenerInfos != null) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700930 for (int i = 0; i < listenerInfos.size(); ++i) {
931 listenerInfos.get(i).notifyListener(
932 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700933 }
934 }
935 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700936 ArrayList<ResourceOveruseListenerInfo> systemListenerInfos =
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700937 mOveruseSystemListenerInfosByUid.valueAt(i);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700938 for (int j = 0; j < systemListenerInfos.size(); ++j) {
939 systemListenerInfos.get(j).notifyListener(
940 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700941 }
942 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700943 if (DEBUG) {
944 Slogf.d(TAG, "Notified resource overuse stats to listening applications");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700945 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700946 }
947
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700948 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700949 private void checkAndHandleDateChangeLocked() {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700950 ZonedDateTime currentDate = mTimeSource.now().atZone(ZoneOffset.UTC)
951 .truncatedTo(STATS_TEMPORAL_UNIT);
952 if (currentDate.equals(mLatestStatsReportDate)) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700953 return;
954 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700955 /* After the first database read or on the first stats sync from the daemon, whichever
956 * happens first, the cached stats would either be empty or initialized from the database.
957 * In either case, don't write to database.
958 */
959 if (mLatestStatsReportDate != null && !mIsWrittenToDatabase) {
960 writeStatsLocked();
961 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700962 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
963 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700964 // Forgive the daily disabled package on date change.
965 for (Map.Entry<String, Integer> entry : usage.oldEnabledStateByPackage.entrySet()) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700966 try {
967 IPackageManager packageManager = ActivityThread.getPackageManager();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700968 if (packageManager.getApplicationEnabledSetting(entry.getKey(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700969 usage.userId)
970 != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
971 continue;
972 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700973 packageManager.setApplicationEnabledSetting(entry.getKey(),
974 entry.getValue(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700975 /* flags= */ 0, usage.userId, mContext.getPackageName());
976 } catch (RemoteException e) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700977 Slogf.e(TAG,
978 "Failed to reset enabled setting for disabled package '%s', user '%d'",
979 usage.genericPackageName, usage.userId);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700980 }
981 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700982 usage.resetStatsLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700983 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700984 mLatestStatsReportDate = currentDate;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700985 if (DEBUG) {
986 Slogf.d(TAG, "Handled date change successfully");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700987 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700988 }
989
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700990 @GuardedBy("mLock")
991 private PackageResourceUsage cacheAndFetchUsageLocked(
992 @UserIdInt int userId, String genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700993 android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700994 String key = getUserPackageUniqueId(userId, genericPackageName);
995 PackageResourceUsage usage = mUsageByUserPackage.get(key);
996 if (usage == null) {
997 usage = new PackageResourceUsage(userId, genericPackageName);
998 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700999 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001000 mUsageByUserPackage.put(key, usage);
1001 return usage;
1002 }
1003
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001004 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001005 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
1006 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001007 * TODO(b/192294393): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001008 * has recurring I/O overuse behavior.
1009 */
1010 return false;
1011 }
1012
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001013 private IoOveruseStats getIoOveruseStatsForPeriod(int userId, String genericPackageName,
1014 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
1015 synchronized (mLock) {
1016 String key = getUserPackageUniqueId(userId, genericPackageName);
1017 PackageResourceUsage usage = mUsageByUserPackage.get(key);
1018 if (usage == null) {
1019 return null;
1020 }
1021 return getIoOveruseStatsLocked(usage, /* minimumBytesWritten= */ 0, maxStatsPeriod);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001022 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001023 }
1024
1025 @GuardedBy("mLock")
1026 private IoOveruseStats getIoOveruseStatsLocked(PackageResourceUsage usage,
1027 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001028 if (!usage.ioUsage.hasUsage()) {
1029 /* Return I/O overuse stats only when the package has usage for the current day.
1030 * Without the current day usage, the returned stats will contain zero remaining
1031 * bytes, which is incorrect.
1032 */
1033 return null;
1034 }
1035 IoOveruseStats currentStats = usage.getIoOveruseStatsLocked();
1036 long totalBytesWritten = currentStats.getTotalBytesWritten();
1037 int numDays = toNumDays(maxStatsPeriod);
1038 IoOveruseStats historyStats = null;
1039 if (numDays > 0) {
1040 historyStats = mWatchdogStorage.getHistoricalIoOveruseStats(
1041 usage.userId, usage.genericPackageName, numDays - 1);
1042 totalBytesWritten += historyStats != null ? historyStats.getTotalBytesWritten() : 0;
1043 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001044 if (totalBytesWritten < minimumBytesWritten) {
1045 return null;
1046 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001047 if (historyStats == null) {
1048 return currentStats;
1049 }
1050 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
1051 historyStats.getStartTime(),
1052 historyStats.getDurationInSeconds() + currentStats.getDurationInSeconds());
1053 statsBuilder.setTotalTimesKilled(
1054 historyStats.getTotalTimesKilled() + currentStats.getTotalTimesKilled());
1055 statsBuilder.setTotalOveruses(
1056 historyStats.getTotalOveruses() + currentStats.getTotalOveruses());
1057 statsBuilder.setTotalBytesWritten(
1058 historyStats.getTotalBytesWritten() + currentStats.getTotalBytesWritten());
1059 statsBuilder.setKillableOnOveruse(currentStats.isKillableOnOveruse());
1060 statsBuilder.setRemainingWriteBytes(currentStats.getRemainingWriteBytes());
1061 return statsBuilder.build();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001062 }
1063
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001064 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001065 private void addResourceOveruseListenerLocked(
1066 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
1067 @NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001068 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001069 int callingPid = Binder.getCallingPid();
1070 int callingUid = Binder.getCallingUid();
1071 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
1072 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
1073 "resource overuse listener";
1074
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001075 IBinder binder = listener.asBinder();
1076 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
1077 if (listenerInfos == null) {
1078 listenerInfos = new ArrayList<>();
1079 listenerInfosByUid.put(callingUid, listenerInfos);
1080 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001081 for (int i = 0; i < listenerInfos.size(); ++i) {
1082 if (listenerInfos.get(i).listener.asBinder() == binder) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001083 throw new IllegalStateException(
1084 "Cannot add " + listenerType + " as it is already added");
1085 }
1086 }
1087
1088 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
1089 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
1090 try {
1091 listenerInfo.linkToDeath();
1092 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001093 Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001094 return;
1095 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001096 listenerInfos.add(listenerInfo);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001097 if (DEBUG) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001098 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
1099 callingPid, callingUid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001100 }
1101 }
1102
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001103 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001104 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001105 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001106 int callingUid = Binder.getCallingUid();
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001107 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
1108 ? "resource overuse system listener" : "resource overuse listener";
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001109 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
1110 if (listenerInfos == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001111 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001112 return;
1113 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001114 IBinder binder = listener.asBinder();
1115 ResourceOveruseListenerInfo cachedListenerInfo = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001116 for (int i = 0; i < listenerInfos.size(); ++i) {
1117 if (listenerInfos.get(i).listener.asBinder() == binder) {
1118 cachedListenerInfo = listenerInfos.get(i);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001119 break;
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001120 }
1121 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001122 if (cachedListenerInfo == null) {
1123 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
1124 return;
1125 }
1126 cachedListenerInfo.unlinkToDeath();
1127 listenerInfos.remove(cachedListenerInfo);
1128 if (listenerInfos.isEmpty()) {
1129 listenerInfosByUid.remove(callingUid);
1130 }
1131 if (DEBUG) {
1132 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
1133 cachedListenerInfo.pid, cachedListenerInfo.uid);
1134 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001135 }
1136
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001137 @GuardedBy("mLock")
1138 private void setPendingSetResourceOveruseConfigurationsRequestLocked(
1139 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) {
1140 if (mPendingSetResourceOveruseConfigurationsRequest != null) {
1141 if (mPendingSetResourceOveruseConfigurationsRequest == configs) {
1142 return;
1143 }
1144 throw new IllegalStateException(
1145 "Pending setResourceOveruseConfigurations request in progress");
1146 }
1147 mPendingSetResourceOveruseConfigurationsRequest = configs;
1148 }
1149
1150 private void retryPendingSetResourceOveruseConfigurations() {
1151 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001152 synchronized (mLock) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001153 if (mPendingSetResourceOveruseConfigurationsRequest == null) {
1154 return;
1155 }
1156 configs = mPendingSetResourceOveruseConfigurationsRequest;
1157 }
1158 try {
1159 int result = setResourceOveruseConfigurationsInternal(configs,
1160 /* isPendingRequest= */ true);
1161 if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) {
1162 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code "
1163 + "%d", result);
1164 }
1165 } catch (Exception e) {
1166 Slogf.e(TAG, e, "Exception on set pending resource overuse configurations");
1167 }
1168 }
1169
1170 private int setResourceOveruseConfigurationsInternal(
1171 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs,
1172 boolean isPendingRequest) throws RemoteException {
1173 boolean doClearPendingRequest = isPendingRequest;
1174 try {
1175 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs);
Lakshman Annadoraia7189562021-08-17 16:36:16 -07001176 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001177 } catch (RemoteException e) {
1178 if (e instanceof TransactionTooLargeException) {
1179 throw e;
1180 }
1181 Slogf.e(TAG, e, "Remote exception on set resource overuse configuration");
1182 synchronized (mLock) {
1183 setPendingSetResourceOveruseConfigurationsRequestLocked(configs);
1184 }
1185 doClearPendingRequest = false;
1186 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1187 } finally {
1188 if (doClearPendingRequest) {
1189 synchronized (mLock) {
1190 mPendingSetResourceOveruseConfigurationsRequest = null;
1191 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001192 }
1193 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001194 if (DEBUG) {
1195 Slogf.d(TAG, "Set the resource overuse configuration successfully");
1196 }
1197 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1198 }
1199
1200 private boolean isConnectedToDaemon() {
1201 synchronized (mLock) {
1202 long startTimeMillis = SystemClock.uptimeMillis();
1203 long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1204 while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) {
1205 try {
1206 mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis);
1207 } catch (InterruptedException e) {
1208 Thread.currentThread().interrupt();
1209 continue;
1210 } finally {
1211 sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1212 }
1213 break;
1214 }
1215 return mIsConnectedToDaemon;
1216 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001217 }
1218
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +00001219 @GuardedBy("mLock")
1220 private boolean isSafeToKillLocked(String genericPackageName, int componentType,
1221 List<String> sharedPackages) {
1222 BiFunction<List<String>, Set<String>, Boolean> isSafeToKillAnyPackage =
1223 (packages, safeToKillPackages) -> {
1224 if (packages == null) {
1225 return false;
1226 }
1227 for (int i = 0; i < packages.size(); i++) {
1228 if (safeToKillPackages.contains(packages.get(i))) {
1229 return true;
1230 }
1231 }
1232 return false;
1233 };
1234
1235 switch (componentType) {
1236 case ComponentType.SYSTEM:
1237 if (mSafeToKillSystemPackages.contains(genericPackageName)) {
1238 return true;
1239 }
1240 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages);
1241 case ComponentType.VENDOR:
1242 if (mSafeToKillVendorPackages.contains(genericPackageName)) {
1243 return true;
1244 }
1245 /*
1246 * Packages under the vendor shared UID may contain system packages because when
1247 * CarWatchdogService derives the shared component type it attributes system
1248 * packages as vendor packages when there is at least one vendor package.
1249 */
1250 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages)
1251 || isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillVendorPackages);
1252 default:
1253 // Third-party apps are always killable
1254 return true;
1255 }
1256 }
1257
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001258 private static String getUserPackageUniqueId(int userId, String genericPackageName) {
1259 return String.valueOf(userId) + ":" + genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001260 }
1261
1262 @VisibleForTesting
1263 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001264 android.automotive.watchdog.IoOveruseStats internalStats,
1265 int totalTimesKilled, boolean isKillableOnOveruses) {
1266 return new IoOveruseStats.Builder(internalStats.startTime, internalStats.durationInSeconds)
1267 .setTotalOveruses(internalStats.totalOveruses)
1268 .setTotalTimesKilled(totalTimesKilled)
1269 .setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes))
1270 .setKillableOnOveruse(isKillableOnOveruses)
1271 .setRemainingWriteBytes(toPerStateBytes(internalStats.remainingWriteBytes));
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001272 }
1273
1274 private static PerStateBytes toPerStateBytes(
1275 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001276 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001277 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001278 }
1279
1280 private static long totalPerStateBytes(
1281 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
1282 BiFunction<Long, Long, Long> sum = (l, r) -> {
1283 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
1284 };
1285 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
1286 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
1287 }
1288
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001289 private static long getMinimumBytesWritten(
1290 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
1291 switch (minimumStatsIoFlag) {
1292 case 0:
1293 return 0;
1294 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
1295 return 1024 * 1024;
1296 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
1297 return 100 * 1024 * 1024;
1298 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
1299 return 1024 * 1024 * 1024;
1300 default:
1301 throw new IllegalArgumentException(
1302 "Must provide valid minimum stats flag for I/O resource");
1303 }
1304 }
1305
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001306 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
1307 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
1308 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1309 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
1310 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
1311 internalConfig.componentType = config.getComponentType();
1312 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
1313 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
1314 internalConfig.packageMetadata = new ArrayList<>();
1315 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
1316 if (entry.getKey().isEmpty()) {
1317 continue;
1318 }
1319 PackageMetadata metadata = new PackageMetadata();
1320 metadata.packageName = entry.getKey();
1321 switch(entry.getValue()) {
1322 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1323 metadata.appCategoryType = ApplicationCategoryType.MAPS;
1324 break;
1325 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1326 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
1327 break;
1328 default:
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001329 Slogf.i(TAG, "Invalid application category type: %s skipping package: %s",
1330 entry.getValue(), metadata.packageName);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001331 continue;
1332 }
1333 internalConfig.packageMetadata.add(metadata);
1334 }
1335 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
1336 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1337 && config.getIoOveruseConfiguration() != null) {
1338 internalConfig.resourceSpecificConfigurations.add(
1339 toResourceSpecificConfiguration(config.getComponentType(),
1340 config.getIoOveruseConfiguration()));
1341 }
1342 return internalConfig;
1343 }
1344
1345 private static ResourceSpecificConfiguration
1346 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
1347 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
1348 new android.automotive.watchdog.internal.IoOveruseConfiguration();
1349 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
1350 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
1351 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
1352 config.getPackageSpecificThresholds());
1353 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
1354 config.getAppCategorySpecificThresholds());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001355 for (int i = 0; i < internalConfig.categorySpecificThresholds.size(); ++i) {
1356 PerStateIoOveruseThreshold threshold = internalConfig.categorySpecificThresholds.get(i);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001357 switch(threshold.name) {
1358 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1359 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
1360 break;
1361 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1362 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
1363 break;
1364 default:
1365 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
1366 }
1367 }
1368 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
1369 config.getSystemWideThresholds());
1370
1371 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
1372 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
1373 return resourceSpecificConfig;
1374 }
1375
1376 @VisibleForTesting
1377 static String toComponentTypeStr(int componentType) {
1378 switch(componentType) {
1379 case ComponentType.SYSTEM:
1380 return "SYSTEM";
1381 case ComponentType.VENDOR:
1382 return "VENDOR";
1383 case ComponentType.THIRD_PARTY:
1384 return "THIRD_PARTY";
1385 default:
1386 return "UNKNOWN";
1387 }
1388 }
1389
1390 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
1391 Map<String, PerStateBytes> thresholds) {
1392 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
1393 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001394 if (!thresholds.isEmpty()) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001395 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
1396 entry.getValue()));
1397 }
1398 }
1399 return internalThresholds;
1400 }
1401
1402 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
1403 PerStateBytes perStateBytes) {
1404 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
1405 threshold.name = name;
1406 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
1407 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
1408 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
1409 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
1410 return threshold;
1411 }
1412
1413 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
1414 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
1415 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
1416 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001417 for (int i = 0; i < thresholds.size(); ++i) {
1418 if (thresholds.get(i).getDurationInSeconds() == 0
1419 || thresholds.get(i).getWrittenBytesPerSecond() == 0) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001420 continue;
1421 }
1422 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
1423 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001424 internalThreshold.durationInSeconds = thresholds.get(i).getDurationInSeconds();
1425 internalThreshold.writtenBytesPerSecond = thresholds.get(i).getWrittenBytesPerSecond();
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001426 internalThresholds.add(internalThreshold);
1427 }
1428 return internalThresholds;
1429 }
1430
1431 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
1432 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
1433 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001434 ArrayMap<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
1435 for (int i = 0; i < internalConfig.packageMetadata.size(); ++i) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001436 String categoryTypeStr;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001437 switch (internalConfig.packageMetadata.get(i).appCategoryType) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001438 case ApplicationCategoryType.MAPS:
1439 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
1440 break;
1441 case ApplicationCategoryType.MEDIA:
1442 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
1443 break;
1444 default:
1445 continue;
1446 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001447 packagesToAppCategoryTypes.put(
1448 internalConfig.packageMetadata.get(i).packageName, categoryTypeStr);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001449 }
1450 ResourceOveruseConfiguration.Builder configBuilder =
1451 new ResourceOveruseConfiguration.Builder(
1452 internalConfig.componentType,
1453 internalConfig.safeToKillPackages,
1454 internalConfig.vendorPackagePrefixes,
1455 packagesToAppCategoryTypes);
1456 for (ResourceSpecificConfiguration resourceSpecificConfig :
1457 internalConfig.resourceSpecificConfigurations) {
1458 if (resourceSpecificConfig.getTag()
1459 == ResourceSpecificConfiguration.ioOveruseConfiguration
1460 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
1461 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
1462 resourceSpecificConfig.getIoOveruseConfiguration()));
1463 }
1464 }
1465 return configBuilder.build();
1466 }
1467
1468 private static IoOveruseConfiguration toIoOveruseConfiguration(
1469 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001470 TriConsumer<Map<String, PerStateBytes>, String, String> replaceKey =
1471 (map, oldKey, newKey) -> {
1472 PerStateBytes perStateBytes = map.get(oldKey);
1473 if (perStateBytes != null) {
1474 map.put(newKey, perStateBytes);
1475 map.remove(oldKey);
1476 }
1477 };
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001478 PerStateBytes componentLevelThresholds =
1479 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001480 ArrayMap<String, PerStateBytes> packageSpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001481 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001482 ArrayMap<String, PerStateBytes> appCategorySpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001483 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001484 replaceKey.accept(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001485 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001486 replaceKey.accept(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001487 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
1488 List<IoOveruseAlertThreshold> systemWideThresholds =
1489 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
1490
1491 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1492 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1493 systemWideThresholds);
1494 return configBuilder.build();
1495 }
1496
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001497 private static ArrayMap<String, PerStateBytes> toPerStateBytesMap(
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001498 List<PerStateIoOveruseThreshold> thresholds) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001499 ArrayMap<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1500 for (int i = 0; i < thresholds.size(); ++i) {
1501 thresholdsMap.put(
1502 thresholds.get(i).name, toPerStateBytes(thresholds.get(i).perStateWriteBytes));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001503 }
1504 return thresholdsMap;
1505 }
1506
1507 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1508 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1509 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001510 for (int i = 0; i < internalThresholds.size(); ++i) {
1511 thresholds.add(new IoOveruseAlertThreshold(internalThresholds.get(i).durationInSeconds,
1512 internalThresholds.get(i).writtenBytesPerSecond));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001513 }
1514 return thresholds;
1515 }
1516
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001517 private static void checkResourceOveruseConfigs(
1518 List<ResourceOveruseConfiguration> configurations,
1519 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1520 ArraySet<Integer> seenComponentTypes = new ArraySet<>();
1521 for (int i = 0; i < configurations.size(); ++i) {
1522 ResourceOveruseConfiguration config = configurations.get(i);
1523 if (seenComponentTypes.contains(config.getComponentType())) {
1524 throw new IllegalArgumentException(
1525 "Cannot provide duplicate configurations for the same component type");
1526 }
1527 checkResourceOveruseConfig(config, resourceOveruseFlag);
1528 seenComponentTypes.add(config.getComponentType());
1529 }
1530 }
1531
1532 private static void checkResourceOveruseConfig(ResourceOveruseConfiguration config,
1533 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1534 int componentType = config.getComponentType();
1535 if (toComponentTypeStr(componentType).equals("UNKNOWN")) {
1536 throw new IllegalArgumentException(
1537 "Invalid component type in the configuration: " + componentType);
1538 }
1539 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1540 && config.getIoOveruseConfiguration() == null) {
1541 throw new IllegalArgumentException("Must provide I/O overuse configuration");
1542 }
1543 checkIoOveruseConfig(config.getIoOveruseConfiguration(), componentType);
1544 }
1545
1546 private static void checkIoOveruseConfig(IoOveruseConfiguration config, int componentType) {
1547 if (config.getComponentLevelThresholds().getBackgroundModeBytes() <= 0
1548 || config.getComponentLevelThresholds().getForegroundModeBytes() <= 0
1549 || config.getComponentLevelThresholds().getGarageModeBytes() <= 0) {
1550 throw new IllegalArgumentException(
1551 "For component: " + toComponentTypeStr(componentType)
1552 + " some thresholds are zero for: "
1553 + config.getComponentLevelThresholds().toString());
1554 }
1555 if (componentType == ComponentType.SYSTEM) {
1556 List<IoOveruseAlertThreshold> systemThresholds = config.getSystemWideThresholds();
1557 if (systemThresholds.isEmpty()) {
1558 throw new IllegalArgumentException(
1559 "Empty system-wide alert thresholds provided in "
1560 + toComponentTypeStr(componentType)
1561 + " config.");
1562 }
1563 for (int i = 0; i < systemThresholds.size(); i++) {
1564 checkIoOveruseAlertThreshold(systemThresholds.get(i));
1565 }
1566 }
1567 }
1568
1569 private static void checkIoOveruseAlertThreshold(
1570 IoOveruseAlertThreshold ioOveruseAlertThreshold) {
1571 if (ioOveruseAlertThreshold.getDurationInSeconds() <= 0) {
1572 throw new IllegalArgumentException(
1573 "System wide threshold duration must be greater than zero for: "
1574 + ioOveruseAlertThreshold);
1575 }
1576 if (ioOveruseAlertThreshold.getWrittenBytesPerSecond() <= 0) {
1577 throw new IllegalArgumentException(
1578 "System wide threshold written bytes per second must be greater than zero for: "
1579 + ioOveruseAlertThreshold);
1580 }
1581 }
1582
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001583 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1584 PerStateBytes perStateBytes = map.get(oldKey);
1585 if (perStateBytes != null) {
1586 map.put(newKey, perStateBytes);
1587 map.remove(oldKey);
1588 }
1589 }
1590
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001591 private static int toNumDays(@CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
1592 switch(maxStatsPeriod) {
1593 case STATS_PERIOD_CURRENT_DAY:
1594 return 0;
1595 case STATS_PERIOD_PAST_3_DAYS:
1596 return 3;
1597 case STATS_PERIOD_PAST_7_DAYS:
1598 return 7;
1599 case STATS_PERIOD_PAST_15_DAYS:
1600 return 15;
1601 case STATS_PERIOD_PAST_30_DAYS:
1602 return 30;
1603 default:
1604 throw new IllegalArgumentException(
1605 "Invalid max stats period provided: " + maxStatsPeriod);
1606 }
1607 }
1608
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001609 private final class PackageResourceUsage {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001610 public final String genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001611 public @UserIdInt final int userId;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001612 @GuardedBy("mLock")
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001613 public final PackageIoUsage ioUsage = new PackageIoUsage();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001614 @GuardedBy("mLock")
1615 public final ArrayMap<String, Integer> oldEnabledStateByPackage = new ArrayMap<>();
1616 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001617 private @KillableState int mKillableState;
1618
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001619 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001620 PackageResourceUsage(@UserIdInt int userId, String genericPackageName) {
1621 this.genericPackageName = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001622 this.userId = userId;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001623 this.mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001624 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001625 }
1626
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001627 public boolean isSharedPackage() {
1628 return this.genericPackageName.startsWith(SHARED_PACKAGE_PREFIX);
1629 }
1630
1631 @GuardedBy("mLock")
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001632 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001633 if (!internalStats.killableOnOveruse) {
1634 /*
1635 * Killable value specified in the internal stats is provided by the native daemon.
1636 * This value reflects whether or not an application is safe-to-kill on overuse.
1637 * This setting is from the I/O overuse configuration specified by the system and
1638 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1639 * specify the application is not killable, the application is not safe-to-kill.
1640 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001641 mKillableState = KILLABLE_STATE_NEVER;
1642 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1643 /*
1644 * This case happens when a previously unsafe to kill system/vendor package was
1645 * recently marked as safe-to-kill so update the old state to the default value.
1646 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001647 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001648 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001649 }
1650 ioUsage.update(internalStats);
1651 }
1652
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001653 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001654 return new ResourceOveruseStats.Builder(genericPackageName, UserHandle.of(userId));
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001655 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001656
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001657 @GuardedBy("mLock")
1658 public IoOveruseStats getIoOveruseStatsLocked() {
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001659 if (!ioUsage.hasUsage()) {
1660 return null;
1661 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001662 return ioUsage.getIoOveruseStats(mKillableState != KILLABLE_STATE_NEVER);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001663 }
1664
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001665 @GuardedBy("mLock")
1666 public @KillableState int getKillableStateLocked() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001667 return mKillableState;
1668 }
1669
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001670 @GuardedBy("mLock")
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001671 public void setKillableStateLocked(@KillableState int killableState) {
1672 mKillableState = killableState;
1673 }
1674
1675 @GuardedBy("mLock")
1676 public boolean verifyAndSetKillableStateLocked(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001677 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001678 return false;
1679 }
1680 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1681 return true;
1682 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001683
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001684 @GuardedBy("mLock")
1685 public int syncAndFetchKillableStateLocked(int myComponentType, boolean isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001686 /*
1687 * The killable state goes out-of-sync:
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001688 * 1. When the on-device safe-to-kill list was recently updated and the user package
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001689 * didn't have any resource usage so the native daemon didn't update the killable state.
1690 * 2. When a package has no resource usage and is initialized outside of processing the
1691 * latest resource usage stats.
1692 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001693 if (myComponentType != ComponentType.THIRD_PARTY && !isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001694 mKillableState = KILLABLE_STATE_NEVER;
1695 } else if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001696 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001697 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1698 }
1699 return mKillableState;
1700 }
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001701
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001702 @GuardedBy("mLock")
1703 public void resetStatsLocked() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001704 oldEnabledStateByPackage.clear();
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001705 ioUsage.resetStats();
1706 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001707 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001708 /** Defines I/O usage fields for a package. */
1709 public static final class PackageIoUsage {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001710 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1711 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001712 private int mTotalTimesKilled;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001713
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001714 private PackageIoUsage() {
1715 mForgivenWriteBytes = new android.automotive.watchdog.PerStateBytes();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001716 mTotalTimesKilled = 0;
1717 }
1718
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001719 public PackageIoUsage(android.automotive.watchdog.IoOveruseStats ioOveruseStats,
1720 android.automotive.watchdog.PerStateBytes forgivenWriteBytes,
1721 int totalTimesKilled) {
1722 mIoOveruseStats = ioOveruseStats;
1723 mForgivenWriteBytes = forgivenWriteBytes;
1724 mTotalTimesKilled = totalTimesKilled;
1725 }
1726
1727 public android.automotive.watchdog.IoOveruseStats getInternalIoOveruseStats() {
1728 return mIoOveruseStats;
1729 }
1730
1731 public android.automotive.watchdog.PerStateBytes getForgivenWriteBytes() {
1732 return mForgivenWriteBytes;
1733 }
1734
1735 public int getTotalTimesKilled() {
1736 return mTotalTimesKilled;
1737 }
1738
1739 boolean hasUsage() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001740 return mIoOveruseStats != null;
1741 }
1742
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001743 void overwrite(PackageIoUsage ioUsage) {
1744 mIoOveruseStats = ioUsage.mIoOveruseStats;
1745 mForgivenWriteBytes = ioUsage.mForgivenWriteBytes;
1746 mTotalTimesKilled = ioUsage.mTotalTimesKilled;
1747 }
1748
1749 void update(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001750 mIoOveruseStats = internalStats;
1751 if (exceedsThreshold()) {
1752 /*
1753 * Forgive written bytes on overuse as the package is either forgiven or killed on
1754 * overuse. When the package is killed, the user may opt to open the corresponding
1755 * app and the package should be forgiven anyways.
1756 * NOTE: If this logic is updated, update the daemon side logic as well.
1757 */
1758 mForgivenWriteBytes = internalStats.writtenBytes;
1759 }
1760 }
1761
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001762 IoOveruseStats getIoOveruseStats(boolean isKillable) {
1763 return toIoOveruseStatsBuilder(mIoOveruseStats, mTotalTimesKilled, isKillable).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001764 }
1765
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001766 boolean exceedsThreshold() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001767 if (!hasUsage()) {
1768 return false;
1769 }
1770 android.automotive.watchdog.PerStateBytes remaining =
1771 mIoOveruseStats.remainingWriteBytes;
1772 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1773 || remaining.garageModeBytes == 0;
1774 }
1775
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001776 void killed() {
1777 ++mTotalTimesKilled;
1778 }
1779
1780 void resetStats() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001781 mIoOveruseStats = null;
1782 mForgivenWriteBytes = null;
1783 mTotalTimesKilled = 0;
1784 }
1785 }
1786
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001787 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1788 public final IResourceOveruseListener listener;
1789 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1790 public final int pid;
1791 public final int uid;
1792 public final boolean isListenerForSystem;
1793
1794 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1795 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1796 boolean isListenerForSystem) {
1797 this.listener = listener;
1798 this.flag = flag;
1799 this.pid = pid;
1800 this.uid = uid;
1801 this.isListenerForSystem = isListenerForSystem;
1802 }
1803
1804 @Override
1805 public void binderDied() {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001806 Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001807 isListenerForSystem ? " for system" : "", pid);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001808 Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo =
1809 listenerInfosByUid -> {
1810 ArrayList<ResourceOveruseListenerInfo> listenerInfos =
1811 listenerInfosByUid.get(uid);
1812 if (listenerInfos == null) {
1813 return;
1814 }
1815 listenerInfos.remove(this);
1816 if (listenerInfos.isEmpty()) {
1817 listenerInfosByUid.remove(uid);
1818 }
1819 };
1820 if (isListenerForSystem) {
1821 removeListenerInfo.accept(mOveruseSystemListenerInfosByUid);
1822 } else {
1823 removeListenerInfo.accept(mOveruseListenerInfosByUid);
1824 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001825 unlinkToDeath();
1826 }
1827
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001828 public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001829 int overusingUid, String overusingGenericPackageName,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001830 ResourceOveruseStats resourceOveruseStats) {
1831 if ((flag & resourceType) == 0) {
1832 return;
1833 }
1834 try {
1835 listener.onOveruse(resourceOveruseStats);
1836 } catch (RemoteException e) {
1837 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001838 + "package(uid %d, generic package name '%s'): %s",
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001839 (isListenerForSystem ? "system listener" : "listener"), uid, pid,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001840 overusingUid, overusingGenericPackageName, e);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001841 }
1842 }
1843
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001844 private void linkToDeath() throws RemoteException {
1845 listener.asBinder().linkToDeath(this, 0);
1846 }
1847
1848 private void unlinkToDeath() {
1849 listener.asBinder().unlinkToDeath(this, 0);
1850 }
1851 }
1852}