blob: f8c0a5a2ac358038cb572ba91b96677c4199971f [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 Annadorai7ecdace2021-07-08 17:05:31 -070033import static com.android.car.watchdog.PackageInfoHandler.SHARED_PACKAGE_PREFIX;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070034import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070035
36import android.annotation.NonNull;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070037import android.annotation.UserIdInt;
38import android.app.ActivityThread;
39import android.automotive.watchdog.ResourceType;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070040import android.automotive.watchdog.internal.ApplicationCategoryType;
41import android.automotive.watchdog.internal.ComponentType;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070042import android.automotive.watchdog.internal.PackageIdentifier;
43import android.automotive.watchdog.internal.PackageIoOveruseStats;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070044import android.automotive.watchdog.internal.PackageMetadata;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070045import android.automotive.watchdog.internal.PackageResourceOveruseAction;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070046import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
47import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070048import android.car.watchdog.CarWatchdogManager;
49import android.car.watchdog.IResourceOveruseListener;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070050import android.car.watchdog.IoOveruseAlertThreshold;
51import android.car.watchdog.IoOveruseConfiguration;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070052import android.car.watchdog.IoOveruseStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070053import android.car.watchdog.PackageKillableState;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070054import android.car.watchdog.PackageKillableState.KillableState;
55import android.car.watchdog.PerStateBytes;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070056import android.car.watchdog.ResourceOveruseConfiguration;
57import android.car.watchdog.ResourceOveruseStats;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070058import android.car.watchdoglib.CarWatchdogDaemonHelper;
59import android.content.Context;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070060import android.content.pm.ApplicationInfo;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070061import android.content.pm.IPackageManager;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070062import android.content.pm.PackageInfo;
63import android.content.pm.PackageManager;
64import android.content.pm.UserInfo;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070065import android.os.Binder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070066import android.os.Handler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070067import android.os.IBinder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070068import android.os.Looper;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070069import android.os.RemoteException;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070070import android.os.SystemClock;
71import android.os.TransactionTooLargeException;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070072import android.os.UserHandle;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070073import android.os.UserManager;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070074import android.util.ArrayMap;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070075import android.util.ArraySet;
76import android.util.IndentingPrintWriter;
77import android.util.SparseArray;
78
Lakshman Annadorai016127e2021-03-18 09:11:43 -070079import com.android.internal.annotations.GuardedBy;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070080import com.android.internal.annotations.VisibleForTesting;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070081import com.android.internal.util.Preconditions;
82import com.android.server.utils.Slogf;
83
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070084import java.time.ZoneOffset;
85import java.time.ZonedDateTime;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070086import java.util.ArrayList;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070087import java.util.Collections;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070088import java.util.List;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070089import java.util.Map;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070090import java.util.Objects;
91import java.util.Set;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070092import java.util.function.BiFunction;
Lakshman Annadorai16999d22021-05-25 08:33:30 -070093import java.util.function.Consumer;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070094
95/**
96 * Handles system resource performance monitoring module.
97 */
98public final class WatchdogPerfHandler {
Lakshman Annadoraie1720472021-04-13 15:22:57 -070099 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS = "MAPS";
100 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA";
101 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN";
102
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700103 private static final long MAX_WAIT_TIME_MILLS = 3_000;
104
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700105 private final Context mContext;
106 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
107 private final PackageInfoHandler mPackageInfoHandler;
108 private final Handler mMainHandler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700109 private final Object mLock = new Object();
110 /*
111 * Cache of added resource overuse listeners by uid.
112 */
113 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700114 private final Map<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>();
115 @GuardedBy("mLock")
116 private final List<PackageResourceOveruseAction> mOveruseActionsByUserPackage =
117 new ArrayList<>();
118 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700119 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>> mOveruseListenerInfosByUid =
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700120 new SparseArray<>();
121 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700122 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>>
123 mOveruseSystemListenerInfosByUid = new SparseArray<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700124 /* Set of safe-to-kill system and vendor packages. */
125 @GuardedBy("mLock")
126 public final Set<String> mSafeToKillPackages = new ArraySet<>();
127 /* Default killable state for packages when not updated by the user. */
128 @GuardedBy("mLock")
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700129 public final Set<String> mDefaultNotKillableGenericPackages = new ArraySet<>();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700130 @GuardedBy("mLock")
131 private ZonedDateTime mLastStatsReportUTC;
132 @GuardedBy("mLock")
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700133 private List<android.automotive.watchdog.internal.ResourceOveruseConfiguration>
134 mPendingSetResourceOveruseConfigurationsRequest = null;
135 @GuardedBy("mLock")
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700136 boolean mIsConnectedToDaemon;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700137
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700138 public WatchdogPerfHandler(Context context, CarWatchdogDaemonHelper daemonHelper,
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700139 PackageInfoHandler packageInfoHandler) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700140 mContext = context;
141 mCarWatchdogDaemonHelper = daemonHelper;
142 mPackageInfoHandler = packageInfoHandler;
143 mMainHandler = new Handler(Looper.getMainLooper());
144 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700145 }
146
147 /** Initializes the handler. */
148 public void init() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700149 /*
150 * TODO(b/183947162): Opt-in to receive package change broadcast and handle package enabled
151 * state changes.
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700152 * TODO(b/192294393): Persist in-memory data: Read the current day's I/O overuse stats from
153 * database.
154 * TODO(b/192665269): Fetch the safe-to-kill from daemon on initialization and update
155 * mSafeToKillPackages.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700156 */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700157 synchronized (mLock) {
158 checkAndHandleDateChangeLocked();
159 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700160 if (DEBUG) {
161 Slogf.d(TAG, "WatchdogPerfHandler is initialized");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700162 }
163 }
164
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700165 /** Releases the handler */
166 public void release() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700167 /* TODO(b/192294393): Write daily usage to SQLite DB storage. */
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700168 if (DEBUG) {
169 Slogf.d(TAG, "WatchdogPerfHandler is released");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700170 }
171 }
172
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700173 /** Dumps its state. */
174 public void dump(IndentingPrintWriter writer) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700175 /*
176 * TODO(b/183436216): Implement this method.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700177 */
178 }
179
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700180 /** Retries any pending requests on re-connecting to the daemon */
181 public void onDaemonConnectionChange(boolean isConnected) {
182 synchronized (mLock) {
183 mIsConnectedToDaemon = isConnected;
184 }
185 if (isConnected) {
186 /*
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700187 * Retry pending set resource overuse configuration request before processing any new
188 * set/get requests. Thus notify the waiting requests only after the retry completes.
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700189 */
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700190 retryPendingSetResourceOveruseConfigurations();
191 }
192 synchronized (mLock) {
193 mLock.notifyAll();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700194 }
195 }
196
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700197 /** Returns resource overuse stats for the calling package. */
198 @NonNull
199 public ResourceOveruseStats getResourceOveruseStats(
200 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
201 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
202 Preconditions.checkArgument((resourceOveruseFlag > 0),
203 "Must provide valid resource overuse flag");
204 Preconditions.checkArgument((maxStatsPeriod > 0),
205 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700206 // When more resource stats are added, make this as optional.
207 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
208 "Must provide resource I/O overuse flag");
209 int callingUid = Binder.getCallingUid();
210 int callingUserId = UserHandle.getUserId(callingUid);
211 UserHandle callingUserHandle = UserHandle.of(callingUserId);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700212 String genericPackageName =
213 mPackageInfoHandler.getNamesForUids(new int[]{callingUid})
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700214 .get(callingUid, null);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700215 if (genericPackageName == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700216 Slogf.w(TAG, "Failed to fetch package info for uid %d", callingUid);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700217 return new ResourceOveruseStats.Builder("", callingUserHandle).build();
218 }
219 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700220 new ResourceOveruseStats.Builder(genericPackageName, callingUserHandle);
221 statsBuilder.setIoOveruseStats(getIoOveruseStats(callingUserId, genericPackageName,
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700222 /* minimumBytesWritten= */ 0, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700223 if (DEBUG) {
224 Slogf.d(TAG, "Returning all resource overuse stats for calling uid %d [user %d and "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700225 + "package '%s']", callingUid, callingUserId, genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700226 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700227 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700228 }
229
230 /** Returns resource overuse stats for all packages. */
231 @NonNull
232 public List<ResourceOveruseStats> getAllResourceOveruseStats(
233 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
234 @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag,
235 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
236 Preconditions.checkArgument((resourceOveruseFlag > 0),
237 "Must provide valid resource overuse flag");
238 Preconditions.checkArgument((maxStatsPeriod > 0),
239 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700240 // When more resource types are added, make this as optional.
241 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
242 "Must provide resource I/O overuse flag");
243 long minimumBytesWritten = getMinimumBytesWritten(minimumStatsFlag);
244 List<ResourceOveruseStats> allStats = new ArrayList<>();
245 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
246 ResourceOveruseStats.Builder statsBuilder = usage.getResourceOveruseStatsBuilder();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700247 IoOveruseStats ioOveruseStats =
248 getIoOveruseStats(usage.userId, usage.genericPackageName, minimumBytesWritten,
249 maxStatsPeriod);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700250 if (ioOveruseStats == null) {
251 continue;
252 }
253 allStats.add(statsBuilder.setIoOveruseStats(ioOveruseStats).build());
254 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700255 if (DEBUG) {
256 Slogf.d(TAG, "Returning all resource overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700257 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700258 return allStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700259 }
260
261 /** Returns resource overuse stats for the specified user package. */
262 @NonNull
263 public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
264 @NonNull String packageName, @NonNull UserHandle userHandle,
265 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
266 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
267 Objects.requireNonNull(packageName, "Package name must be non-null");
268 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700269 Preconditions.checkArgument((userHandle != UserHandle.ALL),
270 "Must provide the user handle for a specific user");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700271 Preconditions.checkArgument((resourceOveruseFlag > 0),
272 "Must provide valid resource overuse flag");
273 Preconditions.checkArgument((maxStatsPeriod > 0),
274 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700275 // When more resource types are added, make this as optional.
276 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
277 "Must provide resource I/O overuse flag");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700278 String genericPackageName =
279 mPackageInfoHandler.getNameForUserPackage(packageName, userHandle.getIdentifier());
280 if (genericPackageName == null) {
281 throw new IllegalArgumentException("Package '" + packageName + "' not found");
282 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700283 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700284 new ResourceOveruseStats.Builder(genericPackageName, userHandle);
285 statsBuilder.setIoOveruseStats(
286 getIoOveruseStats(userHandle.getIdentifier(), genericPackageName,
287 /* minimumBytesWritten= */ 0, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700288 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700289 Slogf.d(TAG, "Returning resource overuse stats for user %d, package '%s', "
290 + "generic package '%s'", userHandle.getIdentifier(), packageName,
291 genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700292 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700293 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700294 }
295
296 /** Adds the resource overuse listener. */
297 public void addResourceOveruseListener(
298 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
299 @NonNull IResourceOveruseListener listener) {
300 Objects.requireNonNull(listener, "Listener must be non-null");
301 Preconditions.checkArgument((resourceOveruseFlag > 0),
302 "Must provide valid resource overuse flag");
303 synchronized (mLock) {
304 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
305 mOveruseListenerInfosByUid);
306 }
307 }
308
309 /** Removes the previously added resource overuse listener. */
310 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
311 Objects.requireNonNull(listener, "Listener must be non-null");
312 synchronized (mLock) {
313 removeResourceOveruseListenerLocked(listener, mOveruseListenerInfosByUid);
314 }
315 }
316
317 /** Adds the resource overuse system listener. */
318 public void addResourceOveruseListenerForSystem(
319 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
320 @NonNull IResourceOveruseListener listener) {
321 Objects.requireNonNull(listener, "Listener must be non-null");
322 Preconditions.checkArgument((resourceOveruseFlag > 0),
323 "Must provide valid resource overuse flag");
324 synchronized (mLock) {
325 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
326 mOveruseSystemListenerInfosByUid);
327 }
328 }
329
330 /** Removes the previously added resource overuse system listener. */
331 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
332 Objects.requireNonNull(listener, "Listener must be non-null");
333 synchronized (mLock) {
334 removeResourceOveruseListenerLocked(listener, mOveruseSystemListenerInfosByUid);
335 }
336 }
337
338 /** Sets whether or not a package is killable on resource overuse. */
339 public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
340 boolean isKillable) {
341 Objects.requireNonNull(packageName, "Package name must be non-null");
342 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700343
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700344 if (userHandle == UserHandle.ALL) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700345 setPackageKillableStateForAllUsers(packageName, isKillable);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700346 return;
347 }
348 int userId = userHandle.getIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700349 String genericPackageName = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
350 if (genericPackageName == null) {
351 throw new IllegalArgumentException("Package '" + packageName + "' not found");
352 }
353 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700354 synchronized (mLock) {
355 /*
356 * When the queried package is not cached in {@link mUsageByUserPackage}, the set API
357 * will update the killable state even when the package should never be killed.
358 * But the get API will return the correct killable state. This behavior is tolerable
359 * because in production the set API should be called only after the get API.
360 * For instance, when this case happens by mistake and the package overuses resource
361 * between the set and the get API calls, the daemon will provide correct killable
362 * state when pushing the latest stats. Ergo, the invalid killable state doesn't have
363 * any effect.
364 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700365 PackageResourceUsage usage = mUsageByUserPackage.get(key);
366 if (usage == null) {
367 usage = new PackageResourceUsage(userId, genericPackageName);
368 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700369 if (!usage.setKillableState(isKillable)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700370 Slogf.e(TAG, "User %d cannot set killable state for package '%s'",
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700371 userHandle.getIdentifier(), genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700372 throw new IllegalArgumentException("Package killable state is not updatable");
373 }
374 mUsageByUserPackage.put(key, usage);
375 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700376 if (DEBUG) {
377 Slogf.d(TAG, "Successfully set killable package state for user %d", userId);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700378 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700379 }
380
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700381 private void setPackageKillableStateForAllUsers(String packageName, boolean isKillable) {
382 UserManager userManager = UserManager.get(mContext);
383 List<UserInfo> userInfos = userManager.getAliveUsers();
384 String genericPackageName = null;
385 for (UserInfo userInfo : userInfos) {
386 String name =
387 mPackageInfoHandler.getNameForUserPackage(packageName, userInfo.id);
388 if (name == null) {
389 continue;
390 }
391 genericPackageName = name;
392 String key = getUserPackageUniqueId(userInfo.id, genericPackageName);
393 synchronized (mLock) {
394 PackageResourceUsage usage = mUsageByUserPackage.get(key);
395 if (usage == null) {
396 continue;
397 }
398 if (!usage.setKillableState(isKillable)) {
399 Slogf.e(TAG, "Cannot set killable state for package '%s'", packageName);
400 throw new IllegalArgumentException(
401 "Package killable state is not updatable");
402 }
403 }
404 }
405 if (genericPackageName != null) {
406 if (!isKillable) {
407 mDefaultNotKillableGenericPackages.add(genericPackageName);
408 } else {
409 mDefaultNotKillableGenericPackages.remove(genericPackageName);
410 }
411 }
412 if (DEBUG) {
413 Slogf.d(TAG, "Successfully set killable package state for all users");
414 }
415 }
416
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700417 /** Returns the list of package killable states on resource overuse for the user. */
418 @NonNull
419 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
420 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700421 PackageManager pm = mContext.getPackageManager();
422 if (userHandle != UserHandle.ALL) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700423 if (DEBUG) {
424 Slogf.d(TAG, "Returning all package killable states for user %d",
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700425 userHandle.getIdentifier());
426 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700427 return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
428 }
429 List<PackageKillableState> packageKillableStates = new ArrayList<>();
430 UserManager userManager = UserManager.get(mContext);
431 List<UserInfo> userInfos = userManager.getAliveUsers();
432 for (UserInfo userInfo : userInfos) {
433 packageKillableStates.addAll(getPackageKillableStatesForUserId(userInfo.id, pm));
434 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700435 if (DEBUG) {
436 Slogf.d(TAG, "Returning all package killable states for all users");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700437 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700438 return packageKillableStates;
439 }
440
441 private List<PackageKillableState> getPackageKillableStatesForUserId(int userId,
442 PackageManager pm) {
443 List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */0, userId);
444 List<PackageKillableState> states = new ArrayList<>();
445 synchronized (mLock) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700446 Map<String, List<ApplicationInfo>> applicationInfosBySharedPackage = new ArrayMap<>();
447 for (PackageInfo packageInfo : packageInfos) {
448 String genericPackageName = mPackageInfoHandler.getNameForPackage(packageInfo);
449 if (packageInfo.sharedUserId == null) {
450 int killableState = getPackageKillableStateForUserPackageLocked(
451 userId, genericPackageName,
452 mPackageInfoHandler.getComponentType(packageInfo.applicationInfo),
453 mSafeToKillPackages.contains(genericPackageName));
454 states.add(new PackageKillableState(packageInfo.packageName, userId,
455 killableState));
456 continue;
457 }
458 List<ApplicationInfo> applicationInfos =
459 applicationInfosBySharedPackage.get(genericPackageName);
460 if (applicationInfos == null) {
461 applicationInfos = new ArrayList<>();
462 }
463 applicationInfos.add(packageInfo.applicationInfo);
464 applicationInfosBySharedPackage.put(genericPackageName, applicationInfos);
465 }
466 for (Map.Entry<String, List<ApplicationInfo>> entry :
467 applicationInfosBySharedPackage.entrySet()) {
468 String genericPackageName = entry.getKey();
469 List<ApplicationInfo> applicationInfos = entry.getValue();
470 int componentType = mPackageInfoHandler.getSharedComponentType(
471 applicationInfos, genericPackageName);
472 boolean isSafeToKill = mSafeToKillPackages.contains(genericPackageName);
473 for (ApplicationInfo applicationInfo : applicationInfos) {
474 isSafeToKill = isSafeToKill
475 || mSafeToKillPackages.contains(applicationInfo.packageName);
476 }
477 int killableState = getPackageKillableStateForUserPackageLocked(
478 userId, genericPackageName, componentType, isSafeToKill);
479 for (ApplicationInfo applicationInfo : applicationInfos) {
480 states.add(new PackageKillableState(
481 applicationInfo.packageName, userId, killableState));
482 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700483 }
484 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700485 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700486 Slogf.d(TAG, "Returning the package killable states for user packages");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700487 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700488 return states;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700489 }
490
491 /** Sets the given resource overuse configurations. */
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700492 @CarWatchdogManager.ReturnCode
493 public int setResourceOveruseConfigurations(
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700494 List<ResourceOveruseConfiguration> configurations,
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700495 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
496 throws RemoteException {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700497 Objects.requireNonNull(configurations, "Configurations must be non-null");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700498 Preconditions.checkArgument((configurations.size() > 0),
499 "Must provide at least one configuration");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700500 Preconditions.checkArgument((resourceOveruseFlag > 0),
501 "Must provide valid resource overuse flag");
502 Set<Integer> seenComponentTypes = new ArraySet<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700503 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
504 new ArrayList<>();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700505 for (ResourceOveruseConfiguration config : configurations) {
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700506 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700507 * TODO(b/186119640): Make sure the validation done here matches the validation done in
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700508 * the daemon so set requests retried at a later time will complete successfully.
509 */
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700510 int componentType = config.getComponentType();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700511 if (toComponentTypeStr(componentType).equals("UNKNOWN")) {
512 throw new IllegalArgumentException("Invalid component type in the configuration");
513 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700514 if (seenComponentTypes.contains(componentType)) {
515 throw new IllegalArgumentException(
516 "Cannot provide duplicate configurations for the same component type");
517 }
518 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
519 && config.getIoOveruseConfiguration() == null) {
520 throw new IllegalArgumentException("Must provide I/O overuse configuration");
521 }
522 seenComponentTypes.add(config.getComponentType());
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700523 internalConfigs.add(toInternalResourceOveruseConfiguration(config,
524 resourceOveruseFlag));
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700525 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700526 synchronized (mLock) {
527 if (!mIsConnectedToDaemon) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700528 setPendingSetResourceOveruseConfigurationsRequestLocked(internalConfigs);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700529 return CarWatchdogManager.RETURN_CODE_SUCCESS;
530 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700531 /* Verify no pending request in progress. */
532 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700533 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700534 return setResourceOveruseConfigurationsInternal(internalConfigs,
535 /* isPendingRequest= */ false);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700536 }
537
538 /** Returns the available resource overuse configurations. */
539 @NonNull
540 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
541 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
542 Preconditions.checkArgument((resourceOveruseFlag > 0),
543 "Must provide valid resource overuse flag");
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700544 if (!isConnectedToDaemon()) {
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700545 throw new IllegalStateException("Car watchdog daemon is not connected");
546 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700547 synchronized (mLock) {
548 /* Verify no pending request in progress. */
549 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
550 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700551 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
552 new ArrayList<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700553 try {
554 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
555 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700556 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700557 throw new IllegalStateException(e);
558 }
559 List<ResourceOveruseConfiguration> configs = new ArrayList<>();
560 for (android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig
561 : internalConfigs) {
562 configs.add(toResourceOveruseConfiguration(internalConfig, resourceOveruseFlag));
563 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700564 if (DEBUG) {
565 Slogf.d(TAG, "Returning the resource overuse configuration");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700566 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700567 return configs;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700568 }
569
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700570 /** Processes the latest I/O overuse stats */
571 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
572 int[] uids = new int[packageIoOveruseStats.size()];
573 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
574 uids[i] = packageIoOveruseStats.get(i).uid;
575 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700576 SparseArray<String> genericPackageNamesByUid = mPackageInfoHandler.getNamesForUids(uids);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700577 synchronized (mLock) {
578 checkAndHandleDateChangeLocked();
579 for (PackageIoOveruseStats stats : packageIoOveruseStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700580 String genericPackageName = genericPackageNamesByUid.get(stats.uid);
581 if (genericPackageName == null) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700582 continue;
583 }
584 int userId = UserHandle.getUserId(stats.uid);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700585 PackageResourceUsage usage = cacheAndFetchUsageLocked(userId, genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700586 stats.ioOveruseStats);
587 if (stats.shouldNotify) {
588 /*
589 * Packages that exceed the warn threshold percentage should be notified as well
590 * and only the daemon is aware of such packages. Thus the flag is used to
591 * indicate which packages should be notified.
592 */
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700593 ResourceOveruseStats resourceOveruseStats =
594 usage.getResourceOveruseStatsBuilder().setIoOveruseStats(
595 usage.getIoOveruseStats()).build();
596 notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700597 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700598
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700599 if (!usage.ioUsage.exceedsThreshold()) {
600 continue;
601 }
602 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
603 overuseAction.packageIdentifier = new PackageIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700604 overuseAction.packageIdentifier.name = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700605 overuseAction.packageIdentifier.uid = stats.uid;
606 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
607 overuseAction.resourceOveruseActionType = NOT_KILLED;
608 /*
609 * No action required on I/O overuse on one of the following cases:
610 * #1 The package is not safe to kill as it is critical for system stability.
611 * #2 The package has no recurring overuse behavior and the user opted to not
612 * kill the package so honor the user's decision.
613 */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700614 int killableState = usage.getKillableState();
615 if (killableState == KILLABLE_STATE_NEVER) {
616 mOveruseActionsByUserPackage.add(overuseAction);
617 continue;
618 }
619 boolean hasRecurringOveruse = isRecurringOveruseLocked(usage);
620 if (!hasRecurringOveruse && killableState == KILLABLE_STATE_NO) {
621 overuseAction.resourceOveruseActionType = NOT_KILLED_USER_OPTED;
622 mOveruseActionsByUserPackage.add(overuseAction);
623 continue;
624 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700625 IPackageManager packageManager = ActivityThread.getPackageManager();
626 List<String> packages = Collections.singletonList(genericPackageName);
627 if (usage.isSharedPackage()) {
628 packages = mPackageInfoHandler.getPackagesForUid(
629 stats.uid, genericPackageName);
630 }
631 for (String pkg : packages) {
632 try {
633 int oldEnabledState = -1;
634 if (!hasRecurringOveruse) {
635 oldEnabledState = packageManager.getApplicationEnabledSetting(
636 pkg, userId);
637 if (oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED
638 || oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED_USER
639 || oldEnabledState
640 == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
641 continue;
642 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700643 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700644 packageManager.setApplicationEnabledSetting(pkg,
645 COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId,
646 mContext.getPackageName());
647 overuseAction.resourceOveruseActionType = hasRecurringOveruse
648 ? KILLED_RECURRING_OVERUSE : KILLED;
649 if (oldEnabledState != -1) {
650 usage.oldEnabledStateByPackage.put(pkg, oldEnabledState);
651 }
652 } catch (RemoteException e) {
653 Slogf.e(TAG, "Failed to disable application for user %d, package '%s'",
654 userId, pkg);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700655 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700656 }
657 mOveruseActionsByUserPackage.add(overuseAction);
658 }
659 if (!mOveruseActionsByUserPackage.isEmpty()) {
660 mMainHandler.sendMessage(obtainMessage(
661 WatchdogPerfHandler::notifyActionsTakenOnOveruse, this));
662 }
663 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700664 if (DEBUG) {
665 Slogf.d(TAG, "Processed latest I/O overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700666 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700667 }
668
669 /** Notify daemon about the actions take on resource overuse */
670 public void notifyActionsTakenOnOveruse() {
671 List<PackageResourceOveruseAction> actions;
672 synchronized (mLock) {
673 if (mOveruseActionsByUserPackage.isEmpty()) {
674 return;
675 }
676 actions = new ArrayList<>(mOveruseActionsByUserPackage);
677 mOveruseActionsByUserPackage.clear();
678 }
679 try {
680 mCarWatchdogDaemonHelper.actionTakenOnResourceOveruse(actions);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700681 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700682 Slogf.w(TAG, e, "Failed to notify car watchdog daemon of actions taken on resource "
683 + "overuse");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700684 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700685 if (DEBUG) {
686 Slogf.d(TAG, "Notified car watchdog daemon of actions taken on resource overuse");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700687 }
688 }
689
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700690 /** Resets the resource overuse stats for the given generic package names. */
691 public void resetResourceOveruseStats(Set<String> genericPackageNames) {
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700692 synchronized (mLock) {
693 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700694 if (genericPackageNames.contains(usage.genericPackageName)) {
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700695 usage.resetStats();
696 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700697 * TODO(b/192294393): When the stats are persisted in local DB, reset the stats
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700698 * for this package from local DB.
699 */
700 }
701 }
702 }
703 }
704
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700705 @GuardedBy("mLock")
706 private int getPackageKillableStateForUserPackageLocked(
707 int userId, String genericPackageName, int componentType, boolean isSafeToKill) {
708 String key = getUserPackageUniqueId(userId, genericPackageName);
709 PackageResourceUsage usage = mUsageByUserPackage.get(key);
710 if (usage == null) {
711 usage = new PackageResourceUsage(userId, genericPackageName);
712 }
713 int killableState = usage.syncAndFetchKillableStateLocked(componentType, isSafeToKill);
714 mUsageByUserPackage.put(key, usage);
715 return killableState;
716 }
717
718 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700719 private void notifyResourceOveruseStatsLocked(int uid,
720 ResourceOveruseStats resourceOveruseStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700721 String genericPackageName = resourceOveruseStats.getPackageName();
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700722 ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid);
723 if (listenerInfos != null) {
724 for (ResourceOveruseListenerInfo listenerInfo : listenerInfos) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700725 listenerInfo.notifyListener(FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700726 resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700727 }
728 }
729 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700730 ArrayList<ResourceOveruseListenerInfo> systemListenerInfos =
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700731 mOveruseSystemListenerInfosByUid.valueAt(i);
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700732 for (ResourceOveruseListenerInfo listenerInfo : systemListenerInfos) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700733 listenerInfo.notifyListener(FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700734 resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700735 }
736 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700737 if (DEBUG) {
738 Slogf.d(TAG, "Notified resource overuse stats to listening applications");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700739 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700740 }
741
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700742 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700743 private void checkAndHandleDateChangeLocked() {
744 ZonedDateTime previousUTC = mLastStatsReportUTC;
745 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
746 if (mLastStatsReportUTC.getDayOfYear() == previousUTC.getDayOfYear()
747 && mLastStatsReportUTC.getYear() == previousUTC.getYear()) {
748 return;
749 }
750 for (PackageResourceUsage usage : mUsageByUserPackage.values()) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700751 // Forgive the daily disabled package on date change.
752 for (Map.Entry<String, Integer> entry : usage.oldEnabledStateByPackage.entrySet()) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700753 try {
754 IPackageManager packageManager = ActivityThread.getPackageManager();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700755 if (packageManager.getApplicationEnabledSetting(entry.getKey(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700756 usage.userId)
757 != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
758 continue;
759 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700760 packageManager.setApplicationEnabledSetting(entry.getKey(),
761 entry.getValue(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700762 /* flags= */ 0, usage.userId, mContext.getPackageName());
763 } catch (RemoteException e) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700764 Slogf.e(TAG,
765 "Failed to reset enabled setting for disabled package '%s', user '%d'",
766 usage.genericPackageName, usage.userId);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700767 }
768 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700769 /* TODO(b/192294393): Stash the old usage into SQLite DB storage. */
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700770 usage.resetStats();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700771 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700772 if (DEBUG) {
773 Slogf.d(TAG, "Handled date change successfully");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700774 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700775 }
776
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700777 @GuardedBy("mLock")
778 private PackageResourceUsage cacheAndFetchUsageLocked(
779 @UserIdInt int userId, String genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700780 android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700781 String key = getUserPackageUniqueId(userId, genericPackageName);
782 PackageResourceUsage usage = mUsageByUserPackage.get(key);
783 if (usage == null) {
784 usage = new PackageResourceUsage(userId, genericPackageName);
785 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700786 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700787 mUsageByUserPackage.put(key, usage);
788 return usage;
789 }
790
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700791 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700792 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
793 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700794 * TODO(b/192294393): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700795 * has recurring I/O overuse behavior.
796 */
797 return false;
798 }
799
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700800 private IoOveruseStats getIoOveruseStats(int userId, String genericPackageName,
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700801 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700802 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700803 PackageResourceUsage usage = mUsageByUserPackage.get(key);
804 if (usage == null) {
805 return null;
806 }
807 IoOveruseStats stats = usage.getIoOveruseStats();
808 long totalBytesWritten = stats != null ? stats.getTotalBytesWritten() : 0;
809 /*
810 * TODO(b/185431129): When maxStatsPeriod > current day, populate the historical stats
811 * from the local database. Also handle the case where the package doesn't have current
812 * day stats but has historical stats.
813 */
814 if (totalBytesWritten < minimumBytesWritten) {
815 return null;
816 }
817 return stats;
818 }
819
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700820 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700821 private void addResourceOveruseListenerLocked(
822 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
823 @NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700824 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700825 int callingPid = Binder.getCallingPid();
826 int callingUid = Binder.getCallingUid();
827 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
828 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
829 "resource overuse listener";
830
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700831 IBinder binder = listener.asBinder();
832 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
833 if (listenerInfos == null) {
834 listenerInfos = new ArrayList<>();
835 listenerInfosByUid.put(callingUid, listenerInfos);
836 }
837 for (ResourceOveruseListenerInfo listenerInfo : listenerInfos) {
838 if (listenerInfo.listener.asBinder() == binder) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700839 throw new IllegalStateException(
840 "Cannot add " + listenerType + " as it is already added");
841 }
842 }
843
844 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
845 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
846 try {
847 listenerInfo.linkToDeath();
848 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700849 Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700850 return;
851 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700852 listenerInfos.add(listenerInfo);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700853 if (DEBUG) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700854 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
855 callingPid, callingUid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700856 }
857 }
858
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700859 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700860 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700861 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700862 int callingUid = Binder.getCallingUid();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700863 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
864 ? "resource overuse system listener" : "resource overuse listener";
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700865 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
866 if (listenerInfos == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700867 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700868 return;
869 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700870 IBinder binder = listener.asBinder();
871 ResourceOveruseListenerInfo cachedListenerInfo = null;
872 for (ResourceOveruseListenerInfo listenerInfo : listenerInfos) {
873 if (listenerInfo.listener.asBinder() == binder) {
874 cachedListenerInfo = listenerInfo;
875 break;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700876 }
877 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700878 if (cachedListenerInfo == null) {
879 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
880 return;
881 }
882 cachedListenerInfo.unlinkToDeath();
883 listenerInfos.remove(cachedListenerInfo);
884 if (listenerInfos.isEmpty()) {
885 listenerInfosByUid.remove(callingUid);
886 }
887 if (DEBUG) {
888 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
889 cachedListenerInfo.pid, cachedListenerInfo.uid);
890 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700891 }
892
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700893 @GuardedBy("mLock")
894 private void setPendingSetResourceOveruseConfigurationsRequestLocked(
895 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) {
896 if (mPendingSetResourceOveruseConfigurationsRequest != null) {
897 if (mPendingSetResourceOveruseConfigurationsRequest == configs) {
898 return;
899 }
900 throw new IllegalStateException(
901 "Pending setResourceOveruseConfigurations request in progress");
902 }
903 mPendingSetResourceOveruseConfigurationsRequest = configs;
904 }
905
906 private void retryPendingSetResourceOveruseConfigurations() {
907 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700908 synchronized (mLock) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700909 if (mPendingSetResourceOveruseConfigurationsRequest == null) {
910 return;
911 }
912 configs = mPendingSetResourceOveruseConfigurationsRequest;
913 }
914 try {
915 int result = setResourceOveruseConfigurationsInternal(configs,
916 /* isPendingRequest= */ true);
917 if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) {
918 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code "
919 + "%d", result);
920 }
921 } catch (Exception e) {
922 Slogf.e(TAG, e, "Exception on set pending resource overuse configurations");
923 }
924 }
925
926 private int setResourceOveruseConfigurationsInternal(
927 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs,
928 boolean isPendingRequest) throws RemoteException {
929 boolean doClearPendingRequest = isPendingRequest;
930 try {
931 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs);
932 } catch (RemoteException e) {
933 if (e instanceof TransactionTooLargeException) {
934 throw e;
935 }
936 Slogf.e(TAG, e, "Remote exception on set resource overuse configuration");
937 synchronized (mLock) {
938 setPendingSetResourceOveruseConfigurationsRequestLocked(configs);
939 }
940 doClearPendingRequest = false;
941 return CarWatchdogManager.RETURN_CODE_SUCCESS;
942 } finally {
943 if (doClearPendingRequest) {
944 synchronized (mLock) {
945 mPendingSetResourceOveruseConfigurationsRequest = null;
946 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700947 }
948 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700949 /* TODO(b/192665269): Fetch safe-to-kill list from daemon and update mSafeToKillPackages. */
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700950 if (DEBUG) {
951 Slogf.d(TAG, "Set the resource overuse configuration successfully");
952 }
953 return CarWatchdogManager.RETURN_CODE_SUCCESS;
954 }
955
956 private boolean isConnectedToDaemon() {
957 synchronized (mLock) {
958 long startTimeMillis = SystemClock.uptimeMillis();
959 long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
960 while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) {
961 try {
962 mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis);
963 } catch (InterruptedException e) {
964 Thread.currentThread().interrupt();
965 continue;
966 } finally {
967 sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
968 }
969 break;
970 }
971 return mIsConnectedToDaemon;
972 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700973 }
974
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700975 private static String getUserPackageUniqueId(int userId, String genericPackageName) {
976 return String.valueOf(userId) + ":" + genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700977 }
978
979 @VisibleForTesting
980 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
981 android.automotive.watchdog.IoOveruseStats internalStats) {
982 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
983 internalStats.startTime, internalStats.durationInSeconds);
984 statsBuilder.setRemainingWriteBytes(
985 toPerStateBytes(internalStats.remainingWriteBytes));
986 statsBuilder.setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes));
987 statsBuilder.setTotalOveruses(internalStats.totalOveruses);
988 return statsBuilder;
989 }
990
991 private static PerStateBytes toPerStateBytes(
992 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700993 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700994 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700995 }
996
997 private static long totalPerStateBytes(
998 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
999 BiFunction<Long, Long, Long> sum = (l, r) -> {
1000 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
1001 };
1002 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
1003 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
1004 }
1005
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001006 private static long getMinimumBytesWritten(
1007 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
1008 switch (minimumStatsIoFlag) {
1009 case 0:
1010 return 0;
1011 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
1012 return 1024 * 1024;
1013 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
1014 return 100 * 1024 * 1024;
1015 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
1016 return 1024 * 1024 * 1024;
1017 default:
1018 throw new IllegalArgumentException(
1019 "Must provide valid minimum stats flag for I/O resource");
1020 }
1021 }
1022
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001023 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
1024 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
1025 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1026 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
1027 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
1028 internalConfig.componentType = config.getComponentType();
1029 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
1030 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
1031 internalConfig.packageMetadata = new ArrayList<>();
1032 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
1033 if (entry.getKey().isEmpty()) {
1034 continue;
1035 }
1036 PackageMetadata metadata = new PackageMetadata();
1037 metadata.packageName = entry.getKey();
1038 switch(entry.getValue()) {
1039 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1040 metadata.appCategoryType = ApplicationCategoryType.MAPS;
1041 break;
1042 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1043 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
1044 break;
1045 default:
1046 continue;
1047 }
1048 internalConfig.packageMetadata.add(metadata);
1049 }
1050 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
1051 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1052 && config.getIoOveruseConfiguration() != null) {
1053 internalConfig.resourceSpecificConfigurations.add(
1054 toResourceSpecificConfiguration(config.getComponentType(),
1055 config.getIoOveruseConfiguration()));
1056 }
1057 return internalConfig;
1058 }
1059
1060 private static ResourceSpecificConfiguration
1061 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
1062 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
1063 new android.automotive.watchdog.internal.IoOveruseConfiguration();
1064 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
1065 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
1066 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
1067 config.getPackageSpecificThresholds());
1068 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
1069 config.getAppCategorySpecificThresholds());
1070 for (PerStateIoOveruseThreshold threshold : internalConfig.categorySpecificThresholds) {
1071 switch(threshold.name) {
1072 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1073 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
1074 break;
1075 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1076 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
1077 break;
1078 default:
1079 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
1080 }
1081 }
1082 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
1083 config.getSystemWideThresholds());
1084
1085 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
1086 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
1087 return resourceSpecificConfig;
1088 }
1089
1090 @VisibleForTesting
1091 static String toComponentTypeStr(int componentType) {
1092 switch(componentType) {
1093 case ComponentType.SYSTEM:
1094 return "SYSTEM";
1095 case ComponentType.VENDOR:
1096 return "VENDOR";
1097 case ComponentType.THIRD_PARTY:
1098 return "THIRD_PARTY";
1099 default:
1100 return "UNKNOWN";
1101 }
1102 }
1103
1104 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
1105 Map<String, PerStateBytes> thresholds) {
1106 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
1107 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
1108 if (!entry.getKey().isEmpty()) {
1109 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
1110 entry.getValue()));
1111 }
1112 }
1113 return internalThresholds;
1114 }
1115
1116 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
1117 PerStateBytes perStateBytes) {
1118 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
1119 threshold.name = name;
1120 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
1121 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
1122 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
1123 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
1124 return threshold;
1125 }
1126
1127 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
1128 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
1129 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
1130 new ArrayList<>();
1131 for (IoOveruseAlertThreshold threshold : thresholds) {
1132 if (threshold.getDurationInSeconds() == 0
1133 || threshold.getWrittenBytesPerSecond() == 0) {
1134 continue;
1135 }
1136 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
1137 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
1138 internalThreshold.durationInSeconds = threshold.getDurationInSeconds();
1139 internalThreshold.writtenBytesPerSecond = threshold.getWrittenBytesPerSecond();
1140 internalThresholds.add(internalThreshold);
1141 }
1142 return internalThresholds;
1143 }
1144
1145 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
1146 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
1147 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1148 Map<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
1149 for (PackageMetadata metadata : internalConfig.packageMetadata) {
1150 String categoryTypeStr;
1151 switch (metadata.appCategoryType) {
1152 case ApplicationCategoryType.MAPS:
1153 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
1154 break;
1155 case ApplicationCategoryType.MEDIA:
1156 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
1157 break;
1158 default:
1159 continue;
1160 }
1161 packagesToAppCategoryTypes.put(metadata.packageName, categoryTypeStr);
1162 }
1163 ResourceOveruseConfiguration.Builder configBuilder =
1164 new ResourceOveruseConfiguration.Builder(
1165 internalConfig.componentType,
1166 internalConfig.safeToKillPackages,
1167 internalConfig.vendorPackagePrefixes,
1168 packagesToAppCategoryTypes);
1169 for (ResourceSpecificConfiguration resourceSpecificConfig :
1170 internalConfig.resourceSpecificConfigurations) {
1171 if (resourceSpecificConfig.getTag()
1172 == ResourceSpecificConfiguration.ioOveruseConfiguration
1173 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
1174 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
1175 resourceSpecificConfig.getIoOveruseConfiguration()));
1176 }
1177 }
1178 return configBuilder.build();
1179 }
1180
1181 private static IoOveruseConfiguration toIoOveruseConfiguration(
1182 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
1183 PerStateBytes componentLevelThresholds =
1184 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
1185 Map<String, PerStateBytes> packageSpecificThresholds =
1186 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
1187 Map<String, PerStateBytes> appCategorySpecificThresholds =
1188 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
1189 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
1190 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
1191 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
1192 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
1193 List<IoOveruseAlertThreshold> systemWideThresholds =
1194 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
1195
1196 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1197 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1198 systemWideThresholds);
1199 return configBuilder.build();
1200 }
1201
1202 private static Map<String, PerStateBytes> toPerStateBytesMap(
1203 List<PerStateIoOveruseThreshold> thresholds) {
1204 Map<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1205 for (PerStateIoOveruseThreshold threshold : thresholds) {
1206 thresholdsMap.put(threshold.name, toPerStateBytes(threshold.perStateWriteBytes));
1207 }
1208 return thresholdsMap;
1209 }
1210
1211 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1212 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1213 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
1214 for (android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold
1215 : internalThresholds) {
1216 thresholds.add(new IoOveruseAlertThreshold(internalThreshold.durationInSeconds,
1217 internalThreshold.writtenBytesPerSecond));
1218 }
1219 return thresholds;
1220 }
1221
1222 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1223 PerStateBytes perStateBytes = map.get(oldKey);
1224 if (perStateBytes != null) {
1225 map.put(newKey, perStateBytes);
1226 map.remove(oldKey);
1227 }
1228 }
1229
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001230 private final class PackageResourceUsage {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001231 public final String genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001232 public @UserIdInt final int userId;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001233 public final PackageIoUsage ioUsage = new PackageIoUsage();
1234 public final Map<String, Integer> oldEnabledStateByPackage = new ArrayMap<>();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001235
1236 private @KillableState int mKillableState;
1237
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001238 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001239 PackageResourceUsage(@UserIdInt int userId, String genericPackageName) {
1240 this.genericPackageName = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001241 this.userId = userId;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001242 this.mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001243 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001244 }
1245
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001246 public boolean isSharedPackage() {
1247 return this.genericPackageName.startsWith(SHARED_PACKAGE_PREFIX);
1248 }
1249
1250 @GuardedBy("mLock")
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001251 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001252 if (!internalStats.killableOnOveruse) {
1253 /*
1254 * Killable value specified in the internal stats is provided by the native daemon.
1255 * This value reflects whether or not an application is safe-to-kill on overuse.
1256 * This setting is from the I/O overuse configuration specified by the system and
1257 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1258 * specify the application is not killable, the application is not safe-to-kill.
1259 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001260 mKillableState = KILLABLE_STATE_NEVER;
1261 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1262 /*
1263 * This case happens when a previously unsafe to kill system/vendor package was
1264 * recently marked as safe-to-kill so update the old state to the default value.
1265 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001266 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001267 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001268 }
1269 ioUsage.update(internalStats);
1270 }
1271
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001272 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001273 return new ResourceOveruseStats.Builder(genericPackageName, UserHandle.of(userId));
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001274 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001275
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001276 public IoOveruseStats getIoOveruseStats() {
1277 if (!ioUsage.hasUsage()) {
1278 return null;
1279 }
1280 return ioUsage.getStatsBuilder().setKillableOnOveruse(
1281 mKillableState != KILLABLE_STATE_NEVER).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001282 }
1283
1284 public @KillableState int getKillableState() {
1285 return mKillableState;
1286 }
1287
1288 public boolean setKillableState(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001289 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001290 return false;
1291 }
1292 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1293 return true;
1294 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001295
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001296 @GuardedBy("mLock")
1297 public int syncAndFetchKillableStateLocked(int myComponentType, boolean isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001298 /*
1299 * The killable state goes out-of-sync:
1300 * 1. When the on-device safe-to-kill list is recently updated and the user package
1301 * didn't have any resource usage so the native daemon didn't update the killable state.
1302 * 2. When a package has no resource usage and is initialized outside of processing the
1303 * latest resource usage stats.
1304 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001305 if (myComponentType != ComponentType.THIRD_PARTY && !isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001306 mKillableState = KILLABLE_STATE_NEVER;
1307 } else if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001308 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001309 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1310 }
1311 return mKillableState;
1312 }
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001313
1314 public void resetStats() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001315 oldEnabledStateByPackage.clear();
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001316 ioUsage.resetStats();
1317 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001318 }
1319
1320 private static final class PackageIoUsage {
1321 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1322 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
1323 private long mTotalTimesKilled;
1324
1325 PackageIoUsage() {
1326 mTotalTimesKilled = 0;
1327 }
1328
1329 public boolean hasUsage() {
1330 return mIoOveruseStats != null;
1331 }
1332
1333 public void update(android.automotive.watchdog.IoOveruseStats internalStats) {
1334 mIoOveruseStats = internalStats;
1335 if (exceedsThreshold()) {
1336 /*
1337 * Forgive written bytes on overuse as the package is either forgiven or killed on
1338 * overuse. When the package is killed, the user may opt to open the corresponding
1339 * app and the package should be forgiven anyways.
1340 * NOTE: If this logic is updated, update the daemon side logic as well.
1341 */
1342 mForgivenWriteBytes = internalStats.writtenBytes;
1343 }
1344 }
1345
1346 public IoOveruseStats.Builder getStatsBuilder() {
1347 IoOveruseStats.Builder statsBuilder = toIoOveruseStatsBuilder(mIoOveruseStats);
1348 statsBuilder.setTotalTimesKilled(mTotalTimesKilled);
1349 return statsBuilder;
1350 }
1351
1352 public boolean exceedsThreshold() {
1353 if (!hasUsage()) {
1354 return false;
1355 }
1356 android.automotive.watchdog.PerStateBytes remaining =
1357 mIoOveruseStats.remainingWriteBytes;
1358 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1359 || remaining.garageModeBytes == 0;
1360 }
1361
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001362 public void resetStats() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001363 mIoOveruseStats = null;
1364 mForgivenWriteBytes = null;
1365 mTotalTimesKilled = 0;
1366 }
1367 }
1368
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001369 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1370 public final IResourceOveruseListener listener;
1371 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1372 public final int pid;
1373 public final int uid;
1374 public final boolean isListenerForSystem;
1375
1376 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1377 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1378 boolean isListenerForSystem) {
1379 this.listener = listener;
1380 this.flag = flag;
1381 this.pid = pid;
1382 this.uid = uid;
1383 this.isListenerForSystem = isListenerForSystem;
1384 }
1385
1386 @Override
1387 public void binderDied() {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001388 Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001389 isListenerForSystem ? " for system" : "", pid);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001390 Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo =
1391 listenerInfosByUid -> {
1392 ArrayList<ResourceOveruseListenerInfo> listenerInfos =
1393 listenerInfosByUid.get(uid);
1394 if (listenerInfos == null) {
1395 return;
1396 }
1397 listenerInfos.remove(this);
1398 if (listenerInfos.isEmpty()) {
1399 listenerInfosByUid.remove(uid);
1400 }
1401 };
1402 if (isListenerForSystem) {
1403 removeListenerInfo.accept(mOveruseSystemListenerInfosByUid);
1404 } else {
1405 removeListenerInfo.accept(mOveruseListenerInfosByUid);
1406 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001407 unlinkToDeath();
1408 }
1409
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001410 public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001411 int overusingUid, String overusingGenericPackageName,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001412 ResourceOveruseStats resourceOveruseStats) {
1413 if ((flag & resourceType) == 0) {
1414 return;
1415 }
1416 try {
1417 listener.onOveruse(resourceOveruseStats);
1418 } catch (RemoteException e) {
1419 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001420 + "package(uid %d, generic package name '%s'): %s",
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001421 (isListenerForSystem ? "system listener" : "listener"), uid, pid,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001422 overusingUid, overusingGenericPackageName, e);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001423 }
1424 }
1425
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001426 private void linkToDeath() throws RemoteException {
1427 listener.asBinder().linkToDeath(this, 0);
1428 }
1429
1430 private void unlinkToDeath() {
1431 listener.asBinder().unlinkToDeath(this, 0);
1432 }
1433 }
1434}