blob: 80dcb8256e9238a74b3f7af866208b790dacc85c [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
599 private void notifyResourceOveruseStatsLocked(int uid,
600 ResourceOveruseStats resourceOveruseStats) {
601 String packageName = resourceOveruseStats.getPackageName();
602 ResourceOveruseListenerInfo listenerInfo = mOveruseListenerInfosByUid.get(uid, null);
603 if (listenerInfo != null && (listenerInfo.flag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
604 try {
605 listenerInfo.listener.onOveruse(resourceOveruseStats);
606 } catch (RemoteException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700607 Slogf.e(CarWatchdogService.TAG,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700608 "Failed to notify listener(uid %d, package '%s') on resource overuse: %s",
609 uid, resourceOveruseStats, e);
610 }
611 }
612 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
613 ResourceOveruseListenerInfo systemListenerInfo =
614 mOveruseSystemListenerInfosByUid.valueAt(i);
615 if ((systemListenerInfo.flag & FLAG_RESOURCE_OVERUSE_IO) == 0) {
616 continue;
617 }
618 try {
619 systemListenerInfo.listener.onOveruse(resourceOveruseStats);
620 } catch (RemoteException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700621 Slogf.e(CarWatchdogService.TAG, "Failed to notify system listener(uid %d, pid: %d) "
622 + "of resource overuse by package(uid %d, package '%s'): %s",
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700623 systemListenerInfo.uid, systemListenerInfo.pid, uid, packageName, e);
624 }
625 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700626 if (CarWatchdogService.DEBUG) {
627 Slogf.d(CarWatchdogService.TAG,
628 "Notified resource overuse stats to listening applications");
629 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700630 }
631
632 private void checkAndHandleDateChangeLocked() {
633 ZonedDateTime previousUTC = mLastStatsReportUTC;
634 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
635 if (mLastStatsReportUTC.getDayOfYear() == previousUTC.getDayOfYear()
636 && mLastStatsReportUTC.getYear() == previousUTC.getYear()) {
637 return;
638 }
639 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
640 if (usage.oldEnabledState > 0) {
641 // Forgive the daily disabled package on date change.
642 try {
643 IPackageManager packageManager = ActivityThread.getPackageManager();
644 if (packageManager.getApplicationEnabledSetting(usage.packageName,
645 usage.userId)
646 != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
647 continue;
648 }
649 packageManager.setApplicationEnabledSetting(usage.packageName,
650 usage.oldEnabledState,
651 /* flags= */ 0, usage.userId, mContext.getPackageName());
652 } catch (RemoteException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700653 Slogf.e(CarWatchdogService.TAG,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700654 "Failed to reset enabled setting for disabled package '%s', user %d",
655 usage.packageName, usage.userId);
656 }
657 }
658 /* TODO(b/170741935): Stash the old usage into SQLite DB storage. */
659 usage.ioUsage.clear();
660 }
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700661 if (CarWatchdogService.DEBUG) {
662 Slogf.d(CarWatchdogService.TAG, "Handled date change successfully");
663 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700664 }
665
666 private PackageResourceUsage cacheAndFetchUsageLocked(@UserIdInt int userId, String packageName,
667 android.automotive.watchdog.IoOveruseStats internalStats) {
668 String key = getUserPackageUniqueId(userId, packageName);
669 PackageResourceUsage usage = mUsageByUserPackage.getOrDefault(key,
670 new PackageResourceUsage(userId, packageName));
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700671 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700672 mUsageByUserPackage.put(key, usage);
673 return usage;
674 }
675
676 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
677 /*
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700678 * TODO(b/185287136): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700679 * has recurring I/O overuse behavior.
680 */
681 return false;
682 }
683
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700684 private IoOveruseStats getIoOveruseStats(int userId, String packageName,
685 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
686 String key = getUserPackageUniqueId(userId, packageName);
687 PackageResourceUsage usage = mUsageByUserPackage.get(key);
688 if (usage == null) {
689 return null;
690 }
691 IoOveruseStats stats = usage.getIoOveruseStats();
692 long totalBytesWritten = stats != null ? stats.getTotalBytesWritten() : 0;
693 /*
694 * TODO(b/185431129): When maxStatsPeriod > current day, populate the historical stats
695 * from the local database. Also handle the case where the package doesn't have current
696 * day stats but has historical stats.
697 */
698 if (totalBytesWritten < minimumBytesWritten) {
699 return null;
700 }
701 return stats;
702 }
703
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700704 private void addResourceOveruseListenerLocked(
705 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
706 @NonNull IResourceOveruseListener listener,
707 SparseArray<ResourceOveruseListenerInfo> listenerInfosByUid) {
708 int callingPid = Binder.getCallingPid();
709 int callingUid = Binder.getCallingUid();
710 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
711 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
712 "resource overuse listener";
713
714 ResourceOveruseListenerInfo existingListenerInfo = listenerInfosByUid.get(callingUid, null);
715 if (existingListenerInfo != null) {
716 IBinder binder = listener.asBinder();
717 if (existingListenerInfo.listener.asBinder() == binder) {
718 throw new IllegalStateException(
719 "Cannot add " + listenerType + " as it is already added");
720 }
721 }
722
723 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
724 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
725 try {
726 listenerInfo.linkToDeath();
727 } catch (RemoteException e) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700728 Slogf.w(CarWatchdogService.TAG, "Cannot add %s: linkToDeath to listener failed",
729 listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700730 return;
731 }
732
733 if (existingListenerInfo != null) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700734 Slogf.w(CarWatchdogService.TAG, "Overwriting existing %s: pid %d, uid: %d",
735 listenerType, existingListenerInfo.pid, existingListenerInfo.uid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700736 existingListenerInfo.unlinkToDeath();
737 }
738
739
740 listenerInfosByUid.put(callingUid, listenerInfo);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700741 if (CarWatchdogService.DEBUG) {
742 Slogf.d(CarWatchdogService.TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700743 callingPid, callingUid);
744 }
745 }
746
747 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
748 SparseArray<ResourceOveruseListenerInfo> listenerInfosByUid) {
749 int callingUid = Binder.getCallingUid();
750
751 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
752 ? "resource overuse system listener" : "resource overuse listener";
753
754 ResourceOveruseListenerInfo listenerInfo = listenerInfosByUid.get(callingUid, null);
755 if (listenerInfo == null || listenerInfo.listener != listener) {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700756 Slogf.w(CarWatchdogService.TAG,
757 "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700758 return;
759 }
760 listenerInfo.unlinkToDeath();
761 listenerInfosByUid.remove(callingUid);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700762 if (CarWatchdogService.DEBUG) {
763 Slogf.d(CarWatchdogService.TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
764 listenerInfo.pid, listenerInfo.uid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700765 }
766 }
767
768 private void onResourceOveruseListenerDeath(int uid, boolean isListenerForSystem) {
769 synchronized (mLock) {
770 if (isListenerForSystem) {
771 mOveruseSystemListenerInfosByUid.remove(uid);
772 } else {
773 mOveruseListenerInfosByUid.remove(uid);
774 }
775 }
776 }
777
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700778 private static String getUserPackageUniqueId(int userId, String packageName) {
779 return String.valueOf(userId) + ":" + packageName;
780 }
781
782 @VisibleForTesting
783 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
784 android.automotive.watchdog.IoOveruseStats internalStats) {
785 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
786 internalStats.startTime, internalStats.durationInSeconds);
787 statsBuilder.setRemainingWriteBytes(
788 toPerStateBytes(internalStats.remainingWriteBytes));
789 statsBuilder.setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes));
790 statsBuilder.setTotalOveruses(internalStats.totalOveruses);
791 return statsBuilder;
792 }
793
794 private static PerStateBytes toPerStateBytes(
795 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700796 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700797 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700798 }
799
800 private static long totalPerStateBytes(
801 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
802 BiFunction<Long, Long, Long> sum = (l, r) -> {
803 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
804 };
805 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
806 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
807 }
808
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700809 private static long getMinimumBytesWritten(
810 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
811 switch (minimumStatsIoFlag) {
812 case 0:
813 return 0;
814 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
815 return 1024 * 1024;
816 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
817 return 100 * 1024 * 1024;
818 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
819 return 1024 * 1024 * 1024;
820 default:
821 throw new IllegalArgumentException(
822 "Must provide valid minimum stats flag for I/O resource");
823 }
824 }
825
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700826 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
827 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
828 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
829 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
830 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
831 internalConfig.componentType = config.getComponentType();
832 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
833 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
834 internalConfig.packageMetadata = new ArrayList<>();
835 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
836 if (entry.getKey().isEmpty()) {
837 continue;
838 }
839 PackageMetadata metadata = new PackageMetadata();
840 metadata.packageName = entry.getKey();
841 switch(entry.getValue()) {
842 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
843 metadata.appCategoryType = ApplicationCategoryType.MAPS;
844 break;
845 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
846 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
847 break;
848 default:
849 continue;
850 }
851 internalConfig.packageMetadata.add(metadata);
852 }
853 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
854 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
855 && config.getIoOveruseConfiguration() != null) {
856 internalConfig.resourceSpecificConfigurations.add(
857 toResourceSpecificConfiguration(config.getComponentType(),
858 config.getIoOveruseConfiguration()));
859 }
860 return internalConfig;
861 }
862
863 private static ResourceSpecificConfiguration
864 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
865 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
866 new android.automotive.watchdog.internal.IoOveruseConfiguration();
867 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
868 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
869 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
870 config.getPackageSpecificThresholds());
871 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
872 config.getAppCategorySpecificThresholds());
873 for (PerStateIoOveruseThreshold threshold : internalConfig.categorySpecificThresholds) {
874 switch(threshold.name) {
875 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
876 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
877 break;
878 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
879 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
880 break;
881 default:
882 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
883 }
884 }
885 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
886 config.getSystemWideThresholds());
887
888 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
889 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
890 return resourceSpecificConfig;
891 }
892
893 @VisibleForTesting
894 static String toComponentTypeStr(int componentType) {
895 switch(componentType) {
896 case ComponentType.SYSTEM:
897 return "SYSTEM";
898 case ComponentType.VENDOR:
899 return "VENDOR";
900 case ComponentType.THIRD_PARTY:
901 return "THIRD_PARTY";
902 default:
903 return "UNKNOWN";
904 }
905 }
906
907 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
908 Map<String, PerStateBytes> thresholds) {
909 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
910 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
911 if (!entry.getKey().isEmpty()) {
912 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
913 entry.getValue()));
914 }
915 }
916 return internalThresholds;
917 }
918
919 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
920 PerStateBytes perStateBytes) {
921 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
922 threshold.name = name;
923 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
924 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
925 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
926 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
927 return threshold;
928 }
929
930 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
931 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
932 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
933 new ArrayList<>();
934 for (IoOveruseAlertThreshold threshold : thresholds) {
935 if (threshold.getDurationInSeconds() == 0
936 || threshold.getWrittenBytesPerSecond() == 0) {
937 continue;
938 }
939 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
940 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
941 internalThreshold.durationInSeconds = threshold.getDurationInSeconds();
942 internalThreshold.writtenBytesPerSecond = threshold.getWrittenBytesPerSecond();
943 internalThresholds.add(internalThreshold);
944 }
945 return internalThresholds;
946 }
947
948 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
949 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
950 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
951 Map<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
952 for (PackageMetadata metadata : internalConfig.packageMetadata) {
953 String categoryTypeStr;
954 switch (metadata.appCategoryType) {
955 case ApplicationCategoryType.MAPS:
956 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
957 break;
958 case ApplicationCategoryType.MEDIA:
959 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
960 break;
961 default:
962 continue;
963 }
964 packagesToAppCategoryTypes.put(metadata.packageName, categoryTypeStr);
965 }
966 ResourceOveruseConfiguration.Builder configBuilder =
967 new ResourceOveruseConfiguration.Builder(
968 internalConfig.componentType,
969 internalConfig.safeToKillPackages,
970 internalConfig.vendorPackagePrefixes,
971 packagesToAppCategoryTypes);
972 for (ResourceSpecificConfiguration resourceSpecificConfig :
973 internalConfig.resourceSpecificConfigurations) {
974 if (resourceSpecificConfig.getTag()
975 == ResourceSpecificConfiguration.ioOveruseConfiguration
976 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
977 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
978 resourceSpecificConfig.getIoOveruseConfiguration()));
979 }
980 }
981 return configBuilder.build();
982 }
983
984 private static IoOveruseConfiguration toIoOveruseConfiguration(
985 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
986 PerStateBytes componentLevelThresholds =
987 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
988 Map<String, PerStateBytes> packageSpecificThresholds =
989 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
990 Map<String, PerStateBytes> appCategorySpecificThresholds =
991 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
992 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
993 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
994 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
995 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
996 List<IoOveruseAlertThreshold> systemWideThresholds =
997 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
998
999 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1000 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1001 systemWideThresholds);
1002 return configBuilder.build();
1003 }
1004
1005 private static Map<String, PerStateBytes> toPerStateBytesMap(
1006 List<PerStateIoOveruseThreshold> thresholds) {
1007 Map<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1008 for (PerStateIoOveruseThreshold threshold : thresholds) {
1009 thresholdsMap.put(threshold.name, toPerStateBytes(threshold.perStateWriteBytes));
1010 }
1011 return thresholdsMap;
1012 }
1013
1014 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1015 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1016 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
1017 for (android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold
1018 : internalThresholds) {
1019 thresholds.add(new IoOveruseAlertThreshold(internalThreshold.durationInSeconds,
1020 internalThreshold.writtenBytesPerSecond));
1021 }
1022 return thresholds;
1023 }
1024
1025 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1026 PerStateBytes perStateBytes = map.get(oldKey);
1027 if (perStateBytes != null) {
1028 map.put(newKey, perStateBytes);
1029 map.remove(oldKey);
1030 }
1031 }
1032
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001033 private final class PackageResourceUsage {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001034 public final String packageName;
1035 public @UserIdInt final int userId;
1036 public final PackageIoUsage ioUsage;
1037 public int oldEnabledState;
1038
1039 private @KillableState int mKillableState;
1040
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001041 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001042 PackageResourceUsage(@UserIdInt int userId, String packageName) {
1043 this.packageName = packageName;
1044 this.userId = userId;
1045 this.ioUsage = new PackageIoUsage();
1046 this.oldEnabledState = -1;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001047 this.mKillableState = mDefaultNotKillablePackages.contains(packageName)
1048 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001049 }
1050
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001051 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001052 if (!internalStats.killableOnOveruse) {
1053 /*
1054 * Killable value specified in the internal stats is provided by the native daemon.
1055 * This value reflects whether or not an application is safe-to-kill on overuse.
1056 * This setting is from the I/O overuse configuration specified by the system and
1057 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1058 * specify the application is not killable, the application is not safe-to-kill.
1059 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001060 mKillableState = KILLABLE_STATE_NEVER;
1061 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1062 /*
1063 * This case happens when a previously unsafe to kill system/vendor package was
1064 * recently marked as safe-to-kill so update the old state to the default value.
1065 */
1066 mKillableState = mDefaultNotKillablePackages.contains(packageName)
1067 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001068 }
1069 ioUsage.update(internalStats);
1070 }
1071
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001072 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
1073 return new ResourceOveruseStats.Builder(packageName, UserHandle.of(userId));
1074 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001075
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001076 public IoOveruseStats getIoOveruseStats() {
1077 if (!ioUsage.hasUsage()) {
1078 return null;
1079 }
1080 return ioUsage.getStatsBuilder().setKillableOnOveruse(
1081 mKillableState != KILLABLE_STATE_NEVER).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001082 }
1083
1084 public @KillableState int getKillableState() {
1085 return mKillableState;
1086 }
1087
1088 public boolean setKillableState(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001089 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001090 return false;
1091 }
1092 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1093 return true;
1094 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001095
1096 public int syncAndFetchKillableStateLocked(int myComponentType) {
1097 /*
1098 * The killable state goes out-of-sync:
1099 * 1. When the on-device safe-to-kill list is recently updated and the user package
1100 * didn't have any resource usage so the native daemon didn't update the killable state.
1101 * 2. When a package has no resource usage and is initialized outside of processing the
1102 * latest resource usage stats.
1103 */
1104 if (myComponentType != ComponentType.THIRD_PARTY
1105 && !mSafeToKillPackages.contains(packageName)) {
1106 mKillableState = KILLABLE_STATE_NEVER;
1107 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1108 mKillableState = mDefaultNotKillablePackages.contains(packageName)
1109 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1110 }
1111 return mKillableState;
1112 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001113 }
1114
1115 private static final class PackageIoUsage {
1116 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1117 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
1118 private long mTotalTimesKilled;
1119
1120 PackageIoUsage() {
1121 mTotalTimesKilled = 0;
1122 }
1123
1124 public boolean hasUsage() {
1125 return mIoOveruseStats != null;
1126 }
1127
1128 public void update(android.automotive.watchdog.IoOveruseStats internalStats) {
1129 mIoOveruseStats = internalStats;
1130 if (exceedsThreshold()) {
1131 /*
1132 * Forgive written bytes on overuse as the package is either forgiven or killed on
1133 * overuse. When the package is killed, the user may opt to open the corresponding
1134 * app and the package should be forgiven anyways.
1135 * NOTE: If this logic is updated, update the daemon side logic as well.
1136 */
1137 mForgivenWriteBytes = internalStats.writtenBytes;
1138 }
1139 }
1140
1141 public IoOveruseStats.Builder getStatsBuilder() {
1142 IoOveruseStats.Builder statsBuilder = toIoOveruseStatsBuilder(mIoOveruseStats);
1143 statsBuilder.setTotalTimesKilled(mTotalTimesKilled);
1144 return statsBuilder;
1145 }
1146
1147 public boolean exceedsThreshold() {
1148 if (!hasUsage()) {
1149 return false;
1150 }
1151 android.automotive.watchdog.PerStateBytes remaining =
1152 mIoOveruseStats.remainingWriteBytes;
1153 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1154 || remaining.garageModeBytes == 0;
1155 }
1156
1157 public void clear() {
1158 mIoOveruseStats = null;
1159 mForgivenWriteBytes = null;
1160 mTotalTimesKilled = 0;
1161 }
1162 }
1163
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001164 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1165 public final IResourceOveruseListener listener;
1166 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1167 public final int pid;
1168 public final int uid;
1169 public final boolean isListenerForSystem;
1170
1171 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1172 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1173 boolean isListenerForSystem) {
1174 this.listener = listener;
1175 this.flag = flag;
1176 this.pid = pid;
1177 this.uid = uid;
1178 this.isListenerForSystem = isListenerForSystem;
1179 }
1180
1181 @Override
1182 public void binderDied() {
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -07001183 Slogf.w(CarWatchdogService.TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001184 isListenerForSystem ? " for system" : "", pid);
1185 onResourceOveruseListenerDeath(uid, isListenerForSystem);
1186 unlinkToDeath();
1187 }
1188
1189 private void linkToDeath() throws RemoteException {
1190 listener.asBinder().linkToDeath(this, 0);
1191 }
1192
1193 private void unlinkToDeath() {
1194 listener.asBinder().unlinkToDeath(this, 0);
1195 }
1196 }
1197}