blob: 8d15e0f2c5df4eaac65858120c4b0174d4b68fa3 [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
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070031import static com.android.car.watchdog.CarWatchdogService.DEBUG;
32import static com.android.car.watchdog.CarWatchdogService.TAG;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070033import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070034
35import android.annotation.NonNull;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070036import android.annotation.UserIdInt;
37import android.app.ActivityThread;
38import android.automotive.watchdog.ResourceType;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070039import android.automotive.watchdog.internal.ApplicationCategoryType;
40import android.automotive.watchdog.internal.ComponentType;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070041import android.automotive.watchdog.internal.PackageIdentifier;
42import android.automotive.watchdog.internal.PackageIoOveruseStats;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070043import android.automotive.watchdog.internal.PackageMetadata;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070044import android.automotive.watchdog.internal.PackageResourceOveruseAction;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070045import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
46import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070047import android.car.watchdog.CarWatchdogManager;
48import android.car.watchdog.IResourceOveruseListener;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070049import android.car.watchdog.IoOveruseAlertThreshold;
50import android.car.watchdog.IoOveruseConfiguration;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070051import android.car.watchdog.IoOveruseStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070052import android.car.watchdog.PackageKillableState;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070053import android.car.watchdog.PackageKillableState.KillableState;
54import android.car.watchdog.PerStateBytes;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070055import android.car.watchdog.ResourceOveruseConfiguration;
56import android.car.watchdog.ResourceOveruseStats;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070057import android.car.watchdoglib.CarWatchdogDaemonHelper;
58import android.content.Context;
59import android.content.pm.IPackageManager;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070060import android.content.pm.PackageInfo;
61import android.content.pm.PackageManager;
62import android.content.pm.UserInfo;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070063import android.os.Binder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070064import android.os.Handler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070065import android.os.IBinder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070066import android.os.Looper;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070067import android.os.RemoteException;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070068import android.os.SystemClock;
69import android.os.TransactionTooLargeException;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070070import android.os.UserHandle;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070071import android.os.UserManager;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070072import android.util.ArrayMap;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070073import android.util.ArraySet;
74import android.util.IndentingPrintWriter;
75import android.util.SparseArray;
76
Lakshman Annadorai016127e2021-03-18 09:11:43 -070077import com.android.internal.annotations.GuardedBy;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070078import com.android.internal.annotations.VisibleForTesting;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070079import com.android.internal.util.Preconditions;
80import com.android.server.utils.Slogf;
81
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070082import java.time.ZoneOffset;
83import java.time.ZonedDateTime;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070084import java.util.ArrayList;
85import java.util.List;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070086import java.util.Map;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070087import java.util.Objects;
88import java.util.Set;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070089import java.util.function.BiFunction;
Lakshman Annadorai16999d22021-05-25 08:33:30 -070090import java.util.function.Consumer;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070091
92/**
93 * Handles system resource performance monitoring module.
94 */
95public final class WatchdogPerfHandler {
Lakshman Annadoraie1720472021-04-13 15:22:57 -070096 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS = "MAPS";
97 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA";
98 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN";
99
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700100 private static final long MAX_WAIT_TIME_MILLS = 3_000;
101
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700102 private final Context mContext;
103 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
104 private final PackageInfoHandler mPackageInfoHandler;
105 private final Handler mMainHandler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700106 private final Object mLock = new Object();
107 /*
108 * Cache of added resource overuse listeners by uid.
109 */
110 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700111 private final Map<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>();
112 @GuardedBy("mLock")
113 private final List<PackageResourceOveruseAction> mOveruseActionsByUserPackage =
114 new ArrayList<>();
115 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700116 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>> mOveruseListenerInfosByUid =
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700117 new SparseArray<>();
118 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700119 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>>
120 mOveruseSystemListenerInfosByUid = new SparseArray<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700121 /* Set of safe-to-kill system and vendor packages. */
122 @GuardedBy("mLock")
123 public final Set<String> mSafeToKillPackages = new ArraySet<>();
124 /* Default killable state for packages when not updated by the user. */
125 @GuardedBy("mLock")
126 public final Set<String> mDefaultNotKillablePackages = new ArraySet<>();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700127 @GuardedBy("mLock")
128 private ZonedDateTime mLastStatsReportUTC;
129 @GuardedBy("mLock")
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700130 private List<android.automotive.watchdog.internal.ResourceOveruseConfiguration>
131 mPendingSetResourceOveruseConfigurationsRequest = null;
132 @GuardedBy("mLock")
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700133 boolean mIsConnectedToDaemon;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700134
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700135 public WatchdogPerfHandler(Context context, CarWatchdogDaemonHelper daemonHelper,
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700136 PackageInfoHandler packageInfoHandler) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700137 mContext = context;
138 mCarWatchdogDaemonHelper = daemonHelper;
139 mPackageInfoHandler = packageInfoHandler;
140 mMainHandler = new Handler(Looper.getMainLooper());
141 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700142 }
143
144 /** Initializes the handler. */
145 public void init() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700146 /*
147 * TODO(b/183947162): Opt-in to receive package change broadcast and handle package enabled
148 * state changes.
149 *
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700150 * TODO(b/185287136): Persist in-memory data:
151 * 1. Read the current day's I/O overuse stats from database and push them
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700152 * to the daemon.
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700153 * 2. Fetch the safe-to-kill from daemon on initialization and update mSafeToKillPackages.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700154 */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700155 synchronized (mLock) {
156 checkAndHandleDateChangeLocked();
157 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700158 if (DEBUG) {
159 Slogf.d(TAG, "WatchdogPerfHandler is initialized");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700160 }
161 }
162
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700163 /** Releases the handler */
164 public void release() {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700165 /* TODO(b/185287136): Write daily usage to SQLite DB storage. */
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700166 if (DEBUG) {
167 Slogf.d(TAG, "WatchdogPerfHandler is released");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700168 }
169 }
170
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700171 /** Dumps its state. */
172 public void dump(IndentingPrintWriter writer) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700173 /*
174 * TODO(b/183436216): Implement this method.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700175 */
176 }
177
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700178 /** Retries any pending requests on re-connecting to the daemon */
179 public void onDaemonConnectionChange(boolean isConnected) {
180 synchronized (mLock) {
181 mIsConnectedToDaemon = isConnected;
182 }
183 if (isConnected) {
184 /*
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700185 * Retry pending set resource overuse configuration request before processing any new
186 * set/get requests. Thus notify the waiting requests only after the retry completes.
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700187 */
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700188 retryPendingSetResourceOveruseConfigurations();
189 }
190 synchronized (mLock) {
191 mLock.notifyAll();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700192 }
193 }
194
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700195 /** Returns resource overuse stats for the calling package. */
196 @NonNull
197 public ResourceOveruseStats getResourceOveruseStats(
198 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
199 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
200 Preconditions.checkArgument((resourceOveruseFlag > 0),
201 "Must provide valid resource overuse flag");
202 Preconditions.checkArgument((maxStatsPeriod > 0),
203 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700204 // When more resource stats are added, make this as optional.
205 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
206 "Must provide resource I/O overuse flag");
207 int callingUid = Binder.getCallingUid();
208 int callingUserId = UserHandle.getUserId(callingUid);
209 UserHandle callingUserHandle = UserHandle.of(callingUserId);
210 String callingPackageName =
211 mPackageInfoHandler.getPackageNamesForUids(new int[]{callingUid})
212 .get(callingUid, null);
213 if (callingPackageName == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700214 Slogf.w(TAG, "Failed to fetch package info for uid %d", callingUid);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700215 return new ResourceOveruseStats.Builder("", callingUserHandle).build();
216 }
217 ResourceOveruseStats.Builder statsBuilder =
218 new ResourceOveruseStats.Builder(callingPackageName, callingUserHandle);
219 statsBuilder.setIoOveruseStats(getIoOveruseStats(callingUserId, callingPackageName,
220 /* minimumBytesWritten= */ 0, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700221 if (DEBUG) {
222 Slogf.d(TAG, "Returning all resource overuse stats for calling uid %d [user %d and "
223 + "package '%s']", callingUid, callingUserId, callingPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700224 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700225 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700226 }
227
228 /** Returns resource overuse stats for all packages. */
229 @NonNull
230 public List<ResourceOveruseStats> getAllResourceOveruseStats(
231 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
232 @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag,
233 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
234 Preconditions.checkArgument((resourceOveruseFlag > 0),
235 "Must provide valid resource overuse flag");
236 Preconditions.checkArgument((maxStatsPeriod > 0),
237 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700238 // When more resource types are added, make this as optional.
239 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
240 "Must provide resource I/O overuse flag");
241 long minimumBytesWritten = getMinimumBytesWritten(minimumStatsFlag);
242 List<ResourceOveruseStats> allStats = new ArrayList<>();
243 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
244 ResourceOveruseStats.Builder statsBuilder = usage.getResourceOveruseStatsBuilder();
245 IoOveruseStats ioOveruseStats = getIoOveruseStats(usage.userId, usage.packageName,
246 minimumBytesWritten, maxStatsPeriod);
247 if (ioOveruseStats == null) {
248 continue;
249 }
250 allStats.add(statsBuilder.setIoOveruseStats(ioOveruseStats).build());
251 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700252 if (DEBUG) {
253 Slogf.d(TAG, "Returning all resource overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700254 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700255 return allStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700256 }
257
258 /** Returns resource overuse stats for the specified user package. */
259 @NonNull
260 public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
261 @NonNull String packageName, @NonNull UserHandle userHandle,
262 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
263 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
264 Objects.requireNonNull(packageName, "Package name must be non-null");
265 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700266 Preconditions.checkArgument((userHandle != UserHandle.ALL),
267 "Must provide the user handle for a specific user");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700268 Preconditions.checkArgument((resourceOveruseFlag > 0),
269 "Must provide valid resource overuse flag");
270 Preconditions.checkArgument((maxStatsPeriod > 0),
271 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700272 // When more resource types are added, make this as optional.
273 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
274 "Must provide resource I/O overuse flag");
275 ResourceOveruseStats.Builder statsBuilder =
276 new ResourceOveruseStats.Builder(packageName, userHandle);
277 statsBuilder.setIoOveruseStats(getIoOveruseStats(userHandle.getIdentifier(), packageName,
278 /* minimumBytesWritten= */ 0, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700279 if (DEBUG) {
280 Slogf.d(TAG, "Returning resource overuse stats for user %d, package '%s'",
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700281 userHandle.getIdentifier(), packageName);
282 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700283 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700284 }
285
286 /** Adds the resource overuse listener. */
287 public void addResourceOveruseListener(
288 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
289 @NonNull IResourceOveruseListener listener) {
290 Objects.requireNonNull(listener, "Listener must be non-null");
291 Preconditions.checkArgument((resourceOveruseFlag > 0),
292 "Must provide valid resource overuse flag");
293 synchronized (mLock) {
294 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
295 mOveruseListenerInfosByUid);
296 }
297 }
298
299 /** Removes the previously added resource overuse listener. */
300 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
301 Objects.requireNonNull(listener, "Listener must be non-null");
302 synchronized (mLock) {
303 removeResourceOveruseListenerLocked(listener, mOveruseListenerInfosByUid);
304 }
305 }
306
307 /** Adds the resource overuse system listener. */
308 public void addResourceOveruseListenerForSystem(
309 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
310 @NonNull IResourceOveruseListener listener) {
311 Objects.requireNonNull(listener, "Listener must be non-null");
312 Preconditions.checkArgument((resourceOveruseFlag > 0),
313 "Must provide valid resource overuse flag");
314 synchronized (mLock) {
315 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
316 mOveruseSystemListenerInfosByUid);
317 }
318 }
319
320 /** Removes the previously added resource overuse system listener. */
321 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
322 Objects.requireNonNull(listener, "Listener must be non-null");
323 synchronized (mLock) {
324 removeResourceOveruseListenerLocked(listener, mOveruseSystemListenerInfosByUid);
325 }
326 }
327
328 /** Sets whether or not a package is killable on resource overuse. */
329 public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
330 boolean isKillable) {
331 Objects.requireNonNull(packageName, "Package name must be non-null");
332 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700333 if (userHandle == UserHandle.ALL) {
334 synchronized (mLock) {
335 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
336 if (!usage.packageName.equals(packageName)) {
337 continue;
338 }
339 if (!usage.setKillableState(isKillable)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700340 Slogf.e(TAG, "Cannot set killable state for package '%s'", packageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700341 throw new IllegalArgumentException(
342 "Package killable state is not updatable");
343 }
344 }
345 if (!isKillable) {
346 mDefaultNotKillablePackages.add(packageName);
347 } else {
348 mDefaultNotKillablePackages.remove(packageName);
349 }
350 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700351 if (DEBUG) {
352 Slogf.d(TAG, "Successfully set killable package state for all users");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700353 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700354 return;
355 }
356 int userId = userHandle.getIdentifier();
357 String key = getUserPackageUniqueId(userId, packageName);
358 synchronized (mLock) {
359 /*
360 * When the queried package is not cached in {@link mUsageByUserPackage}, the set API
361 * will update the killable state even when the package should never be killed.
362 * But the get API will return the correct killable state. This behavior is tolerable
363 * because in production the set API should be called only after the get API.
364 * For instance, when this case happens by mistake and the package overuses resource
365 * between the set and the get API calls, the daemon will provide correct killable
366 * state when pushing the latest stats. Ergo, the invalid killable state doesn't have
367 * any effect.
368 */
369 PackageResourceUsage usage = mUsageByUserPackage.getOrDefault(key,
370 new PackageResourceUsage(userId, packageName));
371 if (!usage.setKillableState(isKillable)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700372 Slogf.e(TAG, "User %d cannot set killable state for package '%s'",
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700373 userHandle.getIdentifier(), packageName);
374 throw new IllegalArgumentException("Package killable state is not updatable");
375 }
376 mUsageByUserPackage.put(key, usage);
377 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700378 if (DEBUG) {
379 Slogf.d(TAG, "Successfully set killable package state for user %d", userId);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700380 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700381 }
382
383 /** Returns the list of package killable states on resource overuse for the user. */
384 @NonNull
385 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
386 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700387 PackageManager pm = mContext.getPackageManager();
388 if (userHandle != UserHandle.ALL) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700389 if (DEBUG) {
390 Slogf.d(TAG, "Returning all package killable states for user %d",
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700391 userHandle.getIdentifier());
392 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700393 return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
394 }
395 List<PackageKillableState> packageKillableStates = new ArrayList<>();
396 UserManager userManager = UserManager.get(mContext);
397 List<UserInfo> userInfos = userManager.getAliveUsers();
398 for (UserInfo userInfo : userInfos) {
399 packageKillableStates.addAll(getPackageKillableStatesForUserId(userInfo.id, pm));
400 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700401 if (DEBUG) {
402 Slogf.d(TAG, "Returning all package killable states for all users");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700403 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700404 return packageKillableStates;
405 }
406
407 private List<PackageKillableState> getPackageKillableStatesForUserId(int userId,
408 PackageManager pm) {
409 List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */0, userId);
410 List<PackageKillableState> states = new ArrayList<>();
411 synchronized (mLock) {
412 for (int i = 0; i < packageInfos.size(); ++i) {
413 PackageInfo packageInfo = packageInfos.get(i);
414 String key = getUserPackageUniqueId(userId, packageInfo.packageName);
415 PackageResourceUsage usage = mUsageByUserPackage.getOrDefault(key,
416 new PackageResourceUsage(userId, packageInfo.packageName));
417 int killableState = usage.syncAndFetchKillableStateLocked(
418 mPackageInfoHandler.getComponentType(packageInfo.packageName,
419 packageInfo.applicationInfo));
420 mUsageByUserPackage.put(key, usage);
421 states.add(
422 new PackageKillableState(packageInfo.packageName, userId, killableState));
423 }
424 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700425 if (DEBUG) {
426 Slogf.d(TAG, "Returning the package killable states for a user package");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700427 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700428 return states;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700429 }
430
431 /** Sets the given resource overuse configurations. */
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700432 @CarWatchdogManager.ReturnCode
433 public int setResourceOveruseConfigurations(
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700434 List<ResourceOveruseConfiguration> configurations,
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700435 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
436 throws RemoteException {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700437 Objects.requireNonNull(configurations, "Configurations must be non-null");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700438 Preconditions.checkArgument((configurations.size() > 0),
439 "Must provide at least one configuration");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700440 Preconditions.checkArgument((resourceOveruseFlag > 0),
441 "Must provide valid resource overuse flag");
442 Set<Integer> seenComponentTypes = new ArraySet<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700443 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
444 new ArrayList<>();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700445 for (ResourceOveruseConfiguration config : configurations) {
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700446 /*
447 * TODO(b/185287136): Make sure the validation done here matches the validation done in
448 * the daemon so set requests retried at a later time will complete successfully.
449 */
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700450 int componentType = config.getComponentType();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700451 if (toComponentTypeStr(componentType).equals("UNKNOWN")) {
452 throw new IllegalArgumentException("Invalid component type in the configuration");
453 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700454 if (seenComponentTypes.contains(componentType)) {
455 throw new IllegalArgumentException(
456 "Cannot provide duplicate configurations for the same component type");
457 }
458 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
459 && config.getIoOveruseConfiguration() == null) {
460 throw new IllegalArgumentException("Must provide I/O overuse configuration");
461 }
462 seenComponentTypes.add(config.getComponentType());
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700463 internalConfigs.add(toInternalResourceOveruseConfiguration(config,
464 resourceOveruseFlag));
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700465 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700466 synchronized (mLock) {
467 if (!mIsConnectedToDaemon) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700468 setPendingSetResourceOveruseConfigurationsRequestLocked(internalConfigs);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700469 return CarWatchdogManager.RETURN_CODE_SUCCESS;
470 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700471 /* Verify no pending request in progress. */
472 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700473 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700474 return setResourceOveruseConfigurationsInternal(internalConfigs,
475 /* isPendingRequest= */ false);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700476 }
477
478 /** Returns the available resource overuse configurations. */
479 @NonNull
480 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
481 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
482 Preconditions.checkArgument((resourceOveruseFlag > 0),
483 "Must provide valid resource overuse flag");
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700484 if (!isConnectedToDaemon()) {
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700485 throw new IllegalStateException("Car watchdog daemon is not connected");
486 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700487 synchronized (mLock) {
488 /* Verify no pending request in progress. */
489 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
490 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700491 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
492 new ArrayList<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700493 try {
494 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
495 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700496 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700497 throw new IllegalStateException(e);
498 }
499 List<ResourceOveruseConfiguration> configs = new ArrayList<>();
500 for (android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig
501 : internalConfigs) {
502 configs.add(toResourceOveruseConfiguration(internalConfig, resourceOveruseFlag));
503 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700504 if (DEBUG) {
505 Slogf.d(TAG, "Returning the resource overuse configuration");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700506 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700507 return configs;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700508 }
509
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700510 /** Processes the latest I/O overuse stats */
511 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
512 int[] uids = new int[packageIoOveruseStats.size()];
513 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
514 uids[i] = packageIoOveruseStats.get(i).uid;
515 }
516 SparseArray<String> packageNamesByUid = mPackageInfoHandler.getPackageNamesForUids(uids);
517 synchronized (mLock) {
518 checkAndHandleDateChangeLocked();
519 for (PackageIoOveruseStats stats : packageIoOveruseStats) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700520 String packageName = packageNamesByUid.get(stats.uid);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700521 if (packageName == null) {
522 continue;
523 }
524 int userId = UserHandle.getUserId(stats.uid);
525 PackageResourceUsage usage = cacheAndFetchUsageLocked(userId, packageName,
526 stats.ioOveruseStats);
527 if (stats.shouldNotify) {
528 /*
529 * Packages that exceed the warn threshold percentage should be notified as well
530 * and only the daemon is aware of such packages. Thus the flag is used to
531 * indicate which packages should be notified.
532 */
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700533 ResourceOveruseStats resourceOveruseStats =
534 usage.getResourceOveruseStatsBuilder().setIoOveruseStats(
535 usage.getIoOveruseStats()).build();
536 notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700537 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700538
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700539 if (!usage.ioUsage.exceedsThreshold()) {
540 continue;
541 }
542 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
543 overuseAction.packageIdentifier = new PackageIdentifier();
544 overuseAction.packageIdentifier.name = packageName;
545 overuseAction.packageIdentifier.uid = stats.uid;
546 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
547 overuseAction.resourceOveruseActionType = NOT_KILLED;
548 /*
549 * No action required on I/O overuse on one of the following cases:
550 * #1 The package is not safe to kill as it is critical for system stability.
551 * #2 The package has no recurring overuse behavior and the user opted to not
552 * kill the package so honor the user's decision.
553 */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700554 int killableState = usage.getKillableState();
555 if (killableState == KILLABLE_STATE_NEVER) {
556 mOveruseActionsByUserPackage.add(overuseAction);
557 continue;
558 }
559 boolean hasRecurringOveruse = isRecurringOveruseLocked(usage);
560 if (!hasRecurringOveruse && killableState == KILLABLE_STATE_NO) {
561 overuseAction.resourceOveruseActionType = NOT_KILLED_USER_OPTED;
562 mOveruseActionsByUserPackage.add(overuseAction);
563 continue;
564 }
565 try {
566 int oldEnabledState = -1;
567 IPackageManager packageManager = ActivityThread.getPackageManager();
568 if (!hasRecurringOveruse) {
569 oldEnabledState = packageManager.getApplicationEnabledSetting(packageName,
570 userId);
571
572 if (oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED
573 || oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED_USER
574 || oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
575 mOveruseActionsByUserPackage.add(overuseAction);
576 continue;
577 }
578 }
579
580 packageManager.setApplicationEnabledSetting(packageName,
581 COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId,
582 mContext.getPackageName());
583
584 overuseAction.resourceOveruseActionType = hasRecurringOveruse
585 ? KILLED_RECURRING_OVERUSE : KILLED;
586 if (!hasRecurringOveruse) {
587 usage.oldEnabledState = oldEnabledState;
588 }
589 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700590 Slogf.e(TAG, "Failed to disable application enabled setting for user %d, "
591 + "package '%s'", userId, packageName);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700592 }
593 mOveruseActionsByUserPackage.add(overuseAction);
594 }
595 if (!mOveruseActionsByUserPackage.isEmpty()) {
596 mMainHandler.sendMessage(obtainMessage(
597 WatchdogPerfHandler::notifyActionsTakenOnOveruse, this));
598 }
599 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700600 if (DEBUG) {
601 Slogf.d(TAG, "Processed latest I/O overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700602 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700603 }
604
605 /** Notify daemon about the actions take on resource overuse */
606 public void notifyActionsTakenOnOveruse() {
607 List<PackageResourceOveruseAction> actions;
608 synchronized (mLock) {
609 if (mOveruseActionsByUserPackage.isEmpty()) {
610 return;
611 }
612 actions = new ArrayList<>(mOveruseActionsByUserPackage);
613 mOveruseActionsByUserPackage.clear();
614 }
615 try {
616 mCarWatchdogDaemonHelper.actionTakenOnResourceOveruse(actions);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700617 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700618 Slogf.w(TAG, e, "Failed to notify car watchdog daemon of actions taken on resource "
619 + "overuse");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700620 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700621 if (DEBUG) {
622 Slogf.d(TAG, "Notified car watchdog daemon of actions taken on resource overuse");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700623 }
624 }
625
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700626 /** Resets the resource overuse stats for the given package. */
627 public void resetResourceOveruseStats(Set<String> packageNames) {
628 synchronized (mLock) {
629 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
630 if (packageNames.contains(usage.packageName)) {
631 usage.resetStats();
632 /*
633 * TODO(b/185287136): When the stats are persisted in local DB, reset the stats
634 * for this package from local DB.
635 */
636 }
637 }
638 }
639 }
640
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700641 private void notifyResourceOveruseStatsLocked(int uid,
642 ResourceOveruseStats resourceOveruseStats) {
643 String packageName = resourceOveruseStats.getPackageName();
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700644 ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid);
645 if (listenerInfos != null) {
646 for (ResourceOveruseListenerInfo listenerInfo : listenerInfos) {
647 listenerInfo.notifyListener(FLAG_RESOURCE_OVERUSE_IO, uid, packageName,
648 resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700649 }
650 }
651 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700652 ArrayList<ResourceOveruseListenerInfo> systemListenerInfos =
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700653 mOveruseSystemListenerInfosByUid.valueAt(i);
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700654 for (ResourceOveruseListenerInfo listenerInfo : systemListenerInfos) {
655 listenerInfo.notifyListener(FLAG_RESOURCE_OVERUSE_IO, uid, packageName,
656 resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700657 }
658 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700659 if (DEBUG) {
660 Slogf.d(TAG, "Notified resource overuse stats to listening applications");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700661 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700662 }
663
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700664 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700665 private void checkAndHandleDateChangeLocked() {
666 ZonedDateTime previousUTC = mLastStatsReportUTC;
667 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
668 if (mLastStatsReportUTC.getDayOfYear() == previousUTC.getDayOfYear()
669 && mLastStatsReportUTC.getYear() == previousUTC.getYear()) {
670 return;
671 }
672 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
673 if (usage.oldEnabledState > 0) {
674 // Forgive the daily disabled package on date change.
675 try {
676 IPackageManager packageManager = ActivityThread.getPackageManager();
677 if (packageManager.getApplicationEnabledSetting(usage.packageName,
678 usage.userId)
679 != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
680 continue;
681 }
682 packageManager.setApplicationEnabledSetting(usage.packageName,
683 usage.oldEnabledState,
684 /* flags= */ 0, usage.userId, mContext.getPackageName());
685 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700686 Slogf.e(TAG, "Failed to reset enabled setting for disabled package '%s', user "
687 + "%d", usage.packageName, usage.userId);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700688 }
689 }
690 /* TODO(b/170741935): Stash the old usage into SQLite DB storage. */
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700691 usage.resetStats();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700692 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700693 if (DEBUG) {
694 Slogf.d(TAG, "Handled date change successfully");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700695 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700696 }
697
698 private PackageResourceUsage cacheAndFetchUsageLocked(@UserIdInt int userId, String packageName,
699 android.automotive.watchdog.IoOveruseStats internalStats) {
700 String key = getUserPackageUniqueId(userId, packageName);
701 PackageResourceUsage usage = mUsageByUserPackage.getOrDefault(key,
702 new PackageResourceUsage(userId, packageName));
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700703 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700704 mUsageByUserPackage.put(key, usage);
705 return usage;
706 }
707
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700708 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700709 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
710 /*
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700711 * TODO(b/185287136): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700712 * has recurring I/O overuse behavior.
713 */
714 return false;
715 }
716
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700717 private IoOveruseStats getIoOveruseStats(int userId, String packageName,
718 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
719 String key = getUserPackageUniqueId(userId, packageName);
720 PackageResourceUsage usage = mUsageByUserPackage.get(key);
721 if (usage == null) {
722 return null;
723 }
724 IoOveruseStats stats = usage.getIoOveruseStats();
725 long totalBytesWritten = stats != null ? stats.getTotalBytesWritten() : 0;
726 /*
727 * TODO(b/185431129): When maxStatsPeriod > current day, populate the historical stats
728 * from the local database. Also handle the case where the package doesn't have current
729 * day stats but has historical stats.
730 */
731 if (totalBytesWritten < minimumBytesWritten) {
732 return null;
733 }
734 return stats;
735 }
736
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700737 private void addResourceOveruseListenerLocked(
738 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
739 @NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700740 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700741 int callingPid = Binder.getCallingPid();
742 int callingUid = Binder.getCallingUid();
743 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
744 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
745 "resource overuse listener";
746
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700747 IBinder binder = listener.asBinder();
748 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
749 if (listenerInfos == null) {
750 listenerInfos = new ArrayList<>();
751 listenerInfosByUid.put(callingUid, listenerInfos);
752 }
753 for (ResourceOveruseListenerInfo listenerInfo : listenerInfos) {
754 if (listenerInfo.listener.asBinder() == binder) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700755 throw new IllegalStateException(
756 "Cannot add " + listenerType + " as it is already added");
757 }
758 }
759
760 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
761 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
762 try {
763 listenerInfo.linkToDeath();
764 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700765 Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700766 return;
767 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700768 listenerInfos.add(listenerInfo);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700769 if (DEBUG) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700770 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
771 callingPid, callingUid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700772 }
773 }
774
775 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700776 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700777 int callingUid = Binder.getCallingUid();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700778 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
779 ? "resource overuse system listener" : "resource overuse listener";
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700780 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
781 if (listenerInfos == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700782 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700783 return;
784 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700785 IBinder binder = listener.asBinder();
786 ResourceOveruseListenerInfo cachedListenerInfo = null;
787 for (ResourceOveruseListenerInfo listenerInfo : listenerInfos) {
788 if (listenerInfo.listener.asBinder() == binder) {
789 cachedListenerInfo = listenerInfo;
790 break;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700791 }
792 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700793 if (cachedListenerInfo == null) {
794 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
795 return;
796 }
797 cachedListenerInfo.unlinkToDeath();
798 listenerInfos.remove(cachedListenerInfo);
799 if (listenerInfos.isEmpty()) {
800 listenerInfosByUid.remove(callingUid);
801 }
802 if (DEBUG) {
803 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
804 cachedListenerInfo.pid, cachedListenerInfo.uid);
805 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700806 }
807
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700808 @GuardedBy("mLock")
809 private void setPendingSetResourceOveruseConfigurationsRequestLocked(
810 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) {
811 if (mPendingSetResourceOveruseConfigurationsRequest != null) {
812 if (mPendingSetResourceOveruseConfigurationsRequest == configs) {
813 return;
814 }
815 throw new IllegalStateException(
816 "Pending setResourceOveruseConfigurations request in progress");
817 }
818 mPendingSetResourceOveruseConfigurationsRequest = configs;
819 }
820
821 private void retryPendingSetResourceOveruseConfigurations() {
822 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700823 synchronized (mLock) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700824 if (mPendingSetResourceOveruseConfigurationsRequest == null) {
825 return;
826 }
827 configs = mPendingSetResourceOveruseConfigurationsRequest;
828 }
829 try {
830 int result = setResourceOveruseConfigurationsInternal(configs,
831 /* isPendingRequest= */ true);
832 if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) {
833 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code "
834 + "%d", result);
835 }
836 } catch (Exception e) {
837 Slogf.e(TAG, e, "Exception on set pending resource overuse configurations");
838 }
839 }
840
841 private int setResourceOveruseConfigurationsInternal(
842 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs,
843 boolean isPendingRequest) throws RemoteException {
844 boolean doClearPendingRequest = isPendingRequest;
845 try {
846 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs);
847 } catch (RemoteException e) {
848 if (e instanceof TransactionTooLargeException) {
849 throw e;
850 }
851 Slogf.e(TAG, e, "Remote exception on set resource overuse configuration");
852 synchronized (mLock) {
853 setPendingSetResourceOveruseConfigurationsRequestLocked(configs);
854 }
855 doClearPendingRequest = false;
856 return CarWatchdogManager.RETURN_CODE_SUCCESS;
857 } finally {
858 if (doClearPendingRequest) {
859 synchronized (mLock) {
860 mPendingSetResourceOveruseConfigurationsRequest = null;
861 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700862 }
863 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700864 /* TODO(b/185287136): Fetch safe-to-kill list from daemon and update mSafeToKillPackages. */
865 if (DEBUG) {
866 Slogf.d(TAG, "Set the resource overuse configuration successfully");
867 }
868 return CarWatchdogManager.RETURN_CODE_SUCCESS;
869 }
870
871 private boolean isConnectedToDaemon() {
872 synchronized (mLock) {
873 long startTimeMillis = SystemClock.uptimeMillis();
874 long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
875 while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) {
876 try {
877 mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis);
878 } catch (InterruptedException e) {
879 Thread.currentThread().interrupt();
880 continue;
881 } finally {
882 sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
883 }
884 break;
885 }
886 return mIsConnectedToDaemon;
887 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700888 }
889
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700890 private static String getUserPackageUniqueId(int userId, String packageName) {
891 return String.valueOf(userId) + ":" + packageName;
892 }
893
894 @VisibleForTesting
895 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
896 android.automotive.watchdog.IoOveruseStats internalStats) {
897 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
898 internalStats.startTime, internalStats.durationInSeconds);
899 statsBuilder.setRemainingWriteBytes(
900 toPerStateBytes(internalStats.remainingWriteBytes));
901 statsBuilder.setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes));
902 statsBuilder.setTotalOveruses(internalStats.totalOveruses);
903 return statsBuilder;
904 }
905
906 private static PerStateBytes toPerStateBytes(
907 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700908 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700909 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700910 }
911
912 private static long totalPerStateBytes(
913 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
914 BiFunction<Long, Long, Long> sum = (l, r) -> {
915 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
916 };
917 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
918 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
919 }
920
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700921 private static long getMinimumBytesWritten(
922 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
923 switch (minimumStatsIoFlag) {
924 case 0:
925 return 0;
926 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
927 return 1024 * 1024;
928 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
929 return 100 * 1024 * 1024;
930 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
931 return 1024 * 1024 * 1024;
932 default:
933 throw new IllegalArgumentException(
934 "Must provide valid minimum stats flag for I/O resource");
935 }
936 }
937
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700938 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
939 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
940 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
941 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
942 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
943 internalConfig.componentType = config.getComponentType();
944 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
945 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
946 internalConfig.packageMetadata = new ArrayList<>();
947 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
948 if (entry.getKey().isEmpty()) {
949 continue;
950 }
951 PackageMetadata metadata = new PackageMetadata();
952 metadata.packageName = entry.getKey();
953 switch(entry.getValue()) {
954 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
955 metadata.appCategoryType = ApplicationCategoryType.MAPS;
956 break;
957 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
958 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
959 break;
960 default:
961 continue;
962 }
963 internalConfig.packageMetadata.add(metadata);
964 }
965 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
966 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
967 && config.getIoOveruseConfiguration() != null) {
968 internalConfig.resourceSpecificConfigurations.add(
969 toResourceSpecificConfiguration(config.getComponentType(),
970 config.getIoOveruseConfiguration()));
971 }
972 return internalConfig;
973 }
974
975 private static ResourceSpecificConfiguration
976 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
977 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
978 new android.automotive.watchdog.internal.IoOveruseConfiguration();
979 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
980 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
981 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
982 config.getPackageSpecificThresholds());
983 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
984 config.getAppCategorySpecificThresholds());
985 for (PerStateIoOveruseThreshold threshold : internalConfig.categorySpecificThresholds) {
986 switch(threshold.name) {
987 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
988 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
989 break;
990 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
991 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
992 break;
993 default:
994 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
995 }
996 }
997 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
998 config.getSystemWideThresholds());
999
1000 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
1001 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
1002 return resourceSpecificConfig;
1003 }
1004
1005 @VisibleForTesting
1006 static String toComponentTypeStr(int componentType) {
1007 switch(componentType) {
1008 case ComponentType.SYSTEM:
1009 return "SYSTEM";
1010 case ComponentType.VENDOR:
1011 return "VENDOR";
1012 case ComponentType.THIRD_PARTY:
1013 return "THIRD_PARTY";
1014 default:
1015 return "UNKNOWN";
1016 }
1017 }
1018
1019 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
1020 Map<String, PerStateBytes> thresholds) {
1021 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
1022 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
1023 if (!entry.getKey().isEmpty()) {
1024 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
1025 entry.getValue()));
1026 }
1027 }
1028 return internalThresholds;
1029 }
1030
1031 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
1032 PerStateBytes perStateBytes) {
1033 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
1034 threshold.name = name;
1035 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
1036 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
1037 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
1038 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
1039 return threshold;
1040 }
1041
1042 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
1043 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
1044 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
1045 new ArrayList<>();
1046 for (IoOveruseAlertThreshold threshold : thresholds) {
1047 if (threshold.getDurationInSeconds() == 0
1048 || threshold.getWrittenBytesPerSecond() == 0) {
1049 continue;
1050 }
1051 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
1052 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
1053 internalThreshold.durationInSeconds = threshold.getDurationInSeconds();
1054 internalThreshold.writtenBytesPerSecond = threshold.getWrittenBytesPerSecond();
1055 internalThresholds.add(internalThreshold);
1056 }
1057 return internalThresholds;
1058 }
1059
1060 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
1061 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
1062 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1063 Map<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
1064 for (PackageMetadata metadata : internalConfig.packageMetadata) {
1065 String categoryTypeStr;
1066 switch (metadata.appCategoryType) {
1067 case ApplicationCategoryType.MAPS:
1068 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
1069 break;
1070 case ApplicationCategoryType.MEDIA:
1071 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
1072 break;
1073 default:
1074 continue;
1075 }
1076 packagesToAppCategoryTypes.put(metadata.packageName, categoryTypeStr);
1077 }
1078 ResourceOveruseConfiguration.Builder configBuilder =
1079 new ResourceOveruseConfiguration.Builder(
1080 internalConfig.componentType,
1081 internalConfig.safeToKillPackages,
1082 internalConfig.vendorPackagePrefixes,
1083 packagesToAppCategoryTypes);
1084 for (ResourceSpecificConfiguration resourceSpecificConfig :
1085 internalConfig.resourceSpecificConfigurations) {
1086 if (resourceSpecificConfig.getTag()
1087 == ResourceSpecificConfiguration.ioOveruseConfiguration
1088 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
1089 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
1090 resourceSpecificConfig.getIoOveruseConfiguration()));
1091 }
1092 }
1093 return configBuilder.build();
1094 }
1095
1096 private static IoOveruseConfiguration toIoOveruseConfiguration(
1097 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
1098 PerStateBytes componentLevelThresholds =
1099 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
1100 Map<String, PerStateBytes> packageSpecificThresholds =
1101 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
1102 Map<String, PerStateBytes> appCategorySpecificThresholds =
1103 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
1104 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
1105 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
1106 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
1107 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
1108 List<IoOveruseAlertThreshold> systemWideThresholds =
1109 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
1110
1111 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1112 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1113 systemWideThresholds);
1114 return configBuilder.build();
1115 }
1116
1117 private static Map<String, PerStateBytes> toPerStateBytesMap(
1118 List<PerStateIoOveruseThreshold> thresholds) {
1119 Map<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1120 for (PerStateIoOveruseThreshold threshold : thresholds) {
1121 thresholdsMap.put(threshold.name, toPerStateBytes(threshold.perStateWriteBytes));
1122 }
1123 return thresholdsMap;
1124 }
1125
1126 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1127 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1128 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
1129 for (android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold
1130 : internalThresholds) {
1131 thresholds.add(new IoOveruseAlertThreshold(internalThreshold.durationInSeconds,
1132 internalThreshold.writtenBytesPerSecond));
1133 }
1134 return thresholds;
1135 }
1136
1137 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1138 PerStateBytes perStateBytes = map.get(oldKey);
1139 if (perStateBytes != null) {
1140 map.put(newKey, perStateBytes);
1141 map.remove(oldKey);
1142 }
1143 }
1144
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001145 private final class PackageResourceUsage {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001146 public final String packageName;
1147 public @UserIdInt final int userId;
1148 public final PackageIoUsage ioUsage;
1149 public int oldEnabledState;
1150
1151 private @KillableState int mKillableState;
1152
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001153 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001154 PackageResourceUsage(@UserIdInt int userId, String packageName) {
1155 this.packageName = packageName;
1156 this.userId = userId;
1157 this.ioUsage = new PackageIoUsage();
1158 this.oldEnabledState = -1;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001159 this.mKillableState = mDefaultNotKillablePackages.contains(packageName)
1160 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001161 }
1162
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001163 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001164 if (!internalStats.killableOnOveruse) {
1165 /*
1166 * Killable value specified in the internal stats is provided by the native daemon.
1167 * This value reflects whether or not an application is safe-to-kill on overuse.
1168 * This setting is from the I/O overuse configuration specified by the system and
1169 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1170 * specify the application is not killable, the application is not safe-to-kill.
1171 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001172 mKillableState = KILLABLE_STATE_NEVER;
1173 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1174 /*
1175 * This case happens when a previously unsafe to kill system/vendor package was
1176 * recently marked as safe-to-kill so update the old state to the default value.
1177 */
1178 mKillableState = mDefaultNotKillablePackages.contains(packageName)
1179 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001180 }
1181 ioUsage.update(internalStats);
1182 }
1183
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001184 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
1185 return new ResourceOveruseStats.Builder(packageName, UserHandle.of(userId));
1186 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001187
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001188 public IoOveruseStats getIoOveruseStats() {
1189 if (!ioUsage.hasUsage()) {
1190 return null;
1191 }
1192 return ioUsage.getStatsBuilder().setKillableOnOveruse(
1193 mKillableState != KILLABLE_STATE_NEVER).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001194 }
1195
1196 public @KillableState int getKillableState() {
1197 return mKillableState;
1198 }
1199
1200 public boolean setKillableState(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001201 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001202 return false;
1203 }
1204 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1205 return true;
1206 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001207
1208 public int syncAndFetchKillableStateLocked(int myComponentType) {
1209 /*
1210 * The killable state goes out-of-sync:
1211 * 1. When the on-device safe-to-kill list is recently updated and the user package
1212 * didn't have any resource usage so the native daemon didn't update the killable state.
1213 * 2. When a package has no resource usage and is initialized outside of processing the
1214 * latest resource usage stats.
1215 */
1216 if (myComponentType != ComponentType.THIRD_PARTY
1217 && !mSafeToKillPackages.contains(packageName)) {
1218 mKillableState = KILLABLE_STATE_NEVER;
1219 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1220 mKillableState = mDefaultNotKillablePackages.contains(packageName)
1221 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1222 }
1223 return mKillableState;
1224 }
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001225
1226 public void resetStats() {
1227 ioUsage.resetStats();
1228 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001229 }
1230
1231 private static final class PackageIoUsage {
1232 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1233 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
1234 private long mTotalTimesKilled;
1235
1236 PackageIoUsage() {
1237 mTotalTimesKilled = 0;
1238 }
1239
1240 public boolean hasUsage() {
1241 return mIoOveruseStats != null;
1242 }
1243
1244 public void update(android.automotive.watchdog.IoOveruseStats internalStats) {
1245 mIoOveruseStats = internalStats;
1246 if (exceedsThreshold()) {
1247 /*
1248 * Forgive written bytes on overuse as the package is either forgiven or killed on
1249 * overuse. When the package is killed, the user may opt to open the corresponding
1250 * app and the package should be forgiven anyways.
1251 * NOTE: If this logic is updated, update the daemon side logic as well.
1252 */
1253 mForgivenWriteBytes = internalStats.writtenBytes;
1254 }
1255 }
1256
1257 public IoOveruseStats.Builder getStatsBuilder() {
1258 IoOveruseStats.Builder statsBuilder = toIoOveruseStatsBuilder(mIoOveruseStats);
1259 statsBuilder.setTotalTimesKilled(mTotalTimesKilled);
1260 return statsBuilder;
1261 }
1262
1263 public boolean exceedsThreshold() {
1264 if (!hasUsage()) {
1265 return false;
1266 }
1267 android.automotive.watchdog.PerStateBytes remaining =
1268 mIoOveruseStats.remainingWriteBytes;
1269 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1270 || remaining.garageModeBytes == 0;
1271 }
1272
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001273 public void resetStats() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001274 mIoOveruseStats = null;
1275 mForgivenWriteBytes = null;
1276 mTotalTimesKilled = 0;
1277 }
1278 }
1279
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001280 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1281 public final IResourceOveruseListener listener;
1282 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1283 public final int pid;
1284 public final int uid;
1285 public final boolean isListenerForSystem;
1286
1287 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1288 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1289 boolean isListenerForSystem) {
1290 this.listener = listener;
1291 this.flag = flag;
1292 this.pid = pid;
1293 this.uid = uid;
1294 this.isListenerForSystem = isListenerForSystem;
1295 }
1296
1297 @Override
1298 public void binderDied() {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001299 Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001300 isListenerForSystem ? " for system" : "", pid);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001301 Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo =
1302 listenerInfosByUid -> {
1303 ArrayList<ResourceOveruseListenerInfo> listenerInfos =
1304 listenerInfosByUid.get(uid);
1305 if (listenerInfos == null) {
1306 return;
1307 }
1308 listenerInfos.remove(this);
1309 if (listenerInfos.isEmpty()) {
1310 listenerInfosByUid.remove(uid);
1311 }
1312 };
1313 if (isListenerForSystem) {
1314 removeListenerInfo.accept(mOveruseSystemListenerInfosByUid);
1315 } else {
1316 removeListenerInfo.accept(mOveruseListenerInfosByUid);
1317 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001318 unlinkToDeath();
1319 }
1320
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001321 public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType,
1322 int overusingUid, String overusingPackage,
1323 ResourceOveruseStats resourceOveruseStats) {
1324 if ((flag & resourceType) == 0) {
1325 return;
1326 }
1327 try {
1328 listener.onOveruse(resourceOveruseStats);
1329 } catch (RemoteException e) {
1330 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by "
1331 + "package(uid %d, package '%s'): %s",
1332 (isListenerForSystem ? "system listener" : "listener"), uid, pid,
1333 overusingUid, overusingPackage, e);
1334 }
1335 }
1336
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001337 private void linkToDeath() throws RemoteException {
1338 listener.asBinder().linkToDeath(this, 0);
1339 }
1340
1341 private void unlinkToDeath() {
1342 listener.asBinder().unlinkToDeath(this, 0);
1343 }
1344 }
1345}