blob: afbb0bbf28c0a3a041b3b3e1379de79f65cb01ef [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 Annadorai36ec22b2021-07-14 16:51:40 -0700114 private final ArrayMap<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700115 @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")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700126 public final ArraySet<String> mSafeToKillPackages = new ArraySet<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700127 /* Default killable state for packages when not updated by the user. */
128 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700129 public final ArraySet<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);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700221 statsBuilder.setIoOveruseStats(
222 getIoOveruseStatsForPeriod(callingUserId, genericPackageName, 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<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700245 synchronized (mLock) {
246 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
247 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
248 ResourceOveruseStats.Builder statsBuilder = usage.getResourceOveruseStatsBuilder();
249 IoOveruseStats ioOveruseStats =
250 getIoOveruseStatsLocked(usage, minimumBytesWritten, maxStatsPeriod);
251 if (ioOveruseStats == null) {
252 continue;
253 }
254 allStats.add(statsBuilder.setIoOveruseStats(ioOveruseStats).build());
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700255 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700256 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700257 if (DEBUG) {
258 Slogf.d(TAG, "Returning all resource overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700259 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700260 return allStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700261 }
262
263 /** Returns resource overuse stats for the specified user package. */
264 @NonNull
265 public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
266 @NonNull String packageName, @NonNull UserHandle userHandle,
267 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
268 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
269 Objects.requireNonNull(packageName, "Package name must be non-null");
270 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700271 Preconditions.checkArgument(!userHandle.equals(UserHandle.ALL),
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700272 "Must provide the user handle for a specific user");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700273 Preconditions.checkArgument((resourceOveruseFlag > 0),
274 "Must provide valid resource overuse flag");
275 Preconditions.checkArgument((maxStatsPeriod > 0),
276 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700277 // When more resource types are added, make this as optional.
278 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
279 "Must provide resource I/O overuse flag");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700280 String genericPackageName =
281 mPackageInfoHandler.getNameForUserPackage(packageName, userHandle.getIdentifier());
282 if (genericPackageName == null) {
283 throw new IllegalArgumentException("Package '" + packageName + "' not found");
284 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700285 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700286 new ResourceOveruseStats.Builder(genericPackageName, userHandle);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700287 statsBuilder.setIoOveruseStats(getIoOveruseStatsForPeriod(userHandle.getIdentifier(),
288 genericPackageName, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700289 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700290 Slogf.d(TAG, "Returning resource overuse stats for user %d, package '%s', "
291 + "generic package '%s'", userHandle.getIdentifier(), packageName,
292 genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700293 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700294 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700295 }
296
297 /** Adds the resource overuse listener. */
298 public void addResourceOveruseListener(
299 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
300 @NonNull IResourceOveruseListener listener) {
301 Objects.requireNonNull(listener, "Listener must be non-null");
302 Preconditions.checkArgument((resourceOveruseFlag > 0),
303 "Must provide valid resource overuse flag");
304 synchronized (mLock) {
305 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
306 mOveruseListenerInfosByUid);
307 }
308 }
309
310 /** Removes the previously added resource overuse listener. */
311 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
312 Objects.requireNonNull(listener, "Listener must be non-null");
313 synchronized (mLock) {
314 removeResourceOveruseListenerLocked(listener, mOveruseListenerInfosByUid);
315 }
316 }
317
318 /** Adds the resource overuse system listener. */
319 public void addResourceOveruseListenerForSystem(
320 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
321 @NonNull IResourceOveruseListener listener) {
322 Objects.requireNonNull(listener, "Listener must be non-null");
323 Preconditions.checkArgument((resourceOveruseFlag > 0),
324 "Must provide valid resource overuse flag");
325 synchronized (mLock) {
326 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
327 mOveruseSystemListenerInfosByUid);
328 }
329 }
330
331 /** Removes the previously added resource overuse system listener. */
332 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
333 Objects.requireNonNull(listener, "Listener must be non-null");
334 synchronized (mLock) {
335 removeResourceOveruseListenerLocked(listener, mOveruseSystemListenerInfosByUid);
336 }
337 }
338
339 /** Sets whether or not a package is killable on resource overuse. */
340 public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
341 boolean isKillable) {
342 Objects.requireNonNull(packageName, "Package name must be non-null");
343 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700344
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700345 if (userHandle.equals(UserHandle.ALL)) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700346 setPackageKillableStateForAllUsers(packageName, isKillable);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700347 return;
348 }
349 int userId = userHandle.getIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700350 String genericPackageName = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
351 if (genericPackageName == null) {
352 throw new IllegalArgumentException("Package '" + packageName + "' not found");
353 }
354 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700355 synchronized (mLock) {
356 /*
357 * When the queried package is not cached in {@link mUsageByUserPackage}, the set API
358 * will update the killable state even when the package should never be killed.
359 * But the get API will return the correct killable state. This behavior is tolerable
360 * because in production the set API should be called only after the get API.
361 * For instance, when this case happens by mistake and the package overuses resource
362 * between the set and the get API calls, the daemon will provide correct killable
363 * state when pushing the latest stats. Ergo, the invalid killable state doesn't have
364 * any effect.
365 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700366 PackageResourceUsage usage = mUsageByUserPackage.get(key);
367 if (usage == null) {
368 usage = new PackageResourceUsage(userId, genericPackageName);
369 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700370 if (!usage.setKillableStateLocked(isKillable)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700371 Slogf.e(TAG, "User %d cannot set killable state for package '%s'",
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700372 userHandle.getIdentifier(), genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700373 throw new IllegalArgumentException("Package killable state is not updatable");
374 }
375 mUsageByUserPackage.put(key, usage);
376 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700377 if (DEBUG) {
378 Slogf.d(TAG, "Successfully set killable package state for user %d", userId);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700379 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700380 }
381
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700382 private void setPackageKillableStateForAllUsers(String packageName, boolean isKillable) {
383 UserManager userManager = UserManager.get(mContext);
384 List<UserInfo> userInfos = userManager.getAliveUsers();
385 String genericPackageName = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700386 synchronized (mLock) {
387 for (int i = 0; i < userInfos.size(); ++i) {
388 int userId = userInfos.get(i).id;
389 String name = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
390 if (name == null) {
391 continue;
392 }
393 genericPackageName = name;
394 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700395 PackageResourceUsage usage = mUsageByUserPackage.get(key);
396 if (usage == null) {
397 continue;
398 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700399 if (!usage.setKillableStateLocked(isKillable)) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700400 Slogf.e(TAG, "Cannot set killable state for package '%s'", packageName);
401 throw new IllegalArgumentException(
402 "Package killable state is not updatable");
403 }
404 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700405 if (genericPackageName != null) {
406 if (!isKillable) {
407 mDefaultNotKillableGenericPackages.add(genericPackageName);
408 } else {
409 mDefaultNotKillableGenericPackages.remove(genericPackageName);
410 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700411 }
412 }
413 if (DEBUG) {
414 Slogf.d(TAG, "Successfully set killable package state for all users");
415 }
416 }
417
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700418 /** Returns the list of package killable states on resource overuse for the user. */
419 @NonNull
420 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
421 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700422 PackageManager pm = mContext.getPackageManager();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700423 if (!userHandle.equals(UserHandle.ALL)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700424 if (DEBUG) {
425 Slogf.d(TAG, "Returning all package killable states for user %d",
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700426 userHandle.getIdentifier());
427 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700428 return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
429 }
430 List<PackageKillableState> packageKillableStates = new ArrayList<>();
431 UserManager userManager = UserManager.get(mContext);
432 List<UserInfo> userInfos = userManager.getAliveUsers();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700433 for (int i = 0; i < userInfos.size(); ++i) {
434 packageKillableStates.addAll(
435 getPackageKillableStatesForUserId(userInfos.get(i).id, pm));
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700436 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700437 if (DEBUG) {
438 Slogf.d(TAG, "Returning all package killable states for all users");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700439 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700440 return packageKillableStates;
441 }
442
443 private List<PackageKillableState> getPackageKillableStatesForUserId(int userId,
444 PackageManager pm) {
445 List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */0, userId);
446 List<PackageKillableState> states = new ArrayList<>();
447 synchronized (mLock) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700448 ArrayMap<String, List<ApplicationInfo>> applicationInfosBySharedPackage =
449 new ArrayMap<>();
450 for (int i = 0; i < packageInfos.size(); ++i) {
451 PackageInfo packageInfo = packageInfos.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700452 String genericPackageName = mPackageInfoHandler.getNameForPackage(packageInfo);
453 if (packageInfo.sharedUserId == null) {
454 int killableState = getPackageKillableStateForUserPackageLocked(
455 userId, genericPackageName,
456 mPackageInfoHandler.getComponentType(packageInfo.applicationInfo),
457 mSafeToKillPackages.contains(genericPackageName));
458 states.add(new PackageKillableState(packageInfo.packageName, userId,
459 killableState));
460 continue;
461 }
462 List<ApplicationInfo> applicationInfos =
463 applicationInfosBySharedPackage.get(genericPackageName);
464 if (applicationInfos == null) {
465 applicationInfos = new ArrayList<>();
466 }
467 applicationInfos.add(packageInfo.applicationInfo);
468 applicationInfosBySharedPackage.put(genericPackageName, applicationInfos);
469 }
470 for (Map.Entry<String, List<ApplicationInfo>> entry :
471 applicationInfosBySharedPackage.entrySet()) {
472 String genericPackageName = entry.getKey();
473 List<ApplicationInfo> applicationInfos = entry.getValue();
474 int componentType = mPackageInfoHandler.getSharedComponentType(
475 applicationInfos, genericPackageName);
476 boolean isSafeToKill = mSafeToKillPackages.contains(genericPackageName);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700477 for (int i = 0; i < applicationInfos.size(); ++i) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700478 isSafeToKill = isSafeToKill
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700479 || mSafeToKillPackages.contains(applicationInfos.get(i).packageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700480 }
481 int killableState = getPackageKillableStateForUserPackageLocked(
482 userId, genericPackageName, componentType, isSafeToKill);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700483 for (int i = 0; i < applicationInfos.size(); ++i) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700484 states.add(new PackageKillableState(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700485 applicationInfos.get(i).packageName, userId, killableState));
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700486 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700487 }
488 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700489 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700490 Slogf.d(TAG, "Returning the package killable states for user packages");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700491 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700492 return states;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700493 }
494
495 /** Sets the given resource overuse configurations. */
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700496 @CarWatchdogManager.ReturnCode
497 public int setResourceOveruseConfigurations(
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700498 List<ResourceOveruseConfiguration> configurations,
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700499 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
500 throws RemoteException {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700501 Objects.requireNonNull(configurations, "Configurations must be non-null");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700502 Preconditions.checkArgument((configurations.size() > 0),
503 "Must provide at least one configuration");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700504 Preconditions.checkArgument((resourceOveruseFlag > 0),
505 "Must provide valid resource overuse flag");
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700506 ArraySet<Integer> seenComponentTypes = new ArraySet<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700507 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
508 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700509 for (int i = 0; i < configurations.size(); ++i) {
510 ResourceOveruseConfiguration config = configurations.get(i);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700511 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700512 * TODO(b/186119640): Make sure the validation done here matches the validation done in
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700513 * the daemon so set requests retried at a later time will complete successfully.
514 */
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700515 int componentType = config.getComponentType();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700516 if (toComponentTypeStr(componentType).equals("UNKNOWN")) {
517 throw new IllegalArgumentException("Invalid component type in the configuration");
518 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700519 if (seenComponentTypes.contains(componentType)) {
520 throw new IllegalArgumentException(
521 "Cannot provide duplicate configurations for the same component type");
522 }
523 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
524 && config.getIoOveruseConfiguration() == null) {
525 throw new IllegalArgumentException("Must provide I/O overuse configuration");
526 }
527 seenComponentTypes.add(config.getComponentType());
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700528 internalConfigs.add(toInternalResourceOveruseConfiguration(config,
529 resourceOveruseFlag));
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700530 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700531 synchronized (mLock) {
532 if (!mIsConnectedToDaemon) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700533 setPendingSetResourceOveruseConfigurationsRequestLocked(internalConfigs);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700534 return CarWatchdogManager.RETURN_CODE_SUCCESS;
535 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700536 /* Verify no pending request in progress. */
537 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700538 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700539 return setResourceOveruseConfigurationsInternal(internalConfigs,
540 /* isPendingRequest= */ false);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700541 }
542
543 /** Returns the available resource overuse configurations. */
544 @NonNull
545 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
546 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
547 Preconditions.checkArgument((resourceOveruseFlag > 0),
548 "Must provide valid resource overuse flag");
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700549 if (!isConnectedToDaemon()) {
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700550 throw new IllegalStateException("Car watchdog daemon is not connected");
551 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700552 synchronized (mLock) {
553 /* Verify no pending request in progress. */
554 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
555 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700556 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
557 new ArrayList<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700558 try {
559 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
560 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700561 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700562 throw new IllegalStateException(e);
563 }
564 List<ResourceOveruseConfiguration> configs = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700565 for (int i = 0; i < internalConfigs.size(); ++i) {
566 configs.add(
567 toResourceOveruseConfiguration(internalConfigs.get(i), resourceOveruseFlag));
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700568 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700569 if (DEBUG) {
570 Slogf.d(TAG, "Returning the resource overuse configuration");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700571 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700572 return configs;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700573 }
574
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700575 /** Processes the latest I/O overuse stats */
576 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
577 int[] uids = new int[packageIoOveruseStats.size()];
578 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
579 uids[i] = packageIoOveruseStats.get(i).uid;
580 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700581 SparseArray<String> genericPackageNamesByUid = mPackageInfoHandler.getNamesForUids(uids);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700582 synchronized (mLock) {
583 checkAndHandleDateChangeLocked();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700584 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
585 PackageIoOveruseStats stats = packageIoOveruseStats.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700586 String genericPackageName = genericPackageNamesByUid.get(stats.uid);
587 if (genericPackageName == null) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700588 continue;
589 }
590 int userId = UserHandle.getUserId(stats.uid);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700591 PackageResourceUsage usage = cacheAndFetchUsageLocked(userId, genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700592 stats.ioOveruseStats);
593 if (stats.shouldNotify) {
594 /*
595 * Packages that exceed the warn threshold percentage should be notified as well
596 * and only the daemon is aware of such packages. Thus the flag is used to
597 * indicate which packages should be notified.
598 */
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700599 ResourceOveruseStats resourceOveruseStats =
600 usage.getResourceOveruseStatsBuilder().setIoOveruseStats(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700601 usage.getIoOveruseStatsLocked()).build();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700602 notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700603 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700604
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700605 if (!usage.ioUsage.exceedsThreshold()) {
606 continue;
607 }
608 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
609 overuseAction.packageIdentifier = new PackageIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700610 overuseAction.packageIdentifier.name = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700611 overuseAction.packageIdentifier.uid = stats.uid;
612 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
613 overuseAction.resourceOveruseActionType = NOT_KILLED;
614 /*
615 * No action required on I/O overuse on one of the following cases:
616 * #1 The package is not safe to kill as it is critical for system stability.
617 * #2 The package has no recurring overuse behavior and the user opted to not
618 * kill the package so honor the user's decision.
619 */
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700620 int killableState = usage.getKillableStateLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700621 if (killableState == KILLABLE_STATE_NEVER) {
622 mOveruseActionsByUserPackage.add(overuseAction);
623 continue;
624 }
625 boolean hasRecurringOveruse = isRecurringOveruseLocked(usage);
626 if (!hasRecurringOveruse && killableState == KILLABLE_STATE_NO) {
627 overuseAction.resourceOveruseActionType = NOT_KILLED_USER_OPTED;
628 mOveruseActionsByUserPackage.add(overuseAction);
629 continue;
630 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700631 IPackageManager packageManager = ActivityThread.getPackageManager();
632 List<String> packages = Collections.singletonList(genericPackageName);
633 if (usage.isSharedPackage()) {
634 packages = mPackageInfoHandler.getPackagesForUid(
635 stats.uid, genericPackageName);
636 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700637 for (int pkgIdx = 0; pkgIdx < packages.size(); ++pkgIdx) {
638 String pkg = packages.get(pkgIdx);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700639 try {
640 int oldEnabledState = -1;
641 if (!hasRecurringOveruse) {
642 oldEnabledState = packageManager.getApplicationEnabledSetting(
643 pkg, userId);
644 if (oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED
645 || oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED_USER
646 || oldEnabledState
647 == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
648 continue;
649 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700650 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700651 packageManager.setApplicationEnabledSetting(pkg,
652 COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId,
653 mContext.getPackageName());
654 overuseAction.resourceOveruseActionType = hasRecurringOveruse
655 ? KILLED_RECURRING_OVERUSE : KILLED;
656 if (oldEnabledState != -1) {
657 usage.oldEnabledStateByPackage.put(pkg, oldEnabledState);
658 }
659 } catch (RemoteException e) {
660 Slogf.e(TAG, "Failed to disable application for user %d, package '%s'",
661 userId, pkg);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700662 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700663 }
664 mOveruseActionsByUserPackage.add(overuseAction);
665 }
666 if (!mOveruseActionsByUserPackage.isEmpty()) {
667 mMainHandler.sendMessage(obtainMessage(
668 WatchdogPerfHandler::notifyActionsTakenOnOveruse, this));
669 }
670 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700671 if (DEBUG) {
672 Slogf.d(TAG, "Processed latest I/O overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700673 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700674 }
675
676 /** Notify daemon about the actions take on resource overuse */
677 public void notifyActionsTakenOnOveruse() {
678 List<PackageResourceOveruseAction> actions;
679 synchronized (mLock) {
680 if (mOveruseActionsByUserPackage.isEmpty()) {
681 return;
682 }
683 actions = new ArrayList<>(mOveruseActionsByUserPackage);
684 mOveruseActionsByUserPackage.clear();
685 }
686 try {
687 mCarWatchdogDaemonHelper.actionTakenOnResourceOveruse(actions);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700688 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700689 Slogf.w(TAG, e, "Failed to notify car watchdog daemon of actions taken on resource "
690 + "overuse");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700691 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700692 if (DEBUG) {
693 Slogf.d(TAG, "Notified car watchdog daemon of actions taken on resource overuse");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700694 }
695 }
696
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700697 /** Resets the resource overuse stats for the given generic package names. */
698 public void resetResourceOveruseStats(Set<String> genericPackageNames) {
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700699 synchronized (mLock) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700700 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
701 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700702 if (genericPackageNames.contains(usage.genericPackageName)) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700703 usage.resetStatsLocked();
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700704 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700705 * TODO(b/192294393): When the stats are persisted in local DB, reset the stats
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700706 * for this package from local DB.
707 */
708 }
709 }
710 }
711 }
712
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700713 @GuardedBy("mLock")
714 private int getPackageKillableStateForUserPackageLocked(
715 int userId, String genericPackageName, int componentType, boolean isSafeToKill) {
716 String key = getUserPackageUniqueId(userId, genericPackageName);
717 PackageResourceUsage usage = mUsageByUserPackage.get(key);
718 if (usage == null) {
719 usage = new PackageResourceUsage(userId, genericPackageName);
720 }
721 int killableState = usage.syncAndFetchKillableStateLocked(componentType, isSafeToKill);
722 mUsageByUserPackage.put(key, usage);
723 return killableState;
724 }
725
726 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700727 private void notifyResourceOveruseStatsLocked(int uid,
728 ResourceOveruseStats resourceOveruseStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700729 String genericPackageName = resourceOveruseStats.getPackageName();
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700730 ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid);
731 if (listenerInfos != null) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700732 for (int i = 0; i < listenerInfos.size(); ++i) {
733 listenerInfos.get(i).notifyListener(
734 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700735 }
736 }
737 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700738 ArrayList<ResourceOveruseListenerInfo> systemListenerInfos =
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700739 mOveruseSystemListenerInfosByUid.valueAt(i);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700740 for (int j = 0; j < systemListenerInfos.size(); ++j) {
741 systemListenerInfos.get(j).notifyListener(
742 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700743 }
744 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700745 if (DEBUG) {
746 Slogf.d(TAG, "Notified resource overuse stats to listening applications");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700747 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700748 }
749
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700750 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700751 private void checkAndHandleDateChangeLocked() {
752 ZonedDateTime previousUTC = mLastStatsReportUTC;
753 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
754 if (mLastStatsReportUTC.getDayOfYear() == previousUTC.getDayOfYear()
755 && mLastStatsReportUTC.getYear() == previousUTC.getYear()) {
756 return;
757 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700758 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
759 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700760 // Forgive the daily disabled package on date change.
761 for (Map.Entry<String, Integer> entry : usage.oldEnabledStateByPackage.entrySet()) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700762 try {
763 IPackageManager packageManager = ActivityThread.getPackageManager();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700764 if (packageManager.getApplicationEnabledSetting(entry.getKey(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700765 usage.userId)
766 != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
767 continue;
768 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700769 packageManager.setApplicationEnabledSetting(entry.getKey(),
770 entry.getValue(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700771 /* flags= */ 0, usage.userId, mContext.getPackageName());
772 } catch (RemoteException e) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700773 Slogf.e(TAG,
774 "Failed to reset enabled setting for disabled package '%s', user '%d'",
775 usage.genericPackageName, usage.userId);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700776 }
777 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700778 /* TODO(b/192294393): Stash the old usage into SQLite DB storage. */
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700779 usage.resetStatsLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700780 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700781 if (DEBUG) {
782 Slogf.d(TAG, "Handled date change successfully");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700783 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700784 }
785
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700786 @GuardedBy("mLock")
787 private PackageResourceUsage cacheAndFetchUsageLocked(
788 @UserIdInt int userId, String genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700789 android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700790 String key = getUserPackageUniqueId(userId, genericPackageName);
791 PackageResourceUsage usage = mUsageByUserPackage.get(key);
792 if (usage == null) {
793 usage = new PackageResourceUsage(userId, genericPackageName);
794 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700795 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700796 mUsageByUserPackage.put(key, usage);
797 return usage;
798 }
799
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700800 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700801 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
802 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700803 * TODO(b/192294393): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700804 * has recurring I/O overuse behavior.
805 */
806 return false;
807 }
808
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700809 private IoOveruseStats getIoOveruseStatsForPeriod(int userId, String genericPackageName,
810 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
811 synchronized (mLock) {
812 String key = getUserPackageUniqueId(userId, genericPackageName);
813 PackageResourceUsage usage = mUsageByUserPackage.get(key);
814 if (usage == null) {
815 return null;
816 }
817 return getIoOveruseStatsLocked(usage, /* minimumBytesWritten= */ 0, maxStatsPeriod);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700818 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700819 }
820
821 @GuardedBy("mLock")
822 private IoOveruseStats getIoOveruseStatsLocked(PackageResourceUsage usage,
823 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
824 IoOveruseStats stats = usage.getIoOveruseStatsLocked();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700825 long totalBytesWritten = stats != null ? stats.getTotalBytesWritten() : 0;
826 /*
827 * TODO(b/185431129): When maxStatsPeriod > current day, populate the historical stats
828 * from the local database. Also handle the case where the package doesn't have current
829 * day stats but has historical stats.
830 */
831 if (totalBytesWritten < minimumBytesWritten) {
832 return null;
833 }
834 return stats;
835 }
836
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700837 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700838 private void addResourceOveruseListenerLocked(
839 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
840 @NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700841 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700842 int callingPid = Binder.getCallingPid();
843 int callingUid = Binder.getCallingUid();
844 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
845 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
846 "resource overuse listener";
847
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700848 IBinder binder = listener.asBinder();
849 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
850 if (listenerInfos == null) {
851 listenerInfos = new ArrayList<>();
852 listenerInfosByUid.put(callingUid, listenerInfos);
853 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700854 for (int i = 0; i < listenerInfos.size(); ++i) {
855 if (listenerInfos.get(i).listener.asBinder() == binder) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700856 throw new IllegalStateException(
857 "Cannot add " + listenerType + " as it is already added");
858 }
859 }
860
861 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
862 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
863 try {
864 listenerInfo.linkToDeath();
865 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700866 Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700867 return;
868 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700869 listenerInfos.add(listenerInfo);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700870 if (DEBUG) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700871 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
872 callingPid, callingUid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700873 }
874 }
875
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700876 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700877 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700878 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700879 int callingUid = Binder.getCallingUid();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700880 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
881 ? "resource overuse system listener" : "resource overuse listener";
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700882 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
883 if (listenerInfos == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700884 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700885 return;
886 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700887 IBinder binder = listener.asBinder();
888 ResourceOveruseListenerInfo cachedListenerInfo = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700889 for (int i = 0; i < listenerInfos.size(); ++i) {
890 if (listenerInfos.get(i).listener.asBinder() == binder) {
891 cachedListenerInfo = listenerInfos.get(i);
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700892 break;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700893 }
894 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700895 if (cachedListenerInfo == null) {
896 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
897 return;
898 }
899 cachedListenerInfo.unlinkToDeath();
900 listenerInfos.remove(cachedListenerInfo);
901 if (listenerInfos.isEmpty()) {
902 listenerInfosByUid.remove(callingUid);
903 }
904 if (DEBUG) {
905 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
906 cachedListenerInfo.pid, cachedListenerInfo.uid);
907 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700908 }
909
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700910 @GuardedBy("mLock")
911 private void setPendingSetResourceOveruseConfigurationsRequestLocked(
912 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) {
913 if (mPendingSetResourceOveruseConfigurationsRequest != null) {
914 if (mPendingSetResourceOveruseConfigurationsRequest == configs) {
915 return;
916 }
917 throw new IllegalStateException(
918 "Pending setResourceOveruseConfigurations request in progress");
919 }
920 mPendingSetResourceOveruseConfigurationsRequest = configs;
921 }
922
923 private void retryPendingSetResourceOveruseConfigurations() {
924 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700925 synchronized (mLock) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700926 if (mPendingSetResourceOveruseConfigurationsRequest == null) {
927 return;
928 }
929 configs = mPendingSetResourceOveruseConfigurationsRequest;
930 }
931 try {
932 int result = setResourceOveruseConfigurationsInternal(configs,
933 /* isPendingRequest= */ true);
934 if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) {
935 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code "
936 + "%d", result);
937 }
938 } catch (Exception e) {
939 Slogf.e(TAG, e, "Exception on set pending resource overuse configurations");
940 }
941 }
942
943 private int setResourceOveruseConfigurationsInternal(
944 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs,
945 boolean isPendingRequest) throws RemoteException {
946 boolean doClearPendingRequest = isPendingRequest;
947 try {
948 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs);
949 } catch (RemoteException e) {
950 if (e instanceof TransactionTooLargeException) {
951 throw e;
952 }
953 Slogf.e(TAG, e, "Remote exception on set resource overuse configuration");
954 synchronized (mLock) {
955 setPendingSetResourceOveruseConfigurationsRequestLocked(configs);
956 }
957 doClearPendingRequest = false;
958 return CarWatchdogManager.RETURN_CODE_SUCCESS;
959 } finally {
960 if (doClearPendingRequest) {
961 synchronized (mLock) {
962 mPendingSetResourceOveruseConfigurationsRequest = null;
963 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700964 }
965 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700966 /* TODO(b/192665269): Fetch safe-to-kill list from daemon and update mSafeToKillPackages. */
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700967 if (DEBUG) {
968 Slogf.d(TAG, "Set the resource overuse configuration successfully");
969 }
970 return CarWatchdogManager.RETURN_CODE_SUCCESS;
971 }
972
973 private boolean isConnectedToDaemon() {
974 synchronized (mLock) {
975 long startTimeMillis = SystemClock.uptimeMillis();
976 long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
977 while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) {
978 try {
979 mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis);
980 } catch (InterruptedException e) {
981 Thread.currentThread().interrupt();
982 continue;
983 } finally {
984 sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
985 }
986 break;
987 }
988 return mIsConnectedToDaemon;
989 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700990 }
991
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700992 private static String getUserPackageUniqueId(int userId, String genericPackageName) {
993 return String.valueOf(userId) + ":" + genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700994 }
995
996 @VisibleForTesting
997 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
998 android.automotive.watchdog.IoOveruseStats internalStats) {
999 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
1000 internalStats.startTime, internalStats.durationInSeconds);
1001 statsBuilder.setRemainingWriteBytes(
1002 toPerStateBytes(internalStats.remainingWriteBytes));
1003 statsBuilder.setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes));
1004 statsBuilder.setTotalOveruses(internalStats.totalOveruses);
1005 return statsBuilder;
1006 }
1007
1008 private static PerStateBytes toPerStateBytes(
1009 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001010 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001011 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001012 }
1013
1014 private static long totalPerStateBytes(
1015 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
1016 BiFunction<Long, Long, Long> sum = (l, r) -> {
1017 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
1018 };
1019 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
1020 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
1021 }
1022
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001023 private static long getMinimumBytesWritten(
1024 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
1025 switch (minimumStatsIoFlag) {
1026 case 0:
1027 return 0;
1028 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
1029 return 1024 * 1024;
1030 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
1031 return 100 * 1024 * 1024;
1032 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
1033 return 1024 * 1024 * 1024;
1034 default:
1035 throw new IllegalArgumentException(
1036 "Must provide valid minimum stats flag for I/O resource");
1037 }
1038 }
1039
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001040 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
1041 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
1042 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1043 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
1044 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
1045 internalConfig.componentType = config.getComponentType();
1046 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
1047 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
1048 internalConfig.packageMetadata = new ArrayList<>();
1049 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
1050 if (entry.getKey().isEmpty()) {
1051 continue;
1052 }
1053 PackageMetadata metadata = new PackageMetadata();
1054 metadata.packageName = entry.getKey();
1055 switch(entry.getValue()) {
1056 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1057 metadata.appCategoryType = ApplicationCategoryType.MAPS;
1058 break;
1059 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1060 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
1061 break;
1062 default:
1063 continue;
1064 }
1065 internalConfig.packageMetadata.add(metadata);
1066 }
1067 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
1068 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1069 && config.getIoOveruseConfiguration() != null) {
1070 internalConfig.resourceSpecificConfigurations.add(
1071 toResourceSpecificConfiguration(config.getComponentType(),
1072 config.getIoOveruseConfiguration()));
1073 }
1074 return internalConfig;
1075 }
1076
1077 private static ResourceSpecificConfiguration
1078 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
1079 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
1080 new android.automotive.watchdog.internal.IoOveruseConfiguration();
1081 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
1082 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
1083 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
1084 config.getPackageSpecificThresholds());
1085 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
1086 config.getAppCategorySpecificThresholds());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001087 for (int i = 0; i < internalConfig.categorySpecificThresholds.size(); ++i) {
1088 PerStateIoOveruseThreshold threshold = internalConfig.categorySpecificThresholds.get(i);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001089 switch(threshold.name) {
1090 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1091 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
1092 break;
1093 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1094 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
1095 break;
1096 default:
1097 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
1098 }
1099 }
1100 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
1101 config.getSystemWideThresholds());
1102
1103 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
1104 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
1105 return resourceSpecificConfig;
1106 }
1107
1108 @VisibleForTesting
1109 static String toComponentTypeStr(int componentType) {
1110 switch(componentType) {
1111 case ComponentType.SYSTEM:
1112 return "SYSTEM";
1113 case ComponentType.VENDOR:
1114 return "VENDOR";
1115 case ComponentType.THIRD_PARTY:
1116 return "THIRD_PARTY";
1117 default:
1118 return "UNKNOWN";
1119 }
1120 }
1121
1122 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
1123 Map<String, PerStateBytes> thresholds) {
1124 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
1125 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001126 if (!thresholds.isEmpty()) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001127 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
1128 entry.getValue()));
1129 }
1130 }
1131 return internalThresholds;
1132 }
1133
1134 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
1135 PerStateBytes perStateBytes) {
1136 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
1137 threshold.name = name;
1138 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
1139 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
1140 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
1141 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
1142 return threshold;
1143 }
1144
1145 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
1146 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
1147 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
1148 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001149 for (int i = 0; i < thresholds.size(); ++i) {
1150 if (thresholds.get(i).getDurationInSeconds() == 0
1151 || thresholds.get(i).getWrittenBytesPerSecond() == 0) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001152 continue;
1153 }
1154 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
1155 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001156 internalThreshold.durationInSeconds = thresholds.get(i).getDurationInSeconds();
1157 internalThreshold.writtenBytesPerSecond = thresholds.get(i).getWrittenBytesPerSecond();
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001158 internalThresholds.add(internalThreshold);
1159 }
1160 return internalThresholds;
1161 }
1162
1163 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
1164 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
1165 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001166 ArrayMap<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
1167 for (int i = 0; i < internalConfig.packageMetadata.size(); ++i) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001168 String categoryTypeStr;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001169 switch (internalConfig.packageMetadata.get(i).appCategoryType) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001170 case ApplicationCategoryType.MAPS:
1171 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
1172 break;
1173 case ApplicationCategoryType.MEDIA:
1174 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
1175 break;
1176 default:
1177 continue;
1178 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001179 packagesToAppCategoryTypes.put(
1180 internalConfig.packageMetadata.get(i).packageName, categoryTypeStr);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001181 }
1182 ResourceOveruseConfiguration.Builder configBuilder =
1183 new ResourceOveruseConfiguration.Builder(
1184 internalConfig.componentType,
1185 internalConfig.safeToKillPackages,
1186 internalConfig.vendorPackagePrefixes,
1187 packagesToAppCategoryTypes);
1188 for (ResourceSpecificConfiguration resourceSpecificConfig :
1189 internalConfig.resourceSpecificConfigurations) {
1190 if (resourceSpecificConfig.getTag()
1191 == ResourceSpecificConfiguration.ioOveruseConfiguration
1192 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
1193 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
1194 resourceSpecificConfig.getIoOveruseConfiguration()));
1195 }
1196 }
1197 return configBuilder.build();
1198 }
1199
1200 private static IoOveruseConfiguration toIoOveruseConfiguration(
1201 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
1202 PerStateBytes componentLevelThresholds =
1203 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001204 ArrayMap<String, PerStateBytes> packageSpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001205 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001206 ArrayMap<String, PerStateBytes> appCategorySpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001207 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
1208 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
1209 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
1210 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
1211 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
1212 List<IoOveruseAlertThreshold> systemWideThresholds =
1213 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
1214
1215 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1216 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1217 systemWideThresholds);
1218 return configBuilder.build();
1219 }
1220
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001221 private static ArrayMap<String, PerStateBytes> toPerStateBytesMap(
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001222 List<PerStateIoOveruseThreshold> thresholds) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001223 ArrayMap<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1224 for (int i = 0; i < thresholds.size(); ++i) {
1225 thresholdsMap.put(
1226 thresholds.get(i).name, toPerStateBytes(thresholds.get(i).perStateWriteBytes));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001227 }
1228 return thresholdsMap;
1229 }
1230
1231 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1232 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1233 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001234 for (int i = 0; i < internalThresholds.size(); ++i) {
1235 thresholds.add(new IoOveruseAlertThreshold(internalThresholds.get(i).durationInSeconds,
1236 internalThresholds.get(i).writtenBytesPerSecond));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001237 }
1238 return thresholds;
1239 }
1240
1241 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1242 PerStateBytes perStateBytes = map.get(oldKey);
1243 if (perStateBytes != null) {
1244 map.put(newKey, perStateBytes);
1245 map.remove(oldKey);
1246 }
1247 }
1248
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001249 private final class PackageResourceUsage {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001250 public final String genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001251 public @UserIdInt final int userId;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001252 @GuardedBy("mLock")
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001253 public final PackageIoUsage ioUsage = new PackageIoUsage();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001254 @GuardedBy("mLock")
1255 public final ArrayMap<String, Integer> oldEnabledStateByPackage = new ArrayMap<>();
1256 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001257 private @KillableState int mKillableState;
1258
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001259 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001260 PackageResourceUsage(@UserIdInt int userId, String genericPackageName) {
1261 this.genericPackageName = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001262 this.userId = userId;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001263 this.mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001264 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001265 }
1266
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001267 public boolean isSharedPackage() {
1268 return this.genericPackageName.startsWith(SHARED_PACKAGE_PREFIX);
1269 }
1270
1271 @GuardedBy("mLock")
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001272 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001273 if (!internalStats.killableOnOveruse) {
1274 /*
1275 * Killable value specified in the internal stats is provided by the native daemon.
1276 * This value reflects whether or not an application is safe-to-kill on overuse.
1277 * This setting is from the I/O overuse configuration specified by the system and
1278 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1279 * specify the application is not killable, the application is not safe-to-kill.
1280 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001281 mKillableState = KILLABLE_STATE_NEVER;
1282 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1283 /*
1284 * This case happens when a previously unsafe to kill system/vendor package was
1285 * recently marked as safe-to-kill so update the old state to the default value.
1286 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001287 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001288 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001289 }
1290 ioUsage.update(internalStats);
1291 }
1292
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001293 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001294 return new ResourceOveruseStats.Builder(genericPackageName, UserHandle.of(userId));
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001295 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001296
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001297 @GuardedBy("mLock")
1298 public IoOveruseStats getIoOveruseStatsLocked() {
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001299 if (!ioUsage.hasUsage()) {
1300 return null;
1301 }
1302 return ioUsage.getStatsBuilder().setKillableOnOveruse(
1303 mKillableState != KILLABLE_STATE_NEVER).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001304 }
1305
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001306 @GuardedBy("mLock")
1307 public @KillableState int getKillableStateLocked() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001308 return mKillableState;
1309 }
1310
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001311 @GuardedBy("mLock")
1312 public boolean setKillableStateLocked(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001313 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001314 return false;
1315 }
1316 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1317 return true;
1318 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001319
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001320 @GuardedBy("mLock")
1321 public int syncAndFetchKillableStateLocked(int myComponentType, boolean isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001322 /*
1323 * The killable state goes out-of-sync:
1324 * 1. When the on-device safe-to-kill list is recently updated and the user package
1325 * didn't have any resource usage so the native daemon didn't update the killable state.
1326 * 2. When a package has no resource usage and is initialized outside of processing the
1327 * latest resource usage stats.
1328 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001329 if (myComponentType != ComponentType.THIRD_PARTY && !isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001330 mKillableState = KILLABLE_STATE_NEVER;
1331 } else if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001332 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001333 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1334 }
1335 return mKillableState;
1336 }
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001337
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001338 @GuardedBy("mLock")
1339 public void resetStatsLocked() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001340 oldEnabledStateByPackage.clear();
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001341 ioUsage.resetStats();
1342 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001343 }
1344
1345 private static final class PackageIoUsage {
1346 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1347 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
1348 private long mTotalTimesKilled;
1349
1350 PackageIoUsage() {
1351 mTotalTimesKilled = 0;
1352 }
1353
1354 public boolean hasUsage() {
1355 return mIoOveruseStats != null;
1356 }
1357
1358 public void update(android.automotive.watchdog.IoOveruseStats internalStats) {
1359 mIoOveruseStats = internalStats;
1360 if (exceedsThreshold()) {
1361 /*
1362 * Forgive written bytes on overuse as the package is either forgiven or killed on
1363 * overuse. When the package is killed, the user may opt to open the corresponding
1364 * app and the package should be forgiven anyways.
1365 * NOTE: If this logic is updated, update the daemon side logic as well.
1366 */
1367 mForgivenWriteBytes = internalStats.writtenBytes;
1368 }
1369 }
1370
1371 public IoOveruseStats.Builder getStatsBuilder() {
1372 IoOveruseStats.Builder statsBuilder = toIoOveruseStatsBuilder(mIoOveruseStats);
1373 statsBuilder.setTotalTimesKilled(mTotalTimesKilled);
1374 return statsBuilder;
1375 }
1376
1377 public boolean exceedsThreshold() {
1378 if (!hasUsage()) {
1379 return false;
1380 }
1381 android.automotive.watchdog.PerStateBytes remaining =
1382 mIoOveruseStats.remainingWriteBytes;
1383 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1384 || remaining.garageModeBytes == 0;
1385 }
1386
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001387 public void resetStats() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001388 mIoOveruseStats = null;
1389 mForgivenWriteBytes = null;
1390 mTotalTimesKilled = 0;
1391 }
1392 }
1393
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001394 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1395 public final IResourceOveruseListener listener;
1396 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1397 public final int pid;
1398 public final int uid;
1399 public final boolean isListenerForSystem;
1400
1401 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1402 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1403 boolean isListenerForSystem) {
1404 this.listener = listener;
1405 this.flag = flag;
1406 this.pid = pid;
1407 this.uid = uid;
1408 this.isListenerForSystem = isListenerForSystem;
1409 }
1410
1411 @Override
1412 public void binderDied() {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001413 Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001414 isListenerForSystem ? " for system" : "", pid);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001415 Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo =
1416 listenerInfosByUid -> {
1417 ArrayList<ResourceOveruseListenerInfo> listenerInfos =
1418 listenerInfosByUid.get(uid);
1419 if (listenerInfos == null) {
1420 return;
1421 }
1422 listenerInfos.remove(this);
1423 if (listenerInfos.isEmpty()) {
1424 listenerInfosByUid.remove(uid);
1425 }
1426 };
1427 if (isListenerForSystem) {
1428 removeListenerInfo.accept(mOveruseSystemListenerInfosByUid);
1429 } else {
1430 removeListenerInfo.accept(mOveruseListenerInfosByUid);
1431 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001432 unlinkToDeath();
1433 }
1434
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001435 public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001436 int overusingUid, String overusingGenericPackageName,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001437 ResourceOveruseStats resourceOveruseStats) {
1438 if ((flag & resourceType) == 0) {
1439 return;
1440 }
1441 try {
1442 listener.onOveruse(resourceOveruseStats);
1443 } catch (RemoteException e) {
1444 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001445 + "package(uid %d, generic package name '%s'): %s",
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001446 (isListenerForSystem ? "system listener" : "listener"), uid, pid,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001447 overusingUid, overusingGenericPackageName, e);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001448 }
1449 }
1450
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001451 private void linkToDeath() throws RemoteException {
1452 listener.asBinder().linkToDeath(this, 0);
1453 }
1454
1455 private void unlinkToDeath() {
1456 listener.asBinder().unlinkToDeath(this, 0);
1457 }
1458 }
1459}