blob: fee73b96686a28cb6150f8da472597838c298af0 [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);
800 break;
801 default:
802 // All third-party apps are killable.
803 break;
804 }
805 }
806 if (DEBUG) {
807 Slogf.d(TAG, "Fetched and synced safe to kill packages.");
808 }
809 }
810 }
811
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700812 private void readFromDatabase() {
813 List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries =
814 mWatchdogStorage.getUserPackageSettings();
815 Slogf.i(TAG, "Read %d user package settings from database", settingsEntries.size());
816 List<WatchdogStorage.IoUsageStatsEntry> ioStatsEntries =
817 mWatchdogStorage.getTodayIoUsageStats();
818 Slogf.i(TAG, "Read %d I/O usage stats from database", ioStatsEntries.size());
819 synchronized (mLock) {
820 for (int i = 0; i < settingsEntries.size(); ++i) {
821 WatchdogStorage.UserPackageSettingsEntry entry = settingsEntries.get(i);
822 if (entry.userId == UserHandle.USER_ALL) {
823 if (entry.killableState != KILLABLE_STATE_YES) {
824 mDefaultNotKillableGenericPackages.add(entry.packageName);
825 }
826 continue;
827 }
828 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
829 PackageResourceUsage usage = mUsageByUserPackage.get(key);
830 if (usage == null) {
831 usage = new PackageResourceUsage(entry.userId, entry.packageName);
832 }
833 usage.setKillableStateLocked(entry.killableState);
834 mUsageByUserPackage.put(key, usage);
835 }
836 ZonedDateTime curReportDate =
837 mTimeSource.now().atZone(ZONE_OFFSET).truncatedTo(STATS_TEMPORAL_UNIT);
838 for (int i = 0; i < ioStatsEntries.size(); ++i) {
839 WatchdogStorage.IoUsageStatsEntry entry = ioStatsEntries.get(i);
840 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
841 PackageResourceUsage usage = mUsageByUserPackage.get(key);
842 if (usage == null) {
843 usage = new PackageResourceUsage(entry.userId, entry.packageName);
844 }
845 /* Overwrite in memory cache as the stats will be merged on the daemon side and
846 * pushed on the next latestIoOveruseStats call. This is tolerable because the next
847 * push should happen soon.
848 */
849 usage.ioUsage.overwrite(entry.ioUsage);
850 mUsageByUserPackage.put(key, usage);
851 }
852 if (!ioStatsEntries.isEmpty()) {
853 /* When mLatestStatsReportDate is null, the latest stats push from daemon hasn't
854 * happened yet. Thus the cached stats contains only the stats read from database.
855 */
856 mIsWrittenToDatabase = mLatestStatsReportDate == null;
857 mLatestStatsReportDate = curReportDate;
858 }
859 }
860 }
861
862 /** Writes user package settings and stats to database. */
863 public void writeToDatabase() {
864 synchronized (mLock) {
865 if (mIsWrittenToDatabase) {
866 return;
867 }
868 List<WatchdogStorage.UserPackageSettingsEntry> entries =
869 new ArrayList<>(mUsageByUserPackage.size());
870 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
871 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
872 entries.add(new WatchdogStorage.UserPackageSettingsEntry(
873 usage.userId, usage.genericPackageName, usage.getKillableStateLocked()));
874 }
875 for (String packageName : mDefaultNotKillableGenericPackages) {
876 entries.add(new WatchdogStorage.UserPackageSettingsEntry(
877 UserHandle.USER_ALL, packageName, KILLABLE_STATE_NO));
878 }
879 if (!mWatchdogStorage.saveUserPackageSettings(entries)) {
880 Slogf.e(TAG, "Failed to write user package settings to database");
881 } else {
882 Slogf.i(TAG, "Successfully saved %d user package settings to database",
883 entries.size());
884 }
885 writeStatsLocked();
886 mIsWrittenToDatabase = true;
887 }
888 }
889
890 @GuardedBy("mLock")
891 private void writeStatsLocked() {
892 List<WatchdogStorage.IoUsageStatsEntry> entries =
893 new ArrayList<>(mUsageByUserPackage.size());
894 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
895 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
896 if (!usage.ioUsage.hasUsage()) {
897 continue;
898 }
899 entries.add(new WatchdogStorage.IoUsageStatsEntry(
900 usage.userId, usage.genericPackageName, usage.ioUsage));
901 }
902 if (!mWatchdogStorage.saveIoUsageStats(entries)) {
903 Slogf.e(TAG, "Failed to write %d I/O overuse stats to database", entries.size());
904 } else {
905 Slogf.i(TAG, "Successfully saved %d I/O overuse stats to database", entries.size());
906 }
907 }
908
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700909 @GuardedBy("mLock")
910 private int getPackageKillableStateForUserPackageLocked(
911 int userId, String genericPackageName, int componentType, boolean isSafeToKill) {
912 String key = getUserPackageUniqueId(userId, genericPackageName);
913 PackageResourceUsage usage = mUsageByUserPackage.get(key);
914 if (usage == null) {
915 usage = new PackageResourceUsage(userId, genericPackageName);
916 }
917 int killableState = usage.syncAndFetchKillableStateLocked(componentType, isSafeToKill);
918 mUsageByUserPackage.put(key, usage);
919 return killableState;
920 }
921
922 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700923 private void notifyResourceOveruseStatsLocked(int uid,
924 ResourceOveruseStats resourceOveruseStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700925 String genericPackageName = resourceOveruseStats.getPackageName();
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700926 ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid);
927 if (listenerInfos != null) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700928 for (int i = 0; i < listenerInfos.size(); ++i) {
929 listenerInfos.get(i).notifyListener(
930 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700931 }
932 }
933 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700934 ArrayList<ResourceOveruseListenerInfo> systemListenerInfos =
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700935 mOveruseSystemListenerInfosByUid.valueAt(i);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700936 for (int j = 0; j < systemListenerInfos.size(); ++j) {
937 systemListenerInfos.get(j).notifyListener(
938 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700939 }
940 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700941 if (DEBUG) {
942 Slogf.d(TAG, "Notified resource overuse stats to listening applications");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700943 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700944 }
945
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700946 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700947 private void checkAndHandleDateChangeLocked() {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700948 ZonedDateTime currentDate = mTimeSource.now().atZone(ZoneOffset.UTC)
949 .truncatedTo(STATS_TEMPORAL_UNIT);
950 if (currentDate.equals(mLatestStatsReportDate)) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700951 return;
952 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700953 /* After the first database read or on the first stats sync from the daemon, whichever
954 * happens first, the cached stats would either be empty or initialized from the database.
955 * In either case, don't write to database.
956 */
957 if (mLatestStatsReportDate != null && !mIsWrittenToDatabase) {
958 writeStatsLocked();
959 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700960 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
961 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700962 // Forgive the daily disabled package on date change.
963 for (Map.Entry<String, Integer> entry : usage.oldEnabledStateByPackage.entrySet()) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700964 try {
965 IPackageManager packageManager = ActivityThread.getPackageManager();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700966 if (packageManager.getApplicationEnabledSetting(entry.getKey(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700967 usage.userId)
968 != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
969 continue;
970 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700971 packageManager.setApplicationEnabledSetting(entry.getKey(),
972 entry.getValue(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700973 /* flags= */ 0, usage.userId, mContext.getPackageName());
974 } catch (RemoteException e) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700975 Slogf.e(TAG,
976 "Failed to reset enabled setting for disabled package '%s', user '%d'",
977 usage.genericPackageName, usage.userId);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700978 }
979 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700980 usage.resetStatsLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700981 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700982 mLatestStatsReportDate = currentDate;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700983 if (DEBUG) {
984 Slogf.d(TAG, "Handled date change successfully");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700985 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700986 }
987
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700988 @GuardedBy("mLock")
989 private PackageResourceUsage cacheAndFetchUsageLocked(
990 @UserIdInt int userId, String genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700991 android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700992 String key = getUserPackageUniqueId(userId, genericPackageName);
993 PackageResourceUsage usage = mUsageByUserPackage.get(key);
994 if (usage == null) {
995 usage = new PackageResourceUsage(userId, genericPackageName);
996 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700997 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700998 mUsageByUserPackage.put(key, usage);
999 return usage;
1000 }
1001
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001002 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001003 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
1004 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001005 * TODO(b/192294393): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001006 * has recurring I/O overuse behavior.
1007 */
1008 return false;
1009 }
1010
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001011 private IoOveruseStats getIoOveruseStatsForPeriod(int userId, String genericPackageName,
1012 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
1013 synchronized (mLock) {
1014 String key = getUserPackageUniqueId(userId, genericPackageName);
1015 PackageResourceUsage usage = mUsageByUserPackage.get(key);
1016 if (usage == null) {
1017 return null;
1018 }
1019 return getIoOveruseStatsLocked(usage, /* minimumBytesWritten= */ 0, maxStatsPeriod);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001020 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001021 }
1022
1023 @GuardedBy("mLock")
1024 private IoOveruseStats getIoOveruseStatsLocked(PackageResourceUsage usage,
1025 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001026 if (!usage.ioUsage.hasUsage()) {
1027 /* Return I/O overuse stats only when the package has usage for the current day.
1028 * Without the current day usage, the returned stats will contain zero remaining
1029 * bytes, which is incorrect.
1030 */
1031 return null;
1032 }
1033 IoOveruseStats currentStats = usage.getIoOveruseStatsLocked();
1034 long totalBytesWritten = currentStats.getTotalBytesWritten();
1035 int numDays = toNumDays(maxStatsPeriod);
1036 IoOveruseStats historyStats = null;
1037 if (numDays > 0) {
1038 historyStats = mWatchdogStorage.getHistoricalIoOveruseStats(
1039 usage.userId, usage.genericPackageName, numDays - 1);
1040 totalBytesWritten += historyStats != null ? historyStats.getTotalBytesWritten() : 0;
1041 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001042 if (totalBytesWritten < minimumBytesWritten) {
1043 return null;
1044 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001045 if (historyStats == null) {
1046 return currentStats;
1047 }
1048 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
1049 historyStats.getStartTime(),
1050 historyStats.getDurationInSeconds() + currentStats.getDurationInSeconds());
1051 statsBuilder.setTotalTimesKilled(
1052 historyStats.getTotalTimesKilled() + currentStats.getTotalTimesKilled());
1053 statsBuilder.setTotalOveruses(
1054 historyStats.getTotalOveruses() + currentStats.getTotalOveruses());
1055 statsBuilder.setTotalBytesWritten(
1056 historyStats.getTotalBytesWritten() + currentStats.getTotalBytesWritten());
1057 statsBuilder.setKillableOnOveruse(currentStats.isKillableOnOveruse());
1058 statsBuilder.setRemainingWriteBytes(currentStats.getRemainingWriteBytes());
1059 return statsBuilder.build();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001060 }
1061
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001062 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001063 private void addResourceOveruseListenerLocked(
1064 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
1065 @NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001066 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001067 int callingPid = Binder.getCallingPid();
1068 int callingUid = Binder.getCallingUid();
1069 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
1070 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
1071 "resource overuse listener";
1072
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001073 IBinder binder = listener.asBinder();
1074 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
1075 if (listenerInfos == null) {
1076 listenerInfos = new ArrayList<>();
1077 listenerInfosByUid.put(callingUid, listenerInfos);
1078 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001079 for (int i = 0; i < listenerInfos.size(); ++i) {
1080 if (listenerInfos.get(i).listener.asBinder() == binder) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001081 throw new IllegalStateException(
1082 "Cannot add " + listenerType + " as it is already added");
1083 }
1084 }
1085
1086 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
1087 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
1088 try {
1089 listenerInfo.linkToDeath();
1090 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001091 Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001092 return;
1093 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001094 listenerInfos.add(listenerInfo);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001095 if (DEBUG) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001096 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
1097 callingPid, callingUid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001098 }
1099 }
1100
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001101 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001102 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001103 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001104 int callingUid = Binder.getCallingUid();
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001105 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
1106 ? "resource overuse system listener" : "resource overuse listener";
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001107 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
1108 if (listenerInfos == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001109 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001110 return;
1111 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001112 IBinder binder = listener.asBinder();
1113 ResourceOveruseListenerInfo cachedListenerInfo = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001114 for (int i = 0; i < listenerInfos.size(); ++i) {
1115 if (listenerInfos.get(i).listener.asBinder() == binder) {
1116 cachedListenerInfo = listenerInfos.get(i);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001117 break;
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001118 }
1119 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001120 if (cachedListenerInfo == null) {
1121 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
1122 return;
1123 }
1124 cachedListenerInfo.unlinkToDeath();
1125 listenerInfos.remove(cachedListenerInfo);
1126 if (listenerInfos.isEmpty()) {
1127 listenerInfosByUid.remove(callingUid);
1128 }
1129 if (DEBUG) {
1130 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
1131 cachedListenerInfo.pid, cachedListenerInfo.uid);
1132 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001133 }
1134
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001135 @GuardedBy("mLock")
1136 private void setPendingSetResourceOveruseConfigurationsRequestLocked(
1137 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) {
1138 if (mPendingSetResourceOveruseConfigurationsRequest != null) {
1139 if (mPendingSetResourceOveruseConfigurationsRequest == configs) {
1140 return;
1141 }
1142 throw new IllegalStateException(
1143 "Pending setResourceOveruseConfigurations request in progress");
1144 }
1145 mPendingSetResourceOveruseConfigurationsRequest = configs;
1146 }
1147
1148 private void retryPendingSetResourceOveruseConfigurations() {
1149 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001150 synchronized (mLock) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001151 if (mPendingSetResourceOveruseConfigurationsRequest == null) {
1152 return;
1153 }
1154 configs = mPendingSetResourceOveruseConfigurationsRequest;
1155 }
1156 try {
1157 int result = setResourceOveruseConfigurationsInternal(configs,
1158 /* isPendingRequest= */ true);
1159 if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) {
1160 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code "
1161 + "%d", result);
1162 }
1163 } catch (Exception e) {
1164 Slogf.e(TAG, e, "Exception on set pending resource overuse configurations");
1165 }
1166 }
1167
1168 private int setResourceOveruseConfigurationsInternal(
1169 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs,
1170 boolean isPendingRequest) throws RemoteException {
1171 boolean doClearPendingRequest = isPendingRequest;
1172 try {
1173 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs);
Lakshman Annadoraia7189562021-08-17 16:36:16 -07001174 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001175 } catch (RemoteException e) {
1176 if (e instanceof TransactionTooLargeException) {
1177 throw e;
1178 }
1179 Slogf.e(TAG, e, "Remote exception on set resource overuse configuration");
1180 synchronized (mLock) {
1181 setPendingSetResourceOveruseConfigurationsRequestLocked(configs);
1182 }
1183 doClearPendingRequest = false;
1184 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1185 } finally {
1186 if (doClearPendingRequest) {
1187 synchronized (mLock) {
1188 mPendingSetResourceOveruseConfigurationsRequest = null;
1189 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001190 }
1191 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001192 if (DEBUG) {
1193 Slogf.d(TAG, "Set the resource overuse configuration successfully");
1194 }
1195 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1196 }
1197
1198 private boolean isConnectedToDaemon() {
1199 synchronized (mLock) {
1200 long startTimeMillis = SystemClock.uptimeMillis();
1201 long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1202 while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) {
1203 try {
1204 mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis);
1205 } catch (InterruptedException e) {
1206 Thread.currentThread().interrupt();
1207 continue;
1208 } finally {
1209 sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1210 }
1211 break;
1212 }
1213 return mIsConnectedToDaemon;
1214 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001215 }
1216
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +00001217 @GuardedBy("mLock")
1218 private boolean isSafeToKillLocked(String genericPackageName, int componentType,
1219 List<String> sharedPackages) {
1220 BiFunction<List<String>, Set<String>, Boolean> isSafeToKillAnyPackage =
1221 (packages, safeToKillPackages) -> {
1222 if (packages == null) {
1223 return false;
1224 }
1225 for (int i = 0; i < packages.size(); i++) {
1226 if (safeToKillPackages.contains(packages.get(i))) {
1227 return true;
1228 }
1229 }
1230 return false;
1231 };
1232
1233 switch (componentType) {
1234 case ComponentType.SYSTEM:
1235 if (mSafeToKillSystemPackages.contains(genericPackageName)) {
1236 return true;
1237 }
1238 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages);
1239 case ComponentType.VENDOR:
1240 if (mSafeToKillVendorPackages.contains(genericPackageName)) {
1241 return true;
1242 }
1243 /*
1244 * Packages under the vendor shared UID may contain system packages because when
1245 * CarWatchdogService derives the shared component type it attributes system
1246 * packages as vendor packages when there is at least one vendor package.
1247 */
1248 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages)
1249 || isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillVendorPackages);
1250 default:
1251 // Third-party apps are always killable
1252 return true;
1253 }
1254 }
1255
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001256 private static String getUserPackageUniqueId(int userId, String genericPackageName) {
1257 return String.valueOf(userId) + ":" + genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001258 }
1259
1260 @VisibleForTesting
1261 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001262 android.automotive.watchdog.IoOveruseStats internalStats,
1263 int totalTimesKilled, boolean isKillableOnOveruses) {
1264 return new IoOveruseStats.Builder(internalStats.startTime, internalStats.durationInSeconds)
1265 .setTotalOveruses(internalStats.totalOveruses)
1266 .setTotalTimesKilled(totalTimesKilled)
1267 .setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes))
1268 .setKillableOnOveruse(isKillableOnOveruses)
1269 .setRemainingWriteBytes(toPerStateBytes(internalStats.remainingWriteBytes));
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001270 }
1271
1272 private static PerStateBytes toPerStateBytes(
1273 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001274 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001275 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001276 }
1277
1278 private static long totalPerStateBytes(
1279 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
1280 BiFunction<Long, Long, Long> sum = (l, r) -> {
1281 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
1282 };
1283 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
1284 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
1285 }
1286
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001287 private static long getMinimumBytesWritten(
1288 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
1289 switch (minimumStatsIoFlag) {
1290 case 0:
1291 return 0;
1292 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
1293 return 1024 * 1024;
1294 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
1295 return 100 * 1024 * 1024;
1296 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
1297 return 1024 * 1024 * 1024;
1298 default:
1299 throw new IllegalArgumentException(
1300 "Must provide valid minimum stats flag for I/O resource");
1301 }
1302 }
1303
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001304 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
1305 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
1306 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1307 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
1308 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
1309 internalConfig.componentType = config.getComponentType();
1310 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
1311 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
1312 internalConfig.packageMetadata = new ArrayList<>();
1313 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
1314 if (entry.getKey().isEmpty()) {
1315 continue;
1316 }
1317 PackageMetadata metadata = new PackageMetadata();
1318 metadata.packageName = entry.getKey();
1319 switch(entry.getValue()) {
1320 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1321 metadata.appCategoryType = ApplicationCategoryType.MAPS;
1322 break;
1323 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1324 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
1325 break;
1326 default:
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001327 Slogf.i(TAG, "Invalid application category type: %s skipping package: %s",
1328 entry.getValue(), metadata.packageName);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001329 continue;
1330 }
1331 internalConfig.packageMetadata.add(metadata);
1332 }
1333 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
1334 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1335 && config.getIoOveruseConfiguration() != null) {
1336 internalConfig.resourceSpecificConfigurations.add(
1337 toResourceSpecificConfiguration(config.getComponentType(),
1338 config.getIoOveruseConfiguration()));
1339 }
1340 return internalConfig;
1341 }
1342
1343 private static ResourceSpecificConfiguration
1344 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
1345 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
1346 new android.automotive.watchdog.internal.IoOveruseConfiguration();
1347 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
1348 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
1349 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
1350 config.getPackageSpecificThresholds());
1351 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
1352 config.getAppCategorySpecificThresholds());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001353 for (int i = 0; i < internalConfig.categorySpecificThresholds.size(); ++i) {
1354 PerStateIoOveruseThreshold threshold = internalConfig.categorySpecificThresholds.get(i);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001355 switch(threshold.name) {
1356 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1357 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
1358 break;
1359 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1360 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
1361 break;
1362 default:
1363 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
1364 }
1365 }
1366 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
1367 config.getSystemWideThresholds());
1368
1369 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
1370 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
1371 return resourceSpecificConfig;
1372 }
1373
1374 @VisibleForTesting
1375 static String toComponentTypeStr(int componentType) {
1376 switch(componentType) {
1377 case ComponentType.SYSTEM:
1378 return "SYSTEM";
1379 case ComponentType.VENDOR:
1380 return "VENDOR";
1381 case ComponentType.THIRD_PARTY:
1382 return "THIRD_PARTY";
1383 default:
1384 return "UNKNOWN";
1385 }
1386 }
1387
1388 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
1389 Map<String, PerStateBytes> thresholds) {
1390 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
1391 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001392 if (!thresholds.isEmpty()) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001393 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
1394 entry.getValue()));
1395 }
1396 }
1397 return internalThresholds;
1398 }
1399
1400 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
1401 PerStateBytes perStateBytes) {
1402 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
1403 threshold.name = name;
1404 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
1405 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
1406 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
1407 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
1408 return threshold;
1409 }
1410
1411 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
1412 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
1413 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
1414 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001415 for (int i = 0; i < thresholds.size(); ++i) {
1416 if (thresholds.get(i).getDurationInSeconds() == 0
1417 || thresholds.get(i).getWrittenBytesPerSecond() == 0) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001418 continue;
1419 }
1420 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
1421 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001422 internalThreshold.durationInSeconds = thresholds.get(i).getDurationInSeconds();
1423 internalThreshold.writtenBytesPerSecond = thresholds.get(i).getWrittenBytesPerSecond();
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001424 internalThresholds.add(internalThreshold);
1425 }
1426 return internalThresholds;
1427 }
1428
1429 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
1430 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
1431 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001432 ArrayMap<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
1433 for (int i = 0; i < internalConfig.packageMetadata.size(); ++i) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001434 String categoryTypeStr;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001435 switch (internalConfig.packageMetadata.get(i).appCategoryType) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001436 case ApplicationCategoryType.MAPS:
1437 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
1438 break;
1439 case ApplicationCategoryType.MEDIA:
1440 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
1441 break;
1442 default:
1443 continue;
1444 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001445 packagesToAppCategoryTypes.put(
1446 internalConfig.packageMetadata.get(i).packageName, categoryTypeStr);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001447 }
1448 ResourceOveruseConfiguration.Builder configBuilder =
1449 new ResourceOveruseConfiguration.Builder(
1450 internalConfig.componentType,
1451 internalConfig.safeToKillPackages,
1452 internalConfig.vendorPackagePrefixes,
1453 packagesToAppCategoryTypes);
1454 for (ResourceSpecificConfiguration resourceSpecificConfig :
1455 internalConfig.resourceSpecificConfigurations) {
1456 if (resourceSpecificConfig.getTag()
1457 == ResourceSpecificConfiguration.ioOveruseConfiguration
1458 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
1459 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
1460 resourceSpecificConfig.getIoOveruseConfiguration()));
1461 }
1462 }
1463 return configBuilder.build();
1464 }
1465
1466 private static IoOveruseConfiguration toIoOveruseConfiguration(
1467 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001468 TriConsumer<Map<String, PerStateBytes>, String, String> replaceKey =
1469 (map, oldKey, newKey) -> {
1470 PerStateBytes perStateBytes = map.get(oldKey);
1471 if (perStateBytes != null) {
1472 map.put(newKey, perStateBytes);
1473 map.remove(oldKey);
1474 }
1475 };
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001476 PerStateBytes componentLevelThresholds =
1477 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001478 ArrayMap<String, PerStateBytes> packageSpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001479 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001480 ArrayMap<String, PerStateBytes> appCategorySpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001481 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001482 replaceKey.accept(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001483 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001484 replaceKey.accept(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001485 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
1486 List<IoOveruseAlertThreshold> systemWideThresholds =
1487 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
1488
1489 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1490 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1491 systemWideThresholds);
1492 return configBuilder.build();
1493 }
1494
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001495 private static ArrayMap<String, PerStateBytes> toPerStateBytesMap(
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001496 List<PerStateIoOveruseThreshold> thresholds) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001497 ArrayMap<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1498 for (int i = 0; i < thresholds.size(); ++i) {
1499 thresholdsMap.put(
1500 thresholds.get(i).name, toPerStateBytes(thresholds.get(i).perStateWriteBytes));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001501 }
1502 return thresholdsMap;
1503 }
1504
1505 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1506 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1507 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001508 for (int i = 0; i < internalThresholds.size(); ++i) {
1509 thresholds.add(new IoOveruseAlertThreshold(internalThresholds.get(i).durationInSeconds,
1510 internalThresholds.get(i).writtenBytesPerSecond));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001511 }
1512 return thresholds;
1513 }
1514
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001515 private static void checkResourceOveruseConfigs(
1516 List<ResourceOveruseConfiguration> configurations,
1517 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1518 ArraySet<Integer> seenComponentTypes = new ArraySet<>();
1519 for (int i = 0; i < configurations.size(); ++i) {
1520 ResourceOveruseConfiguration config = configurations.get(i);
1521 if (seenComponentTypes.contains(config.getComponentType())) {
1522 throw new IllegalArgumentException(
1523 "Cannot provide duplicate configurations for the same component type");
1524 }
1525 checkResourceOveruseConfig(config, resourceOveruseFlag);
1526 seenComponentTypes.add(config.getComponentType());
1527 }
1528 }
1529
1530 private static void checkResourceOveruseConfig(ResourceOveruseConfiguration config,
1531 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1532 int componentType = config.getComponentType();
1533 if (toComponentTypeStr(componentType).equals("UNKNOWN")) {
1534 throw new IllegalArgumentException(
1535 "Invalid component type in the configuration: " + componentType);
1536 }
1537 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1538 && config.getIoOveruseConfiguration() == null) {
1539 throw new IllegalArgumentException("Must provide I/O overuse configuration");
1540 }
1541 checkIoOveruseConfig(config.getIoOveruseConfiguration(), componentType);
1542 }
1543
1544 private static void checkIoOveruseConfig(IoOveruseConfiguration config, int componentType) {
1545 if (config.getComponentLevelThresholds().getBackgroundModeBytes() <= 0
1546 || config.getComponentLevelThresholds().getForegroundModeBytes() <= 0
1547 || config.getComponentLevelThresholds().getGarageModeBytes() <= 0) {
1548 throw new IllegalArgumentException(
1549 "For component: " + toComponentTypeStr(componentType)
1550 + " some thresholds are zero for: "
1551 + config.getComponentLevelThresholds().toString());
1552 }
1553 if (componentType == ComponentType.SYSTEM) {
1554 List<IoOveruseAlertThreshold> systemThresholds = config.getSystemWideThresholds();
1555 if (systemThresholds.isEmpty()) {
1556 throw new IllegalArgumentException(
1557 "Empty system-wide alert thresholds provided in "
1558 + toComponentTypeStr(componentType)
1559 + " config.");
1560 }
1561 for (int i = 0; i < systemThresholds.size(); i++) {
1562 checkIoOveruseAlertThreshold(systemThresholds.get(i));
1563 }
1564 }
1565 }
1566
1567 private static void checkIoOveruseAlertThreshold(
1568 IoOveruseAlertThreshold ioOveruseAlertThreshold) {
1569 if (ioOveruseAlertThreshold.getDurationInSeconds() <= 0) {
1570 throw new IllegalArgumentException(
1571 "System wide threshold duration must be greater than zero for: "
1572 + ioOveruseAlertThreshold);
1573 }
1574 if (ioOveruseAlertThreshold.getWrittenBytesPerSecond() <= 0) {
1575 throw new IllegalArgumentException(
1576 "System wide threshold written bytes per second must be greater than zero for: "
1577 + ioOveruseAlertThreshold);
1578 }
1579 }
1580
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001581 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1582 PerStateBytes perStateBytes = map.get(oldKey);
1583 if (perStateBytes != null) {
1584 map.put(newKey, perStateBytes);
1585 map.remove(oldKey);
1586 }
1587 }
1588
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001589 private static int toNumDays(@CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
1590 switch(maxStatsPeriod) {
1591 case STATS_PERIOD_CURRENT_DAY:
1592 return 0;
1593 case STATS_PERIOD_PAST_3_DAYS:
1594 return 3;
1595 case STATS_PERIOD_PAST_7_DAYS:
1596 return 7;
1597 case STATS_PERIOD_PAST_15_DAYS:
1598 return 15;
1599 case STATS_PERIOD_PAST_30_DAYS:
1600 return 30;
1601 default:
1602 throw new IllegalArgumentException(
1603 "Invalid max stats period provided: " + maxStatsPeriod);
1604 }
1605 }
1606
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001607 private final class PackageResourceUsage {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001608 public final String genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001609 public @UserIdInt final int userId;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001610 @GuardedBy("mLock")
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001611 public final PackageIoUsage ioUsage = new PackageIoUsage();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001612 @GuardedBy("mLock")
1613 public final ArrayMap<String, Integer> oldEnabledStateByPackage = new ArrayMap<>();
1614 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001615 private @KillableState int mKillableState;
1616
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001617 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001618 PackageResourceUsage(@UserIdInt int userId, String genericPackageName) {
1619 this.genericPackageName = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001620 this.userId = userId;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001621 this.mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001622 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001623 }
1624
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001625 public boolean isSharedPackage() {
1626 return this.genericPackageName.startsWith(SHARED_PACKAGE_PREFIX);
1627 }
1628
1629 @GuardedBy("mLock")
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001630 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001631 if (!internalStats.killableOnOveruse) {
1632 /*
1633 * Killable value specified in the internal stats is provided by the native daemon.
1634 * This value reflects whether or not an application is safe-to-kill on overuse.
1635 * This setting is from the I/O overuse configuration specified by the system and
1636 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1637 * specify the application is not killable, the application is not safe-to-kill.
1638 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001639 mKillableState = KILLABLE_STATE_NEVER;
1640 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1641 /*
1642 * This case happens when a previously unsafe to kill system/vendor package was
1643 * recently marked as safe-to-kill so update the old state to the default value.
1644 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001645 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001646 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001647 }
1648 ioUsage.update(internalStats);
1649 }
1650
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001651 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001652 return new ResourceOveruseStats.Builder(genericPackageName, UserHandle.of(userId));
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001653 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001654
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001655 @GuardedBy("mLock")
1656 public IoOveruseStats getIoOveruseStatsLocked() {
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001657 if (!ioUsage.hasUsage()) {
1658 return null;
1659 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001660 return ioUsage.getIoOveruseStats(mKillableState != KILLABLE_STATE_NEVER);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001661 }
1662
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001663 @GuardedBy("mLock")
1664 public @KillableState int getKillableStateLocked() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001665 return mKillableState;
1666 }
1667
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001668 @GuardedBy("mLock")
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001669 public void setKillableStateLocked(@KillableState int killableState) {
1670 mKillableState = killableState;
1671 }
1672
1673 @GuardedBy("mLock")
1674 public boolean verifyAndSetKillableStateLocked(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001675 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001676 return false;
1677 }
1678 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1679 return true;
1680 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001681
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001682 @GuardedBy("mLock")
1683 public int syncAndFetchKillableStateLocked(int myComponentType, boolean isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001684 /*
1685 * The killable state goes out-of-sync:
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001686 * 1. When the on-device safe-to-kill list was recently updated and the user package
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001687 * didn't have any resource usage so the native daemon didn't update the killable state.
1688 * 2. When a package has no resource usage and is initialized outside of processing the
1689 * latest resource usage stats.
1690 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001691 if (myComponentType != ComponentType.THIRD_PARTY && !isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001692 mKillableState = KILLABLE_STATE_NEVER;
1693 } else if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001694 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001695 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1696 }
1697 return mKillableState;
1698 }
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001699
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001700 @GuardedBy("mLock")
1701 public void resetStatsLocked() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001702 oldEnabledStateByPackage.clear();
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001703 ioUsage.resetStats();
1704 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001705 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001706 /** Defines I/O usage fields for a package. */
1707 public static final class PackageIoUsage {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001708 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1709 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001710 private int mTotalTimesKilled;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001711
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001712 private PackageIoUsage() {
1713 mForgivenWriteBytes = new android.automotive.watchdog.PerStateBytes();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001714 mTotalTimesKilled = 0;
1715 }
1716
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001717 public PackageIoUsage(android.automotive.watchdog.IoOveruseStats ioOveruseStats,
1718 android.automotive.watchdog.PerStateBytes forgivenWriteBytes,
1719 int totalTimesKilled) {
1720 mIoOveruseStats = ioOveruseStats;
1721 mForgivenWriteBytes = forgivenWriteBytes;
1722 mTotalTimesKilled = totalTimesKilled;
1723 }
1724
1725 public android.automotive.watchdog.IoOveruseStats getInternalIoOveruseStats() {
1726 return mIoOveruseStats;
1727 }
1728
1729 public android.automotive.watchdog.PerStateBytes getForgivenWriteBytes() {
1730 return mForgivenWriteBytes;
1731 }
1732
1733 public int getTotalTimesKilled() {
1734 return mTotalTimesKilled;
1735 }
1736
1737 boolean hasUsage() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001738 return mIoOveruseStats != null;
1739 }
1740
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001741 void overwrite(PackageIoUsage ioUsage) {
1742 mIoOveruseStats = ioUsage.mIoOveruseStats;
1743 mForgivenWriteBytes = ioUsage.mForgivenWriteBytes;
1744 mTotalTimesKilled = ioUsage.mTotalTimesKilled;
1745 }
1746
1747 void update(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001748 mIoOveruseStats = internalStats;
1749 if (exceedsThreshold()) {
1750 /*
1751 * Forgive written bytes on overuse as the package is either forgiven or killed on
1752 * overuse. When the package is killed, the user may opt to open the corresponding
1753 * app and the package should be forgiven anyways.
1754 * NOTE: If this logic is updated, update the daemon side logic as well.
1755 */
1756 mForgivenWriteBytes = internalStats.writtenBytes;
1757 }
1758 }
1759
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001760 IoOveruseStats getIoOveruseStats(boolean isKillable) {
1761 return toIoOveruseStatsBuilder(mIoOveruseStats, mTotalTimesKilled, isKillable).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001762 }
1763
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001764 boolean exceedsThreshold() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001765 if (!hasUsage()) {
1766 return false;
1767 }
1768 android.automotive.watchdog.PerStateBytes remaining =
1769 mIoOveruseStats.remainingWriteBytes;
1770 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1771 || remaining.garageModeBytes == 0;
1772 }
1773
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001774 void killed() {
1775 ++mTotalTimesKilled;
1776 }
1777
1778 void resetStats() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001779 mIoOveruseStats = null;
1780 mForgivenWriteBytes = null;
1781 mTotalTimesKilled = 0;
1782 }
1783 }
1784
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001785 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1786 public final IResourceOveruseListener listener;
1787 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1788 public final int pid;
1789 public final int uid;
1790 public final boolean isListenerForSystem;
1791
1792 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1793 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1794 boolean isListenerForSystem) {
1795 this.listener = listener;
1796 this.flag = flag;
1797 this.pid = pid;
1798 this.uid = uid;
1799 this.isListenerForSystem = isListenerForSystem;
1800 }
1801
1802 @Override
1803 public void binderDied() {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001804 Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001805 isListenerForSystem ? " for system" : "", pid);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001806 Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo =
1807 listenerInfosByUid -> {
1808 ArrayList<ResourceOveruseListenerInfo> listenerInfos =
1809 listenerInfosByUid.get(uid);
1810 if (listenerInfos == null) {
1811 return;
1812 }
1813 listenerInfos.remove(this);
1814 if (listenerInfos.isEmpty()) {
1815 listenerInfosByUid.remove(uid);
1816 }
1817 };
1818 if (isListenerForSystem) {
1819 removeListenerInfo.accept(mOveruseSystemListenerInfosByUid);
1820 } else {
1821 removeListenerInfo.accept(mOveruseListenerInfosByUid);
1822 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001823 unlinkToDeath();
1824 }
1825
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001826 public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001827 int overusingUid, String overusingGenericPackageName,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001828 ResourceOveruseStats resourceOveruseStats) {
1829 if ((flag & resourceType) == 0) {
1830 return;
1831 }
1832 try {
1833 listener.onOveruse(resourceOveruseStats);
1834 } catch (RemoteException e) {
1835 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001836 + "package(uid %d, generic package name '%s'): %s",
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001837 (isListenerForSystem ? "system listener" : "listener"), uid, pid,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001838 overusingUid, overusingGenericPackageName, e);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001839 }
1840 }
1841
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001842 private void linkToDeath() throws RemoteException {
1843 listener.asBinder().linkToDeath(this, 0);
1844 }
1845
1846 private void unlinkToDeath() {
1847 listener.asBinder().unlinkToDeath(this, 0);
1848 }
1849 }
1850}