blob: c7124314cae014e945a127e4d811257d6a04e19f [file] [log] [blame]
Brian Carlstrom7395a8a2014-04-28 22:11:01 -07001/*
2 * Copyright (C) 2014 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.server.pm;
18
David Brazdil37a87692016-04-07 10:43:18 +010019import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
20
Arthur Eubanks09dd1ec2017-09-15 09:28:51 -070021import android.annotation.Nullable;
Christopher Tatecf1a2f72014-06-16 15:51:39 -070022import android.app.job.JobInfo;
23import android.app.job.JobParameters;
24import android.app.job.JobScheduler;
25import android.app.job.JobService;
26import android.content.ComponentName;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070027import android.content.Context;
David Brazdil37a87692016-04-07 10:43:18 +010028import android.content.Intent;
29import android.content.IntentFilter;
Felix Lopez Luis7ca1abd2018-12-12 10:32:32 +000030import android.content.pm.PackageInfo;
David Brazdil37a87692016-04-07 10:43:18 +010031import android.os.BatteryManager;
Narayan Kamath8735f072016-08-16 18:25:08 +010032import android.os.Environment;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070033import android.os.ServiceManager;
Calin Juravle51f521c2017-01-25 18:00:05 -080034import android.os.SystemProperties;
Felix Lopez Luis7ca1abd2018-12-12 10:32:32 +000035import android.os.UserHandle;
Narayan Kamath8735f072016-08-16 18:25:08 +010036import android.os.storage.StorageManager;
Jeff Sharkey9f837a92014-10-24 12:07:24 -070037import android.util.ArraySet;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070038import android.util.Log;
Felix Lopez Luis7ca1abd2018-12-12 10:32:32 +000039import android.util.StatsLog;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070040
Felix Lopez Luis7ca1abd2018-12-12 10:32:32 +000041import com.android.internal.util.ArrayUtils;
Carmen Jacksonf107a232017-05-16 10:37:26 -070042import com.android.server.LocalServices;
43import com.android.server.PinnerService;
Felix Lopez Luis7ca1abd2018-12-12 10:32:32 +000044import com.android.server.pm.dex.DexManager;
Calin Juravle1d0e83d2017-07-17 15:12:01 -070045import com.android.server.pm.dex.DexoptOptions;
Calin Juravle51f521c2017-01-25 18:00:05 -080046
Narayan Kamath8735f072016-08-16 18:25:08 +010047import java.io.File;
Felix Lopez Luis7ca1abd2018-12-12 10:32:32 +000048import java.nio.file.Paths;
Arthur Eubanks09dd1ec2017-09-15 09:28:51 -070049import java.util.List;
Shubham Ajmera246dccf2017-05-24 17:46:36 -070050import java.util.Set;
Nicolas Geoffray27c07372015-11-05 16:54:09 +000051import java.util.concurrent.TimeUnit;
Felix Lopez Luis7ca1abd2018-12-12 10:32:32 +000052import java.util.concurrent.atomic.AtomicBoolean;
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +000053import java.util.function.Supplier;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070054
55/**
56 * {@hide}
57 */
Christopher Tatecf1a2f72014-06-16 15:51:39 -070058public class BackgroundDexOptService extends JobService {
Calin Juravlea50d58e2016-12-22 18:50:05 +020059 private static final String TAG = "BackgroundDexOptService";
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070060
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +000061 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Christopher Tate2c9655b2015-06-12 13:06:45 -070062
Calin Juravlea50d58e2016-12-22 18:50:05 +020063 private static final int JOB_IDLE_OPTIMIZE = 800;
64 private static final int JOB_POST_BOOT_UPDATE = 801;
65
66 private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG
67 ? TimeUnit.MINUTES.toMillis(1)
68 : TimeUnit.DAYS.toMillis(1);
David Brazdil37a87692016-04-07 10:43:18 +010069
Christopher Tatecf1a2f72014-06-16 15:51:39 -070070 private static ComponentName sDexoptServiceName = new ComponentName(
Christopher Tate1b8b3aa2014-06-20 13:17:01 -070071 "android",
Christopher Tatecf1a2f72014-06-16 15:51:39 -070072 BackgroundDexOptService.class.getName());
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070073
Calin Juravle51f521c2017-01-25 18:00:05 -080074 // Possible return codes of individual optimization steps.
75
76 // Optimizations finished. All packages were processed.
77 private static final int OPTIMIZE_PROCESSED = 0;
78 // Optimizations should continue. Issued after checking the scheduler, disk space or battery.
79 private static final int OPTIMIZE_CONTINUE = 1;
80 // Optimizations should be aborted. Job scheduler requested it.
81 private static final int OPTIMIZE_ABORT_BY_JOB_SCHEDULER = 2;
82 // Optimizations should be aborted. No space left on device.
83 private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3;
84
Shubham Ajmera246dccf2017-05-24 17:46:36 -070085 // Used for calculating space threshold for downgrading unused apps.
86 private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2;
87
Brian Carlstroma00be9b2014-12-12 13:14:36 -080088 /**
89 * Set of failed packages remembered across job runs.
90 */
Calin Juravle51f521c2017-01-25 18:00:05 -080091 static final ArraySet<String> sFailedPackageNamesPrimary = new ArraySet<String>();
92 static final ArraySet<String> sFailedPackageNamesSecondary = new ArraySet<String>();
Brian Carlstroma00be9b2014-12-12 13:14:36 -080093
David Brazdil37a87692016-04-07 10:43:18 +010094 /**
95 * Atomics set to true if the JobScheduler requests an abort.
96 */
Calin Juravle51f521c2017-01-25 18:00:05 -080097 private final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
98 private final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
David Brazdil37a87692016-04-07 10:43:18 +010099
100 /**
101 * Atomic set to true if one job should exit early because another job was started.
102 */
Calin Juravle51f521c2017-01-25 18:00:05 -0800103 private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700104
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200105 private final File mDataDir = Environment.getDataDirectory();
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000106 private static final long mDowngradeUnusedAppsThresholdInMillis =
107 getDowngradeUnusedAppsThresholdInMillis();
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700108
Calin Juravledb4a79a2015-12-23 18:55:08 +0200109 public static void schedule(Context context) {
Shubham Ajmera3067a1d2017-07-13 18:46:02 -0700110 if (isBackgroundDexoptDisabled()) {
111 return;
112 }
113
Christopher Tatecf1a2f72014-06-16 15:51:39 -0700114 JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
David Brazdil37a87692016-04-07 10:43:18 +0100115
116 // Schedule a one-off job which scans installed packages and updates
117 // out-of-date oat files.
118 js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
119 .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
120 .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
121 .build());
122
123 // Schedule a daily job which scans installed packages and compiles
124 // those with fresh profiling data.
125 js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
126 .setRequiresDeviceIdle(true)
127 .setRequiresCharging(true)
Calin Juravlea50d58e2016-12-22 18:50:05 +0200128 .setPeriodic(IDLE_OPTIMIZATION_PERIOD)
David Brazdil37a87692016-04-07 10:43:18 +0100129 .build());
130
131 if (DEBUG_DEXOPT) {
132 Log.i(TAG, "Jobs scheduled");
133 }
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700134 }
135
David Brazdilace80c52016-04-11 13:37:29 +0100136 public static void notifyPackageChanged(String packageName) {
137 // The idle maintanance job skips packages which previously failed to
138 // compile. The given package has changed and may successfully compile
139 // now. Remove it from the list of known failing packages.
Calin Juravle51f521c2017-01-25 18:00:05 -0800140 synchronized (sFailedPackageNamesPrimary) {
141 sFailedPackageNamesPrimary.remove(packageName);
142 }
143 synchronized (sFailedPackageNamesSecondary) {
144 sFailedPackageNamesSecondary.remove(packageName);
David Brazdilace80c52016-04-11 13:37:29 +0100145 }
146 }
147
David Brazdil37a87692016-04-07 10:43:18 +0100148 // Returns the current battery level as a 0-100 integer.
149 private int getBatteryLevel() {
150 IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
151 Intent intent = registerReceiver(null, filter);
152 int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
153 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
Todd Poynor1cd7cc82018-02-20 20:11:43 -0800154 boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
155
156 if (!present) {
157 // No battery, treat as if 100%, no possibility of draining battery.
158 return 100;
159 }
Christopher Tatecf1a2f72014-06-16 15:51:39 -0700160
David Brazdil37a87692016-04-07 10:43:18 +0100161 if (level < 0 || scale <= 0) {
162 // Battery data unavailable. This should never happen, so assume the worst.
163 return 0;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700164 }
David Brazdil37a87692016-04-07 10:43:18 +0100165
166 return (100 * level / scale);
167 }
168
Calin Juravlec6604752017-01-25 17:08:03 -0800169 private long getLowStorageThreshold(Context context) {
Narayan Kamath8735f072016-08-16 18:25:08 +0100170 @SuppressWarnings("deprecation")
Calin Juravlec6604752017-01-25 17:08:03 -0800171 final long lowThreshold = StorageManager.from(context).getStorageLowBytes(mDataDir);
Narayan Kamath8735f072016-08-16 18:25:08 +0100172 if (lowThreshold == 0) {
173 Log.e(TAG, "Invalid low storage threshold");
174 }
175
176 return lowThreshold;
177 }
178
David Brazdil37a87692016-04-07 10:43:18 +0100179 private boolean runPostBootUpdate(final JobParameters jobParams,
180 final PackageManagerService pm, final ArraySet<String> pkgs) {
181 if (mExitPostBootUpdate.get()) {
182 // This job has already been superseded. Do not start it.
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700183 return false;
184 }
David Brazdil37a87692016-04-07 10:43:18 +0100185 new Thread("BackgroundDexOptService_PostBootUpdate") {
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700186 @Override
187 public void run() {
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200188 postBootUpdate(jobParams, pm, pkgs);
189 }
Narayan Kamath8735f072016-08-16 18:25:08 +0100190
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200191 }.start();
192 return true;
193 }
Calin Juravle8412cf42016-05-09 17:56:18 +0100194
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200195 private void postBootUpdate(JobParameters jobParams, PackageManagerService pm,
196 ArraySet<String> pkgs) {
197 // Load low battery threshold from the system config. This is a 0-100 integer.
198 final int lowBatteryThreshold = getResources().getInteger(
199 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Calin Juravlec6604752017-01-25 17:08:03 -0800200 final long lowThreshold = getLowStorageThreshold(this);
Calin Juravle8412cf42016-05-09 17:56:18 +0100201
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200202 mAbortPostBootUpdate.set(false);
203
Calin Juravle31ce3a82017-05-22 17:49:01 -0700204 ArraySet<String> updatedPackages = new ArraySet<>();
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200205 for (String pkg : pkgs) {
206 if (mAbortPostBootUpdate.get()) {
207 // JobScheduler requested an early abort.
208 return;
209 }
210 if (mExitPostBootUpdate.get()) {
211 // Different job, which supersedes this one, is running.
212 break;
213 }
214 if (getBatteryLevel() < lowBatteryThreshold) {
215 // Rather bail than completely drain the battery.
216 break;
217 }
218 long usableSpace = mDataDir.getUsableSpace();
219 if (usableSpace < lowThreshold) {
220 // Rather bail than completely fill up the disk.
221 Log.w(TAG, "Aborting background dex opt job due to low storage: " +
222 usableSpace);
223 break;
224 }
225
226 if (DEBUG_DEXOPT) {
227 Log.i(TAG, "Updating package " + pkg);
228 }
229
230 // Update package if needed. Note that there can be no race between concurrent
231 // jobs because PackageDexOptimizer.performDexOpt is synchronized.
232
233 // checkProfiles is false to avoid merging profiles during boot which
234 // might interfere with background compilation (b/28612421).
235 // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
236 // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
237 // trade-off worth doing to save boot time work.
Calin Juravle1d0e83d2017-07-17 15:12:01 -0700238 int result = pm.performDexOptWithStatus(new DexoptOptions(
239 pkg,
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200240 PackageManagerService.REASON_BOOT,
Calin Juravle1d0e83d2017-07-17 15:12:01 -0700241 DexoptOptions.DEXOPT_BOOT_COMPLETE));
Calin Juravle31ce3a82017-05-22 17:49:01 -0700242 if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
243 updatedPackages.add(pkg);
244 }
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200245 }
Calin Juravle31ce3a82017-05-22 17:49:01 -0700246 notifyPinService(updatedPackages);
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200247 // Ran to completion, so we abandon our timeslice and do not reschedule.
248 jobFinished(jobParams, /* reschedule */ false);
249 }
250
251 private boolean runIdleOptimization(final JobParameters jobParams,
252 final PackageManagerService pm, final ArraySet<String> pkgs) {
253 new Thread("BackgroundDexOptService_IdleOptimization") {
254 @Override
255 public void run() {
Calin Juravle51f521c2017-01-25 18:00:05 -0800256 int result = idleOptimization(pm, pkgs, BackgroundDexOptService.this);
Jim Kayefb7cdc3f2019-06-28 10:38:36 -0700257 if (result == OPTIMIZE_PROCESSED) {
258 Log.i(TAG, "Idle optimizations completed.");
259 } else if (result == OPTIMIZE_ABORT_NO_SPACE_LEFT) {
Calin Juravle51f521c2017-01-25 18:00:05 -0800260 Log.w(TAG, "Idle optimizations aborted because of space constraints.");
Jim Kayefb7cdc3f2019-06-28 10:38:36 -0700261 } else if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
262 Log.w(TAG, "Idle optimizations aborted by job scheduler.");
263 } else {
264 Log.w(TAG, "Idle optimizations ended with unexpected code: " + result);
265 }
266 if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
Calin Juravlec6604752017-01-25 17:08:03 -0800267 // Abandon our timeslice and do not reschedule.
268 jobFinished(jobParams, /* reschedule */ false);
269 }
David Brazdil37a87692016-04-07 10:43:18 +0100270 }
271 }.start();
272 return true;
273 }
274
Calin Juravle51f521c2017-01-25 18:00:05 -0800275 // Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700276 private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
277 Context context) {
Calin Juravlea50d58e2016-12-22 18:50:05 +0200278 Log.i(TAG, "Performing idle optimizations");
David Brazdil37a87692016-04-07 10:43:18 +0100279 // If post-boot update is still running, request that it exits early.
280 mExitPostBootUpdate.set(true);
David Brazdil37a87692016-04-07 10:43:18 +0100281 mAbortIdleOptimization.set(false);
Narayan Kamath8735f072016-08-16 18:25:08 +0100282
Calin Juravlec6604752017-01-25 17:08:03 -0800283 long lowStorageThreshold = getLowStorageThreshold(context);
Andreas Gampe76089fe2019-07-26 13:31:43 -0700284 int result = idleOptimizePackages(pm, pkgs, lowStorageThreshold);
Calin Juravle51f521c2017-01-25 18:00:05 -0800285 return result;
286 }
287
Felix Lopez Luis7ca1abd2018-12-12 10:32:32 +0000288 /**
289 * Get the size of the directory. It uses recursion to go over all files.
290 * @param f
291 * @return
292 */
293 private long getDirectorySize(File f) {
294 long size = 0;
295 if (f.isDirectory()) {
296 for (File file: f.listFiles()) {
297 size += getDirectorySize(file);
298 }
299 } else {
300 size = f.length();
301 }
302 return size;
303 }
304
305 /**
306 * Get the size of a package.
307 * @param pkg
308 */
309 private long getPackageSize(PackageManagerService pm, String pkg) {
310 PackageInfo info = pm.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
311 long size = 0;
312 if (info != null && info.applicationInfo != null) {
313 File path = Paths.get(info.applicationInfo.sourceDir).toFile();
314 if (path.isFile()) {
315 path = path.getParentFile();
316 }
317 size += getDirectorySize(path);
318 if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) {
319 for (String splitSourceDir : info.applicationInfo.splitSourceDirs) {
320 path = Paths.get(splitSourceDir).toFile();
321 if (path.isFile()) {
322 path = path.getParentFile();
323 }
324 size += getDirectorySize(path);
325 }
326 }
327 return size;
328 }
329 return 0;
330 }
331
Andreas Gampe76089fe2019-07-26 13:31:43 -0700332 private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
333 long lowStorageThreshold) {
Calin Juravle31ce3a82017-05-22 17:49:01 -0700334 ArraySet<String> updatedPackages = new ArraySet<>();
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000335
Andreas Gampe6cec0712019-07-25 14:49:53 -0700336 try {
Andreas Gampe76089fe2019-07-26 13:31:43 -0700337 final boolean supportSecondaryDex = supportSecondaryDex();
338
339 if (supportSecondaryDex) {
340 int result = reconcileSecondaryDexFiles(pm.getDexManager());
341 if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
342 return result;
343 }
344 }
345
Andreas Gampe6cec0712019-07-25 14:49:53 -0700346 // Only downgrade apps when space is low on device.
347 // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
348 // up disk before user hits the actual lowStorageThreshold.
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000349 final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE
Andreas Gampe6cec0712019-07-25 14:49:53 -0700350 * lowStorageThreshold;
351 boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
352 Log.d(TAG, "Should Downgrade " + shouldDowngrade);
353 if (shouldDowngrade) {
354 Set<String> unusedPackages =
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000355 pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
356 Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages));
Andreas Gampe6cec0712019-07-25 14:49:53 -0700357
Andreas Gampe6cec0712019-07-25 14:49:53 -0700358 if (!unusedPackages.isEmpty()) {
Andreas Gampe76089fe2019-07-26 13:31:43 -0700359 for (String pkg : unusedPackages) {
360 int abortCode = abortIdleOptimizations(/*lowStorageThreshold*/ -1);
361 if (abortCode != OPTIMIZE_CONTINUE) {
362 // Should be aborted by the scheduler.
363 return abortCode;
364 }
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000365 if (downgradePackage(pm, pkg, /*isForPrimaryDex*/ true)) {
Andreas Gampe76089fe2019-07-26 13:31:43 -0700366 updatedPackages.add(pkg);
367 }
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000368 if (supportSecondaryDex) {
369 downgradePackage(pm, pkg, /*isForPrimaryDex*/ false);
370 }
Andreas Gampe76089fe2019-07-26 13:31:43 -0700371 }
372
Andreas Gampe6cec0712019-07-25 14:49:53 -0700373 pkgs = new ArraySet<>(pkgs);
374 pkgs.removeAll(unusedPackages);
375 }
376 }
377
Andreas Gampe76089fe2019-07-26 13:31:43 -0700378 int primaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
379 /*isForPrimaryDex*/ true, updatedPackages);
380 if (primaryResult != OPTIMIZE_PROCESSED) {
381 return primaryResult;
Andreas Gampe6cec0712019-07-25 14:49:53 -0700382 }
383
Andreas Gampe76089fe2019-07-26 13:31:43 -0700384 if (!supportSecondaryDex) {
385 return OPTIMIZE_PROCESSED;
386 }
387
388 int secondaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
389 /*isForPrimaryDex*/ false, updatedPackages);
390 return secondaryResult;
Andreas Gampe6cec0712019-07-25 14:49:53 -0700391 } finally {
392 // Always let the pinner service know about changes.
393 notifyPinService(updatedPackages);
394 }
Calin Juravlec6604752017-01-25 17:08:03 -0800395 }
396
Andreas Gampe76089fe2019-07-26 13:31:43 -0700397 private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
398 long lowStorageThreshold, boolean isForPrimaryDex, ArraySet<String> updatedPackages) {
399 for (String pkg : pkgs) {
400 int abortCode = abortIdleOptimizations(lowStorageThreshold);
401 if (abortCode != OPTIMIZE_CONTINUE) {
402 // Either aborted by the scheduler or no space left.
403 return abortCode;
404 }
405
406 boolean dexOptPerformed = optimizePackage(pm, pkg, isForPrimaryDex);
407 if (dexOptPerformed) {
408 updatedPackages.add(pkg);
409 }
410 }
411 return OPTIMIZE_PROCESSED;
412 }
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000413
414 /**
415 * Try to downgrade the package to a smaller compilation filter.
416 * eg. if the package is in speed-profile the package will be downgraded to verify.
417 * @param pm PackageManagerService
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000418 * @param pkg The package to be downgraded.
419 * @param isForPrimaryDex. Apps can have several dex file, primary and secondary.
420 * @return true if the package was downgraded.
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000421 */
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000422 private boolean downgradePackage(PackageManagerService pm, String pkg,
423 boolean isForPrimaryDex) {
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000424 Log.d(TAG, "Downgrading " + pkg);
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000425 boolean dex_opt_performed = false;
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000426 int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
427 int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
428 | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB
429 | DexoptOptions.DEXOPT_DOWNGRADE;
430 long package_size_before = getPackageSize(pm, pkg);
Carmen Agimofbdcc1112019-08-02 11:23:15 +0100431
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000432 if (isForPrimaryDex) {
433 // This applies for system apps or if packages location is not a directory, i.e.
434 // monolithic install.
435 if (!pm.canHaveOatDir(pkg)) {
436 // For apps that don't have the oat directory, instead of downgrading,
437 // remove their compiler artifacts from dalvik cache.
438 pm.deleteOatArtifactsOfPackage(pkg);
439 } else {
440 dex_opt_performed = performDexOptPrimary(pm, pkg, reason, dexoptFlags);
Carmen Agimofbdcc1112019-08-02 11:23:15 +0100441 }
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000442 } else {
443 dex_opt_performed = performDexOptSecondary(pm, pkg, reason, dexoptFlags);
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000444 }
445
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000446 if (dex_opt_performed) {
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000447 StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before,
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000448 getPackageSize(pm, pkg), /*aggressive=*/ false);
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000449 }
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000450 return dex_opt_performed;
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000451 }
452
453 private boolean supportSecondaryDex() {
454 return (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false));
455 }
456
Calin Juravle51f521c2017-01-25 18:00:05 -0800457 private int reconcileSecondaryDexFiles(DexManager dm) {
458 // TODO(calin): should we blacklist packages for which we fail to reconcile?
459 for (String p : dm.getAllPackagesWithSecondaryDexFiles()) {
460 if (mAbortIdleOptimization.get()) {
461 return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
462 }
463 dm.reconcileSecondaryDexFiles(p);
464 }
465 return OPTIMIZE_PROCESSED;
466 }
467
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000468 /**
469 *
470 * Optimize package if needed. Note that there can be no race between
471 * concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
472 * @param pm An instance of PackageManagerService
473 * @param pkg The package to be downgraded.
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000474 * @param isForPrimaryDex. Apps can have several dex file, primary and secondary.
Felix Lopez Luis4bf2be92018-12-11 14:07:24 +0000475 * @return true if the package was downgraded.
476 */
477 private boolean optimizePackage(PackageManagerService pm, String pkg,
478 boolean isForPrimaryDex) {
479 int reason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
480 int dexoptFlags = DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
481 | DexoptOptions.DEXOPT_BOOT_COMPLETE
482 | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
483
484 return isForPrimaryDex
485 ? performDexOptPrimary(pm, pkg, reason, dexoptFlags)
486 : performDexOptSecondary(pm, pkg, reason, dexoptFlags);
487 }
488
489 private boolean performDexOptPrimary(PackageManagerService pm, String pkg, int reason,
490 int dexoptFlags) {
491 int result = trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ false,
492 () -> pm.performDexOptWithStatus(new DexoptOptions(pkg, reason, dexoptFlags)));
493 return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
494 }
495
496 private boolean performDexOptSecondary(PackageManagerService pm, String pkg, int reason,
497 int dexoptFlags) {
498 DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason,
499 dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX);
500 int result = trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ true,
501 () -> pm.performDexOpt(dexoptOptions)
502 ? PackageDexOptimizer.DEX_OPT_PERFORMED : PackageDexOptimizer.DEX_OPT_FAILED
503 );
504 return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
505 }
506
507 /**
508 * Execute the dexopt wrapper and make sure that if performDexOpt wrapper fails
509 * the package is added to the list of failed packages.
510 * Return one of following result:
511 * {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
512 * {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
513 * {@link PackageDexOptimizer#DEX_OPT_FAILED}
514 */
515 private int trackPerformDexOpt(String pkg, boolean isForPrimaryDex,
516 Supplier<Integer> performDexOptWrapper) {
517 ArraySet<String> sFailedPackageNames =
518 isForPrimaryDex ? sFailedPackageNamesPrimary : sFailedPackageNamesSecondary;
519 synchronized (sFailedPackageNames) {
520 if (sFailedPackageNames.contains(pkg)) {
521 // Skip previously failing package
522 return PackageDexOptimizer.DEX_OPT_SKIPPED;
523 }
524 sFailedPackageNames.add(pkg);
525 }
526 int result = performDexOptWrapper.get();
527 if (result != PackageDexOptimizer.DEX_OPT_FAILED) {
528 synchronized (sFailedPackageNames) {
529 sFailedPackageNames.remove(pkg);
530 }
531 }
532 return result;
533 }
534
Calin Juravle51f521c2017-01-25 18:00:05 -0800535 // Evaluate whether or not idle optimizations should continue.
536 private int abortIdleOptimizations(long lowStorageThreshold) {
Calin Juravlec6604752017-01-25 17:08:03 -0800537 if (mAbortIdleOptimization.get()) {
538 // JobScheduler requested an early abort.
Calin Juravle51f521c2017-01-25 18:00:05 -0800539 return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
Calin Juravlec6604752017-01-25 17:08:03 -0800540 }
541 long usableSpace = mDataDir.getUsableSpace();
542 if (usableSpace < lowStorageThreshold) {
543 // Rather bail than completely fill up the disk.
544 Log.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
Calin Juravle51f521c2017-01-25 18:00:05 -0800545 return OPTIMIZE_ABORT_NO_SPACE_LEFT;
Calin Juravlec6604752017-01-25 17:08:03 -0800546 }
547
Calin Juravle51f521c2017-01-25 18:00:05 -0800548 return OPTIMIZE_CONTINUE;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700549 }
550
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700551 // Evaluate whether apps should be downgraded.
552 private boolean shouldDowngrade(long lowStorageThresholdForDowngrade) {
553 long usableSpace = mDataDir.getUsableSpace();
554 if (usableSpace < lowStorageThresholdForDowngrade) {
555 return true;
556 }
557
558 return false;
559 }
560
Calin Juravlecb5f41e2017-01-25 17:16:08 -0800561 /**
Arthur Eubanks09dd1ec2017-09-15 09:28:51 -0700562 * Execute idle optimizations immediately on packages in packageNames. If packageNames is null,
563 * then execute on all packages.
Calin Juravlecb5f41e2017-01-25 17:16:08 -0800564 */
Arthur Eubanks09dd1ec2017-09-15 09:28:51 -0700565 public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context,
566 @Nullable List<String> packageNames) {
Calin Juravlecb5f41e2017-01-25 17:16:08 -0800567 // Create a new object to make sure we don't interfere with the scheduled jobs.
568 // Note that this may still run at the same time with the job scheduled by the
569 // JobScheduler but the scheduler will not be able to cancel it.
570 BackgroundDexOptService bdos = new BackgroundDexOptService();
Arthur Eubanks09dd1ec2017-09-15 09:28:51 -0700571 ArraySet<String> packagesToOptimize;
572 if (packageNames == null) {
573 packagesToOptimize = pm.getOptimizablePackages();
574 } else {
575 packagesToOptimize = new ArraySet<>(packageNames);
576 }
577 int result = bdos.idleOptimization(pm, packagesToOptimize, context);
Calin Juravle51f521c2017-01-25 18:00:05 -0800578 return result == OPTIMIZE_PROCESSED;
Calin Juravlecb5f41e2017-01-25 17:16:08 -0800579 }
580
Christopher Tatecf1a2f72014-06-16 15:51:39 -0700581 @Override
David Brazdil37a87692016-04-07 10:43:18 +0100582 public boolean onStartJob(JobParameters params) {
583 if (DEBUG_DEXOPT) {
584 Log.i(TAG, "onStartJob");
585 }
586
Narayan Kamath8735f072016-08-16 18:25:08 +0100587 // NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
588 // the checks above. This check is not "live" - the value is determined by a background
589 // restart with a period of ~1 minute.
David Brazdil37a87692016-04-07 10:43:18 +0100590 PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000591 if (pm.isStorageLow()) {
592 if (DEBUG_DEXOPT) {
593 Log.i(TAG, "Low storage, skipping this run");
594 }
595 return false;
596 }
David Brazdil37a87692016-04-07 10:43:18 +0100597
598 final ArraySet<String> pkgs = pm.getOptimizablePackages();
Calin Juravlec6604752017-01-25 17:08:03 -0800599 if (pkgs.isEmpty()) {
David Brazdil37a87692016-04-07 10:43:18 +0100600 if (DEBUG_DEXOPT) {
601 Log.i(TAG, "No packages to optimize");
602 }
603 return false;
604 }
605
Carmen Jacksonf107a232017-05-16 10:37:26 -0700606 boolean result;
David Brazdil37a87692016-04-07 10:43:18 +0100607 if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
Carmen Jacksonf107a232017-05-16 10:37:26 -0700608 result = runPostBootUpdate(params, pm, pkgs);
David Brazdil37a87692016-04-07 10:43:18 +0100609 } else {
Carmen Jacksonf107a232017-05-16 10:37:26 -0700610 result = runIdleOptimization(params, pm, pkgs);
David Brazdil37a87692016-04-07 10:43:18 +0100611 }
Carmen Jacksonf107a232017-05-16 10:37:26 -0700612
Carmen Jacksonf107a232017-05-16 10:37:26 -0700613 return result;
David Brazdil37a87692016-04-07 10:43:18 +0100614 }
615
616 @Override
Christopher Tatecf1a2f72014-06-16 15:51:39 -0700617 public boolean onStopJob(JobParameters params) {
David Brazdil37a87692016-04-07 10:43:18 +0100618 if (DEBUG_DEXOPT) {
619 Log.i(TAG, "onStopJob");
620 }
621
622 if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
623 mAbortPostBootUpdate.set(true);
Andreas Gampe6da7d872018-03-27 15:04:19 -0700624
625 // Do not reschedule.
626 // TODO: We should reschedule if we didn't process all apps, yet.
627 return false;
David Brazdil37a87692016-04-07 10:43:18 +0100628 } else {
629 mAbortIdleOptimization.set(true);
Andreas Gampe6da7d872018-03-27 15:04:19 -0700630
631 // Reschedule the run.
632 // TODO: Should this be dependent on the stop reason?
633 return true;
David Brazdil37a87692016-04-07 10:43:18 +0100634 }
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700635 }
Calin Juravle31ce3a82017-05-22 17:49:01 -0700636
637 private void notifyPinService(ArraySet<String> updatedPackages) {
638 PinnerService pinnerService = LocalServices.getService(PinnerService.class);
639 if (pinnerService != null) {
640 Log.i(TAG, "Pinning optimized code " + updatedPackages);
Jorim Jaggi7119800f2018-07-09 17:57:10 +0200641 pinnerService.update(updatedPackages, false /* force */);
Calin Juravle31ce3a82017-05-22 17:49:01 -0700642 }
643 }
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700644
645 private static long getDowngradeUnusedAppsThresholdInMillis() {
646 final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
647 String sysPropValue = SystemProperties.get(sysPropKey);
648 if (sysPropValue == null || sysPropValue.isEmpty()) {
649 Log.w(TAG, "SysProp " + sysPropKey + " not set");
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000650 return Long.MAX_VALUE;
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700651 }
Carmen Agimof1ef924b2019-11-22 13:48:32 +0000652 return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue));
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700653 }
Shubham Ajmera3067a1d2017-07-13 18:46:02 -0700654
655 private static boolean isBackgroundDexoptDisabled() {
656 return SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt" /* key */,
657 false /* default */);
658 }
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700659}