blob: 8ed2301939468a6f31c85ba916881442c36d3d9f [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 Annadoraicf5f3a92021-04-02 15:26:16 -070024import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NEVER;
25import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NO;
26import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_YES;
27import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
28import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
29import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
30
31import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070032
33import android.annotation.NonNull;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070034import android.annotation.UserIdInt;
35import android.app.ActivityThread;
36import android.automotive.watchdog.ResourceType;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070037import android.automotive.watchdog.internal.ApplicationCategoryType;
38import android.automotive.watchdog.internal.ComponentType;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070039import android.automotive.watchdog.internal.PackageIdentifier;
40import android.automotive.watchdog.internal.PackageIoOveruseStats;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070041import android.automotive.watchdog.internal.PackageMetadata;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070042import android.automotive.watchdog.internal.PackageResourceOveruseAction;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070043import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
44import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070045import android.car.watchdog.CarWatchdogManager;
46import android.car.watchdog.IResourceOveruseListener;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070047import android.car.watchdog.IoOveruseAlertThreshold;
48import android.car.watchdog.IoOveruseConfiguration;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070049import android.car.watchdog.IoOveruseStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070050import android.car.watchdog.PackageKillableState;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070051import android.car.watchdog.PackageKillableState.KillableState;
52import android.car.watchdog.PerStateBytes;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070053import android.car.watchdog.ResourceOveruseConfiguration;
54import android.car.watchdog.ResourceOveruseStats;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070055import android.car.watchdoglib.CarWatchdogDaemonHelper;
56import android.content.Context;
57import android.content.pm.IPackageManager;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070058import android.content.pm.PackageInfo;
59import android.content.pm.PackageManager;
60import android.content.pm.UserInfo;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070061import android.os.Binder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070062import android.os.Handler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070063import android.os.IBinder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070064import android.os.Looper;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070065import android.os.RemoteException;
66import android.os.UserHandle;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070067import android.os.UserManager;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070068import android.util.ArrayMap;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070069import android.util.ArraySet;
70import android.util.IndentingPrintWriter;
71import android.util.SparseArray;
72
Lakshman Annadorai016127e2021-03-18 09:11:43 -070073import com.android.internal.annotations.GuardedBy;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070074import com.android.internal.annotations.VisibleForTesting;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070075import com.android.internal.util.Preconditions;
76import com.android.server.utils.Slogf;
77
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070078import java.time.ZoneOffset;
79import java.time.ZonedDateTime;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070080import java.util.ArrayList;
81import java.util.List;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070082import java.util.Map;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070083import java.util.Objects;
84import java.util.Set;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070085import java.util.function.BiFunction;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070086
87/**
88 * Handles system resource performance monitoring module.
89 */
90public final class WatchdogPerfHandler {
Lakshman Annadoraie1720472021-04-13 15:22:57 -070091 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS = "MAPS";
92 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA";
93 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN";
94
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070095 private final Context mContext;
96 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
97 private final PackageInfoHandler mPackageInfoHandler;
98 private final Handler mMainHandler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070099 private final Object mLock = new Object();
100 /*
101 * Cache of added resource overuse listeners by uid.
102 */
103 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700104 private final Map<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>();
105 @GuardedBy("mLock")
106 private final List<PackageResourceOveruseAction> mOveruseActionsByUserPackage =
107 new ArrayList<>();
108 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700109 private final SparseArray<ResourceOveruseListenerInfo> mOveruseListenerInfosByUid =
110 new SparseArray<>();
111 @GuardedBy("mLock")
112 private final SparseArray<ResourceOveruseListenerInfo> mOveruseSystemListenerInfosByUid =
113 new SparseArray<>();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700114 @GuardedBy("mLock")
115 private ZonedDateTime mLastStatsReportUTC;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700116 /* Set of safe-to-kill system and vendor packages. */
117 @GuardedBy("mLock")
118 public final Set<String> mSafeToKillPackages = new ArraySet<>();
119 /* Default killable state for packages when not updated by the user. */
120 @GuardedBy("mLock")
121 public final Set<String> mDefaultNotKillablePackages = new ArraySet<>();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700122
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700123 public WatchdogPerfHandler(Context context, CarWatchdogDaemonHelper daemonHelper,
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700124 PackageInfoHandler packageInfoHandler) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700125 mContext = context;
126 mCarWatchdogDaemonHelper = daemonHelper;
127 mPackageInfoHandler = packageInfoHandler;
128 mMainHandler = new Handler(Looper.getMainLooper());
129 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700130 }
131
132 /** Initializes the handler. */
133 public void init() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700134 /*
135 * TODO(b/183947162): Opt-in to receive package change broadcast and handle package enabled
136 * state changes.
137 *
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700138 * TODO(b/185287136): Persist in-memory data:
139 * 1. Read the current day's I/O overuse stats from database and push them
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700140 * to the daemon.
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700141 * 2. Fetch the safe-to-kill from daemon on initialization and update mSafeToKillPackages.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700142 */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700143 synchronized (mLock) {
144 checkAndHandleDateChangeLocked();
145 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700146 if (CarWatchdogService.DEBUG) {
147 Slogf.d(CarWatchdogService.TAG, "WatchdogPerfHandler is initialized");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700148 }
149 }
150
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700151 /** Releases the handler */
152 public void release() {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700153 /* TODO(b/185287136): Write daily usage to SQLite DB storage. */
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700154 if (CarWatchdogService.DEBUG) {
155 Slogf.d(CarWatchdogService.TAG, "WatchdogPerfHandler is released");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700156 }
157 }
158
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700159 /** Dumps its state. */
160 public void dump(IndentingPrintWriter writer) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700161 /*
162 * TODO(b/183436216): Implement this method.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700163 */
164 }
165
166 /** Returns resource overuse stats for the calling package. */
167 @NonNull
168 public ResourceOveruseStats getResourceOveruseStats(
169 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
170 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
171 Preconditions.checkArgument((resourceOveruseFlag > 0),
172 "Must provide valid resource overuse flag");
173 Preconditions.checkArgument((maxStatsPeriod > 0),
174 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700175 // When more resource stats are added, make this as optional.
176 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
177 "Must provide resource I/O overuse flag");
178 int callingUid = Binder.getCallingUid();
179 int callingUserId = UserHandle.getUserId(callingUid);
180 UserHandle callingUserHandle = UserHandle.of(callingUserId);
181 String callingPackageName =
182 mPackageInfoHandler.getPackageNamesForUids(new int[]{callingUid})
183 .get(callingUid, null);
184 if (callingPackageName == null) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700185 Slogf.w(CarWatchdogService.TAG, "Failed to fetch package info for uid %d", callingUid);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700186 return new ResourceOveruseStats.Builder("", callingUserHandle).build();
187 }
188 ResourceOveruseStats.Builder statsBuilder =
189 new ResourceOveruseStats.Builder(callingPackageName, callingUserHandle);
190 statsBuilder.setIoOveruseStats(getIoOveruseStats(callingUserId, callingPackageName,
191 /* minimumBytesWritten= */ 0, maxStatsPeriod));
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700192 if (CarWatchdogService.DEBUG) {
193 Slogf.d(CarWatchdogService.TAG, "Returning all resource overuse stats for calling uid "
194 + "%d [user %d and package '%s']", callingUid, callingUserId,
195 callingPackageName);
196 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700197 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700198 }
199
200 /** Returns resource overuse stats for all packages. */
201 @NonNull
202 public List<ResourceOveruseStats> getAllResourceOveruseStats(
203 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
204 @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag,
205 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
206 Preconditions.checkArgument((resourceOveruseFlag > 0),
207 "Must provide valid resource overuse flag");
208 Preconditions.checkArgument((maxStatsPeriod > 0),
209 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700210 // When more resource types are added, make this as optional.
211 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
212 "Must provide resource I/O overuse flag");
213 long minimumBytesWritten = getMinimumBytesWritten(minimumStatsFlag);
214 List<ResourceOveruseStats> allStats = new ArrayList<>();
215 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
216 ResourceOveruseStats.Builder statsBuilder = usage.getResourceOveruseStatsBuilder();
217 IoOveruseStats ioOveruseStats = getIoOveruseStats(usage.userId, usage.packageName,
218 minimumBytesWritten, maxStatsPeriod);
219 if (ioOveruseStats == null) {
220 continue;
221 }
222 allStats.add(statsBuilder.setIoOveruseStats(ioOveruseStats).build());
223 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700224 if (CarWatchdogService.DEBUG) {
225 Slogf.d(CarWatchdogService.TAG, "Returning all resource overuse stats");
226 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700227 return allStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700228 }
229
230 /** Returns resource overuse stats for the specified user package. */
231 @NonNull
232 public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
233 @NonNull String packageName, @NonNull UserHandle userHandle,
234 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
235 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
236 Objects.requireNonNull(packageName, "Package name must be non-null");
237 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700238 Preconditions.checkArgument((userHandle != UserHandle.ALL),
239 "Must provide the user handle for a specific user");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700240 Preconditions.checkArgument((resourceOveruseFlag > 0),
241 "Must provide valid resource overuse flag");
242 Preconditions.checkArgument((maxStatsPeriod > 0),
243 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700244 // When more resource types are added, make this as optional.
245 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
246 "Must provide resource I/O overuse flag");
247 ResourceOveruseStats.Builder statsBuilder =
248 new ResourceOveruseStats.Builder(packageName, userHandle);
249 statsBuilder.setIoOveruseStats(getIoOveruseStats(userHandle.getIdentifier(), packageName,
250 /* minimumBytesWritten= */ 0, maxStatsPeriod));
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700251 if (CarWatchdogService.DEBUG) {
252 Slogf.d(CarWatchdogService.TAG,
253 "Returning resource overuse stats for user %d, package '%s'",
254 userHandle.getIdentifier(), packageName);
255 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700256 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700257 }
258
259 /** Adds the resource overuse listener. */
260 public void addResourceOveruseListener(
261 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
262 @NonNull IResourceOveruseListener listener) {
263 Objects.requireNonNull(listener, "Listener must be non-null");
264 Preconditions.checkArgument((resourceOveruseFlag > 0),
265 "Must provide valid resource overuse flag");
266 synchronized (mLock) {
267 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
268 mOveruseListenerInfosByUid);
269 }
270 }
271
272 /** Removes the previously added resource overuse listener. */
273 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
274 Objects.requireNonNull(listener, "Listener must be non-null");
275 synchronized (mLock) {
276 removeResourceOveruseListenerLocked(listener, mOveruseListenerInfosByUid);
277 }
278 }
279
280 /** Adds the resource overuse system listener. */
281 public void addResourceOveruseListenerForSystem(
282 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
283 @NonNull IResourceOveruseListener listener) {
284 Objects.requireNonNull(listener, "Listener must be non-null");
285 Preconditions.checkArgument((resourceOveruseFlag > 0),
286 "Must provide valid resource overuse flag");
287 synchronized (mLock) {
288 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
289 mOveruseSystemListenerInfosByUid);
290 }
291 }
292
293 /** Removes the previously added resource overuse system listener. */
294 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
295 Objects.requireNonNull(listener, "Listener must be non-null");
296 synchronized (mLock) {
297 removeResourceOveruseListenerLocked(listener, mOveruseSystemListenerInfosByUid);
298 }
299 }
300
301 /** Sets whether or not a package is killable on resource overuse. */
302 public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
303 boolean isKillable) {
304 Objects.requireNonNull(packageName, "Package name must be non-null");
305 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700306 if (userHandle == UserHandle.ALL) {
307 synchronized (mLock) {
308 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
309 if (!usage.packageName.equals(packageName)) {
310 continue;
311 }
312 if (!usage.setKillableState(isKillable)) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700313 Slogf.e(CarWatchdogService.TAG,
314 "Cannot set killable state for package '%s'", packageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700315 throw new IllegalArgumentException(
316 "Package killable state is not updatable");
317 }
318 }
319 if (!isKillable) {
320 mDefaultNotKillablePackages.add(packageName);
321 } else {
322 mDefaultNotKillablePackages.remove(packageName);
323 }
324 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700325 if (CarWatchdogService.DEBUG) {
326 Slogf.d(CarWatchdogService.TAG,
327 "Successfully set killable package state for all users");
328 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700329 return;
330 }
331 int userId = userHandle.getIdentifier();
332 String key = getUserPackageUniqueId(userId, packageName);
333 synchronized (mLock) {
334 /*
335 * When the queried package is not cached in {@link mUsageByUserPackage}, the set API
336 * will update the killable state even when the package should never be killed.
337 * But the get API will return the correct killable state. This behavior is tolerable
338 * because in production the set API should be called only after the get API.
339 * For instance, when this case happens by mistake and the package overuses resource
340 * between the set and the get API calls, the daemon will provide correct killable
341 * state when pushing the latest stats. Ergo, the invalid killable state doesn't have
342 * any effect.
343 */
344 PackageResourceUsage usage = mUsageByUserPackage.getOrDefault(key,
345 new PackageResourceUsage(userId, packageName));
346 if (!usage.setKillableState(isKillable)) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700347 Slogf.e(CarWatchdogService.TAG,
348 "User %d cannot set killable state for package '%s'",
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700349 userHandle.getIdentifier(), packageName);
350 throw new IllegalArgumentException("Package killable state is not updatable");
351 }
352 mUsageByUserPackage.put(key, usage);
353 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700354 if (CarWatchdogService.DEBUG) {
355 Slogf.d(CarWatchdogService.TAG, "Successfully set killable package state for user %d",
356 userId);
357 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700358 }
359
360 /** Returns the list of package killable states on resource overuse for the user. */
361 @NonNull
362 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
363 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700364 PackageManager pm = mContext.getPackageManager();
365 if (userHandle != UserHandle.ALL) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700366 if (CarWatchdogService.DEBUG) {
367 Slogf.d(CarWatchdogService.TAG, "Returning all package killable states for user %d",
368 userHandle.getIdentifier());
369 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700370 return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
371 }
372 List<PackageKillableState> packageKillableStates = new ArrayList<>();
373 UserManager userManager = UserManager.get(mContext);
374 List<UserInfo> userInfos = userManager.getAliveUsers();
375 for (UserInfo userInfo : userInfos) {
376 packageKillableStates.addAll(getPackageKillableStatesForUserId(userInfo.id, pm));
377 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700378 if (CarWatchdogService.DEBUG) {
379 Slogf.d(CarWatchdogService.TAG, "Returning all package killable states for all users");
380 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700381 return packageKillableStates;
382 }
383
384 private List<PackageKillableState> getPackageKillableStatesForUserId(int userId,
385 PackageManager pm) {
386 List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */0, userId);
387 List<PackageKillableState> states = new ArrayList<>();
388 synchronized (mLock) {
389 for (int i = 0; i < packageInfos.size(); ++i) {
390 PackageInfo packageInfo = packageInfos.get(i);
391 String key = getUserPackageUniqueId(userId, packageInfo.packageName);
392 PackageResourceUsage usage = mUsageByUserPackage.getOrDefault(key,
393 new PackageResourceUsage(userId, packageInfo.packageName));
394 int killableState = usage.syncAndFetchKillableStateLocked(
395 mPackageInfoHandler.getComponentType(packageInfo.packageName,
396 packageInfo.applicationInfo));
397 mUsageByUserPackage.put(key, usage);
398 states.add(
399 new PackageKillableState(packageInfo.packageName, userId, killableState));
400 }
401 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700402 if (CarWatchdogService.DEBUG) {
403 Slogf.d(CarWatchdogService.TAG,
404 "Returning the package killable states for a user package");
405 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700406 return states;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700407 }
408
409 /** Sets the given resource overuse configurations. */
410 public void setResourceOveruseConfigurations(
411 List<ResourceOveruseConfiguration> configurations,
412 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
413 Objects.requireNonNull(configurations, "Configurations must be non-null");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700414 Preconditions.checkArgument((configurations.size() > 0),
415 "Must provide at least one configuration");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700416 Preconditions.checkArgument((resourceOveruseFlag > 0),
417 "Must provide valid resource overuse flag");
418 Set<Integer> seenComponentTypes = new ArraySet<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700419 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
420 new ArrayList<>();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700421 for (ResourceOveruseConfiguration config : configurations) {
422 int componentType = config.getComponentType();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700423 if (toComponentTypeStr(componentType).equals("UNKNOWN")) {
424 throw new IllegalArgumentException("Invalid component type in the configuration");
425 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700426 if (seenComponentTypes.contains(componentType)) {
427 throw new IllegalArgumentException(
428 "Cannot provide duplicate configurations for the same component type");
429 }
430 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
431 && config.getIoOveruseConfiguration() == null) {
432 throw new IllegalArgumentException("Must provide I/O overuse configuration");
433 }
434 seenComponentTypes.add(config.getComponentType());
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700435 internalConfigs.add(toInternalResourceOveruseConfiguration(config,
436 resourceOveruseFlag));
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700437 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700438
439 // TODO(b/186119640): Add retry logic when daemon is not available.
440 try {
441 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(internalConfigs);
442 } catch (IllegalArgumentException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700443 Slogf.w(CarWatchdogService.TAG, "Failed to set resource overuse configurations: %s", e);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700444 throw e;
445 } catch (RemoteException | RuntimeException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700446 Slogf.w(CarWatchdogService.TAG, "Failed to set resource overuse configurations: %s", e);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700447 throw new IllegalStateException(e);
448 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700449 /* TODO(b/185287136): Fetch safe-to-kill list from daemon and update mSafeToKillPackages. */
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700450 if (CarWatchdogService.DEBUG) {
451 Slogf.d(CarWatchdogService.TAG, "Set the resource overuse configuration successfully");
452 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700453 }
454
455 /** Returns the available resource overuse configurations. */
456 @NonNull
457 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
458 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
459 Preconditions.checkArgument((resourceOveruseFlag > 0),
460 "Must provide valid resource overuse flag");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700461 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
462 new ArrayList<>();
463 // TODO(b/186119640): Add retry logic when daemon is not available.
464 try {
465 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
466 } catch (RemoteException | RuntimeException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700467 Slogf.w(CarWatchdogService.TAG, "Failed to fetch resource overuse configurations: %s",
468 e);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700469 throw new IllegalStateException(e);
470 }
471 List<ResourceOveruseConfiguration> configs = new ArrayList<>();
472 for (android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig
473 : internalConfigs) {
474 configs.add(toResourceOveruseConfiguration(internalConfig, resourceOveruseFlag));
475 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700476 if (CarWatchdogService.DEBUG) {
477 Slogf.d(CarWatchdogService.TAG, "Returning the resource overuse configuration");
478 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700479 return configs;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700480 }
481
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700482 /** Processes the latest I/O overuse stats */
483 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
484 int[] uids = new int[packageIoOveruseStats.size()];
485 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
486 uids[i] = packageIoOveruseStats.get(i).uid;
487 }
488 SparseArray<String> packageNamesByUid = mPackageInfoHandler.getPackageNamesForUids(uids);
489 synchronized (mLock) {
490 checkAndHandleDateChangeLocked();
491 for (PackageIoOveruseStats stats : packageIoOveruseStats) {
492 String packageName = packageNamesByUid.get(stats.uid, null);
493 if (packageName == null) {
494 continue;
495 }
496 int userId = UserHandle.getUserId(stats.uid);
497 PackageResourceUsage usage = cacheAndFetchUsageLocked(userId, packageName,
498 stats.ioOveruseStats);
499 if (stats.shouldNotify) {
500 /*
501 * Packages that exceed the warn threshold percentage should be notified as well
502 * and only the daemon is aware of such packages. Thus the flag is used to
503 * indicate which packages should be notified.
504 */
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700505 ResourceOveruseStats resourceOveruseStats =
506 usage.getResourceOveruseStatsBuilder().setIoOveruseStats(
507 usage.getIoOveruseStats()).build();
508 notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700509 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700510
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700511 if (!usage.ioUsage.exceedsThreshold()) {
512 continue;
513 }
514 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
515 overuseAction.packageIdentifier = new PackageIdentifier();
516 overuseAction.packageIdentifier.name = packageName;
517 overuseAction.packageIdentifier.uid = stats.uid;
518 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
519 overuseAction.resourceOveruseActionType = NOT_KILLED;
520 /*
521 * No action required on I/O overuse on one of the following cases:
522 * #1 The package is not safe to kill as it is critical for system stability.
523 * #2 The package has no recurring overuse behavior and the user opted to not
524 * kill the package so honor the user's decision.
525 */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700526 int killableState = usage.getKillableState();
527 if (killableState == KILLABLE_STATE_NEVER) {
528 mOveruseActionsByUserPackage.add(overuseAction);
529 continue;
530 }
531 boolean hasRecurringOveruse = isRecurringOveruseLocked(usage);
532 if (!hasRecurringOveruse && killableState == KILLABLE_STATE_NO) {
533 overuseAction.resourceOveruseActionType = NOT_KILLED_USER_OPTED;
534 mOveruseActionsByUserPackage.add(overuseAction);
535 continue;
536 }
537 try {
538 int oldEnabledState = -1;
539 IPackageManager packageManager = ActivityThread.getPackageManager();
540 if (!hasRecurringOveruse) {
541 oldEnabledState = packageManager.getApplicationEnabledSetting(packageName,
542 userId);
543
544 if (oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED
545 || oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED_USER
546 || oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
547 mOveruseActionsByUserPackage.add(overuseAction);
548 continue;
549 }
550 }
551
552 packageManager.setApplicationEnabledSetting(packageName,
553 COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId,
554 mContext.getPackageName());
555
556 overuseAction.resourceOveruseActionType = hasRecurringOveruse
557 ? KILLED_RECURRING_OVERUSE : KILLED;
558 if (!hasRecurringOveruse) {
559 usage.oldEnabledState = oldEnabledState;
560 }
561 } catch (RemoteException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700562 Slogf.e(CarWatchdogService.TAG, "Failed to disable application enabled setting "
563 + "for user %d, package '%s'", userId, packageName);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700564 }
565 mOveruseActionsByUserPackage.add(overuseAction);
566 }
567 if (!mOveruseActionsByUserPackage.isEmpty()) {
568 mMainHandler.sendMessage(obtainMessage(
569 WatchdogPerfHandler::notifyActionsTakenOnOveruse, this));
570 }
571 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700572 if (CarWatchdogService.DEBUG) {
573 Slogf.d(CarWatchdogService.TAG, "Processed latest I/O overuse stats");
574 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700575 }
576
577 /** Notify daemon about the actions take on resource overuse */
578 public void notifyActionsTakenOnOveruse() {
579 List<PackageResourceOveruseAction> actions;
580 synchronized (mLock) {
581 if (mOveruseActionsByUserPackage.isEmpty()) {
582 return;
583 }
584 actions = new ArrayList<>(mOveruseActionsByUserPackage);
585 mOveruseActionsByUserPackage.clear();
586 }
587 try {
588 mCarWatchdogDaemonHelper.actionTakenOnResourceOveruse(actions);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700589 } catch (RemoteException | RuntimeException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700590 Slogf.w(CarWatchdogService.TAG, "Failed to notify car watchdog daemon of actions taken "
591 + "on resource overuse: %s", e);
592 }
593 if (CarWatchdogService.DEBUG) {
594 Slogf.d(CarWatchdogService.TAG,
595 "Notified car watchdog daemon of actions taken on resource overuse");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700596 }
597 }
598
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700599 /** Resets the resource overuse stats for the given package. */
600 public void resetResourceOveruseStats(Set<String> packageNames) {
601 synchronized (mLock) {
602 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
603 if (packageNames.contains(usage.packageName)) {
604 usage.resetStats();
605 /*
606 * TODO(b/185287136): When the stats are persisted in local DB, reset the stats
607 * for this package from local DB.
608 */
609 }
610 }
611 }
612 }
613
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700614 private void notifyResourceOveruseStatsLocked(int uid,
615 ResourceOveruseStats resourceOveruseStats) {
616 String packageName = resourceOveruseStats.getPackageName();
617 ResourceOveruseListenerInfo listenerInfo = mOveruseListenerInfosByUid.get(uid, null);
618 if (listenerInfo != null && (listenerInfo.flag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
619 try {
620 listenerInfo.listener.onOveruse(resourceOveruseStats);
621 } catch (RemoteException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700622 Slogf.e(CarWatchdogService.TAG,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700623 "Failed to notify listener(uid %d, package '%s') on resource overuse: %s",
624 uid, resourceOveruseStats, e);
625 }
626 }
627 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
628 ResourceOveruseListenerInfo systemListenerInfo =
629 mOveruseSystemListenerInfosByUid.valueAt(i);
630 if ((systemListenerInfo.flag & FLAG_RESOURCE_OVERUSE_IO) == 0) {
631 continue;
632 }
633 try {
634 systemListenerInfo.listener.onOveruse(resourceOveruseStats);
635 } catch (RemoteException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700636 Slogf.e(CarWatchdogService.TAG, "Failed to notify system listener(uid %d, pid: %d) "
637 + "of resource overuse by package(uid %d, package '%s'): %s",
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700638 systemListenerInfo.uid, systemListenerInfo.pid, uid, packageName, e);
639 }
640 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700641 if (CarWatchdogService.DEBUG) {
642 Slogf.d(CarWatchdogService.TAG,
643 "Notified resource overuse stats to listening applications");
644 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700645 }
646
647 private void checkAndHandleDateChangeLocked() {
648 ZonedDateTime previousUTC = mLastStatsReportUTC;
649 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
650 if (mLastStatsReportUTC.getDayOfYear() == previousUTC.getDayOfYear()
651 && mLastStatsReportUTC.getYear() == previousUTC.getYear()) {
652 return;
653 }
654 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
655 if (usage.oldEnabledState > 0) {
656 // Forgive the daily disabled package on date change.
657 try {
658 IPackageManager packageManager = ActivityThread.getPackageManager();
659 if (packageManager.getApplicationEnabledSetting(usage.packageName,
660 usage.userId)
661 != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
662 continue;
663 }
664 packageManager.setApplicationEnabledSetting(usage.packageName,
665 usage.oldEnabledState,
666 /* flags= */ 0, usage.userId, mContext.getPackageName());
667 } catch (RemoteException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700668 Slogf.e(CarWatchdogService.TAG,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700669 "Failed to reset enabled setting for disabled package '%s', user %d",
670 usage.packageName, usage.userId);
671 }
672 }
673 /* TODO(b/170741935): Stash the old usage into SQLite DB storage. */
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700674 usage.resetStats();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700675 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700676 if (CarWatchdogService.DEBUG) {
677 Slogf.d(CarWatchdogService.TAG, "Handled date change successfully");
678 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700679 }
680
681 private PackageResourceUsage cacheAndFetchUsageLocked(@UserIdInt int userId, String packageName,
682 android.automotive.watchdog.IoOveruseStats internalStats) {
683 String key = getUserPackageUniqueId(userId, packageName);
684 PackageResourceUsage usage = mUsageByUserPackage.getOrDefault(key,
685 new PackageResourceUsage(userId, packageName));
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700686 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700687 mUsageByUserPackage.put(key, usage);
688 return usage;
689 }
690
691 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
692 /*
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700693 * TODO(b/185287136): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700694 * has recurring I/O overuse behavior.
695 */
696 return false;
697 }
698
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700699 private IoOveruseStats getIoOveruseStats(int userId, String packageName,
700 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
701 String key = getUserPackageUniqueId(userId, packageName);
702 PackageResourceUsage usage = mUsageByUserPackage.get(key);
703 if (usage == null) {
704 return null;
705 }
706 IoOveruseStats stats = usage.getIoOveruseStats();
707 long totalBytesWritten = stats != null ? stats.getTotalBytesWritten() : 0;
708 /*
709 * TODO(b/185431129): When maxStatsPeriod > current day, populate the historical stats
710 * from the local database. Also handle the case where the package doesn't have current
711 * day stats but has historical stats.
712 */
713 if (totalBytesWritten < minimumBytesWritten) {
714 return null;
715 }
716 return stats;
717 }
718
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700719 private void addResourceOveruseListenerLocked(
720 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
721 @NonNull IResourceOveruseListener listener,
722 SparseArray<ResourceOveruseListenerInfo> listenerInfosByUid) {
723 int callingPid = Binder.getCallingPid();
724 int callingUid = Binder.getCallingUid();
725 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
726 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
727 "resource overuse listener";
728
729 ResourceOveruseListenerInfo existingListenerInfo = listenerInfosByUid.get(callingUid, null);
730 if (existingListenerInfo != null) {
731 IBinder binder = listener.asBinder();
732 if (existingListenerInfo.listener.asBinder() == binder) {
733 throw new IllegalStateException(
734 "Cannot add " + listenerType + " as it is already added");
735 }
736 }
737
738 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
739 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
740 try {
741 listenerInfo.linkToDeath();
742 } catch (RemoteException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700743 Slogf.w(CarWatchdogService.TAG, "Cannot add %s: linkToDeath to listener failed",
744 listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700745 return;
746 }
747
748 if (existingListenerInfo != null) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700749 Slogf.w(CarWatchdogService.TAG, "Overwriting existing %s: pid %d, uid: %d",
750 listenerType, existingListenerInfo.pid, existingListenerInfo.uid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700751 existingListenerInfo.unlinkToDeath();
752 }
753
754
755 listenerInfosByUid.put(callingUid, listenerInfo);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700756 if (CarWatchdogService.DEBUG) {
757 Slogf.d(CarWatchdogService.TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700758 callingPid, callingUid);
759 }
760 }
761
762 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
763 SparseArray<ResourceOveruseListenerInfo> listenerInfosByUid) {
764 int callingUid = Binder.getCallingUid();
765
766 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
767 ? "resource overuse system listener" : "resource overuse listener";
768
769 ResourceOveruseListenerInfo listenerInfo = listenerInfosByUid.get(callingUid, null);
770 if (listenerInfo == null || listenerInfo.listener != listener) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700771 Slogf.w(CarWatchdogService.TAG,
772 "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700773 return;
774 }
775 listenerInfo.unlinkToDeath();
776 listenerInfosByUid.remove(callingUid);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700777 if (CarWatchdogService.DEBUG) {
778 Slogf.d(CarWatchdogService.TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
779 listenerInfo.pid, listenerInfo.uid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700780 }
781 }
782
783 private void onResourceOveruseListenerDeath(int uid, boolean isListenerForSystem) {
784 synchronized (mLock) {
785 if (isListenerForSystem) {
786 mOveruseSystemListenerInfosByUid.remove(uid);
787 } else {
788 mOveruseListenerInfosByUid.remove(uid);
789 }
790 }
791 }
792
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700793 private static String getUserPackageUniqueId(int userId, String packageName) {
794 return String.valueOf(userId) + ":" + packageName;
795 }
796
797 @VisibleForTesting
798 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
799 android.automotive.watchdog.IoOveruseStats internalStats) {
800 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
801 internalStats.startTime, internalStats.durationInSeconds);
802 statsBuilder.setRemainingWriteBytes(
803 toPerStateBytes(internalStats.remainingWriteBytes));
804 statsBuilder.setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes));
805 statsBuilder.setTotalOveruses(internalStats.totalOveruses);
806 return statsBuilder;
807 }
808
809 private static PerStateBytes toPerStateBytes(
810 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700811 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700812 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700813 }
814
815 private static long totalPerStateBytes(
816 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
817 BiFunction<Long, Long, Long> sum = (l, r) -> {
818 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
819 };
820 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
821 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
822 }
823
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700824 private static long getMinimumBytesWritten(
825 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
826 switch (minimumStatsIoFlag) {
827 case 0:
828 return 0;
829 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
830 return 1024 * 1024;
831 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
832 return 100 * 1024 * 1024;
833 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
834 return 1024 * 1024 * 1024;
835 default:
836 throw new IllegalArgumentException(
837 "Must provide valid minimum stats flag for I/O resource");
838 }
839 }
840
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700841 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
842 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
843 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
844 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
845 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
846 internalConfig.componentType = config.getComponentType();
847 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
848 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
849 internalConfig.packageMetadata = new ArrayList<>();
850 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
851 if (entry.getKey().isEmpty()) {
852 continue;
853 }
854 PackageMetadata metadata = new PackageMetadata();
855 metadata.packageName = entry.getKey();
856 switch(entry.getValue()) {
857 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
858 metadata.appCategoryType = ApplicationCategoryType.MAPS;
859 break;
860 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
861 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
862 break;
863 default:
864 continue;
865 }
866 internalConfig.packageMetadata.add(metadata);
867 }
868 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
869 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
870 && config.getIoOveruseConfiguration() != null) {
871 internalConfig.resourceSpecificConfigurations.add(
872 toResourceSpecificConfiguration(config.getComponentType(),
873 config.getIoOveruseConfiguration()));
874 }
875 return internalConfig;
876 }
877
878 private static ResourceSpecificConfiguration
879 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
880 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
881 new android.automotive.watchdog.internal.IoOveruseConfiguration();
882 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
883 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
884 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
885 config.getPackageSpecificThresholds());
886 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
887 config.getAppCategorySpecificThresholds());
888 for (PerStateIoOveruseThreshold threshold : internalConfig.categorySpecificThresholds) {
889 switch(threshold.name) {
890 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
891 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
892 break;
893 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
894 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
895 break;
896 default:
897 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
898 }
899 }
900 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
901 config.getSystemWideThresholds());
902
903 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
904 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
905 return resourceSpecificConfig;
906 }
907
908 @VisibleForTesting
909 static String toComponentTypeStr(int componentType) {
910 switch(componentType) {
911 case ComponentType.SYSTEM:
912 return "SYSTEM";
913 case ComponentType.VENDOR:
914 return "VENDOR";
915 case ComponentType.THIRD_PARTY:
916 return "THIRD_PARTY";
917 default:
918 return "UNKNOWN";
919 }
920 }
921
922 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
923 Map<String, PerStateBytes> thresholds) {
924 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
925 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
926 if (!entry.getKey().isEmpty()) {
927 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
928 entry.getValue()));
929 }
930 }
931 return internalThresholds;
932 }
933
934 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
935 PerStateBytes perStateBytes) {
936 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
937 threshold.name = name;
938 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
939 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
940 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
941 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
942 return threshold;
943 }
944
945 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
946 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
947 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
948 new ArrayList<>();
949 for (IoOveruseAlertThreshold threshold : thresholds) {
950 if (threshold.getDurationInSeconds() == 0
951 || threshold.getWrittenBytesPerSecond() == 0) {
952 continue;
953 }
954 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
955 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
956 internalThreshold.durationInSeconds = threshold.getDurationInSeconds();
957 internalThreshold.writtenBytesPerSecond = threshold.getWrittenBytesPerSecond();
958 internalThresholds.add(internalThreshold);
959 }
960 return internalThresholds;
961 }
962
963 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
964 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
965 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
966 Map<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
967 for (PackageMetadata metadata : internalConfig.packageMetadata) {
968 String categoryTypeStr;
969 switch (metadata.appCategoryType) {
970 case ApplicationCategoryType.MAPS:
971 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
972 break;
973 case ApplicationCategoryType.MEDIA:
974 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
975 break;
976 default:
977 continue;
978 }
979 packagesToAppCategoryTypes.put(metadata.packageName, categoryTypeStr);
980 }
981 ResourceOveruseConfiguration.Builder configBuilder =
982 new ResourceOveruseConfiguration.Builder(
983 internalConfig.componentType,
984 internalConfig.safeToKillPackages,
985 internalConfig.vendorPackagePrefixes,
986 packagesToAppCategoryTypes);
987 for (ResourceSpecificConfiguration resourceSpecificConfig :
988 internalConfig.resourceSpecificConfigurations) {
989 if (resourceSpecificConfig.getTag()
990 == ResourceSpecificConfiguration.ioOveruseConfiguration
991 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
992 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
993 resourceSpecificConfig.getIoOveruseConfiguration()));
994 }
995 }
996 return configBuilder.build();
997 }
998
999 private static IoOveruseConfiguration toIoOveruseConfiguration(
1000 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
1001 PerStateBytes componentLevelThresholds =
1002 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
1003 Map<String, PerStateBytes> packageSpecificThresholds =
1004 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
1005 Map<String, PerStateBytes> appCategorySpecificThresholds =
1006 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
1007 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
1008 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
1009 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
1010 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
1011 List<IoOveruseAlertThreshold> systemWideThresholds =
1012 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
1013
1014 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1015 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1016 systemWideThresholds);
1017 return configBuilder.build();
1018 }
1019
1020 private static Map<String, PerStateBytes> toPerStateBytesMap(
1021 List<PerStateIoOveruseThreshold> thresholds) {
1022 Map<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1023 for (PerStateIoOveruseThreshold threshold : thresholds) {
1024 thresholdsMap.put(threshold.name, toPerStateBytes(threshold.perStateWriteBytes));
1025 }
1026 return thresholdsMap;
1027 }
1028
1029 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1030 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1031 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
1032 for (android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold
1033 : internalThresholds) {
1034 thresholds.add(new IoOveruseAlertThreshold(internalThreshold.durationInSeconds,
1035 internalThreshold.writtenBytesPerSecond));
1036 }
1037 return thresholds;
1038 }
1039
1040 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1041 PerStateBytes perStateBytes = map.get(oldKey);
1042 if (perStateBytes != null) {
1043 map.put(newKey, perStateBytes);
1044 map.remove(oldKey);
1045 }
1046 }
1047
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001048 private final class PackageResourceUsage {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001049 public final String packageName;
1050 public @UserIdInt final int userId;
1051 public final PackageIoUsage ioUsage;
1052 public int oldEnabledState;
1053
1054 private @KillableState int mKillableState;
1055
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001056 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001057 PackageResourceUsage(@UserIdInt int userId, String packageName) {
1058 this.packageName = packageName;
1059 this.userId = userId;
1060 this.ioUsage = new PackageIoUsage();
1061 this.oldEnabledState = -1;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001062 this.mKillableState = mDefaultNotKillablePackages.contains(packageName)
1063 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001064 }
1065
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001066 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001067 if (!internalStats.killableOnOveruse) {
1068 /*
1069 * Killable value specified in the internal stats is provided by the native daemon.
1070 * This value reflects whether or not an application is safe-to-kill on overuse.
1071 * This setting is from the I/O overuse configuration specified by the system and
1072 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1073 * specify the application is not killable, the application is not safe-to-kill.
1074 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001075 mKillableState = KILLABLE_STATE_NEVER;
1076 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1077 /*
1078 * This case happens when a previously unsafe to kill system/vendor package was
1079 * recently marked as safe-to-kill so update the old state to the default value.
1080 */
1081 mKillableState = mDefaultNotKillablePackages.contains(packageName)
1082 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001083 }
1084 ioUsage.update(internalStats);
1085 }
1086
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001087 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
1088 return new ResourceOveruseStats.Builder(packageName, UserHandle.of(userId));
1089 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001090
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001091 public IoOveruseStats getIoOveruseStats() {
1092 if (!ioUsage.hasUsage()) {
1093 return null;
1094 }
1095 return ioUsage.getStatsBuilder().setKillableOnOveruse(
1096 mKillableState != KILLABLE_STATE_NEVER).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001097 }
1098
1099 public @KillableState int getKillableState() {
1100 return mKillableState;
1101 }
1102
1103 public boolean setKillableState(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001104 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001105 return false;
1106 }
1107 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1108 return true;
1109 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001110
1111 public int syncAndFetchKillableStateLocked(int myComponentType) {
1112 /*
1113 * The killable state goes out-of-sync:
1114 * 1. When the on-device safe-to-kill list is recently updated and the user package
1115 * didn't have any resource usage so the native daemon didn't update the killable state.
1116 * 2. When a package has no resource usage and is initialized outside of processing the
1117 * latest resource usage stats.
1118 */
1119 if (myComponentType != ComponentType.THIRD_PARTY
1120 && !mSafeToKillPackages.contains(packageName)) {
1121 mKillableState = KILLABLE_STATE_NEVER;
1122 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1123 mKillableState = mDefaultNotKillablePackages.contains(packageName)
1124 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1125 }
1126 return mKillableState;
1127 }
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001128
1129 public void resetStats() {
1130 ioUsage.resetStats();
1131 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001132 }
1133
1134 private static final class PackageIoUsage {
1135 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1136 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
1137 private long mTotalTimesKilled;
1138
1139 PackageIoUsage() {
1140 mTotalTimesKilled = 0;
1141 }
1142
1143 public boolean hasUsage() {
1144 return mIoOveruseStats != null;
1145 }
1146
1147 public void update(android.automotive.watchdog.IoOveruseStats internalStats) {
1148 mIoOveruseStats = internalStats;
1149 if (exceedsThreshold()) {
1150 /*
1151 * Forgive written bytes on overuse as the package is either forgiven or killed on
1152 * overuse. When the package is killed, the user may opt to open the corresponding
1153 * app and the package should be forgiven anyways.
1154 * NOTE: If this logic is updated, update the daemon side logic as well.
1155 */
1156 mForgivenWriteBytes = internalStats.writtenBytes;
1157 }
1158 }
1159
1160 public IoOveruseStats.Builder getStatsBuilder() {
1161 IoOveruseStats.Builder statsBuilder = toIoOveruseStatsBuilder(mIoOveruseStats);
1162 statsBuilder.setTotalTimesKilled(mTotalTimesKilled);
1163 return statsBuilder;
1164 }
1165
1166 public boolean exceedsThreshold() {
1167 if (!hasUsage()) {
1168 return false;
1169 }
1170 android.automotive.watchdog.PerStateBytes remaining =
1171 mIoOveruseStats.remainingWriteBytes;
1172 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1173 || remaining.garageModeBytes == 0;
1174 }
1175
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001176 public void resetStats() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001177 mIoOveruseStats = null;
1178 mForgivenWriteBytes = null;
1179 mTotalTimesKilled = 0;
1180 }
1181 }
1182
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001183 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1184 public final IResourceOveruseListener listener;
1185 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1186 public final int pid;
1187 public final int uid;
1188 public final boolean isListenerForSystem;
1189
1190 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1191 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1192 boolean isListenerForSystem) {
1193 this.listener = listener;
1194 this.flag = flag;
1195 this.pid = pid;
1196 this.uid = uid;
1197 this.isListenerForSystem = isListenerForSystem;
1198 }
1199
1200 @Override
1201 public void binderDied() {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -07001202 Slogf.w(CarWatchdogService.TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001203 isListenerForSystem ? " for system" : "", pid);
1204 onResourceOveruseListenerDeath(uid, isListenerForSystem);
1205 unlinkToDeath();
1206 }
1207
1208 private void linkToDeath() throws RemoteException {
1209 listener.asBinder().linkToDeath(this, 0);
1210 }
1211
1212 private void unlinkToDeath() {
1213 listener.asBinder().unlinkToDeath(this, 0);
1214 }
1215 }
1216}