blob: 0774672b2fdc100bde2895fc17c76ca43b3858d3 [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;
30import android.os.BatteryManager;
Narayan Kamath8735f072016-08-16 18:25:08 +010031import android.os.Environment;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070032import android.os.ServiceManager;
Calin Juravle51f521c2017-01-25 18:00:05 -080033import android.os.SystemProperties;
Narayan Kamath8735f072016-08-16 18:25:08 +010034import android.os.storage.StorageManager;
Jeff Sharkey9f837a92014-10-24 12:07:24 -070035import android.util.ArraySet;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070036import android.util.Log;
37
Calin Juravle51f521c2017-01-25 18:00:05 -080038import com.android.server.pm.dex.DexManager;
Carmen Jacksonf107a232017-05-16 10:37:26 -070039import com.android.server.LocalServices;
40import com.android.server.PinnerService;
Calin Juravle1d0e83d2017-07-17 15:12:01 -070041import com.android.server.pm.dex.DexoptOptions;
Calin Juravle51f521c2017-01-25 18:00:05 -080042
Narayan Kamath8735f072016-08-16 18:25:08 +010043import java.io.File;
Arthur Eubanks09dd1ec2017-09-15 09:28:51 -070044import java.util.List;
Shubham Ajmera246dccf2017-05-24 17:46:36 -070045import java.util.Set;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070046import java.util.concurrent.atomic.AtomicBoolean;
Nicolas Geoffray27c07372015-11-05 16:54:09 +000047import java.util.concurrent.TimeUnit;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070048
49/**
50 * {@hide}
51 */
Christopher Tatecf1a2f72014-06-16 15:51:39 -070052public class BackgroundDexOptService extends JobService {
Calin Juravlea50d58e2016-12-22 18:50:05 +020053 private static final String TAG = "BackgroundDexOptService";
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070054
Calin Juravlea50d58e2016-12-22 18:50:05 +020055 private static final boolean DEBUG = false;
Christopher Tate2c9655b2015-06-12 13:06:45 -070056
Calin Juravlea50d58e2016-12-22 18:50:05 +020057 private static final int JOB_IDLE_OPTIMIZE = 800;
58 private static final int JOB_POST_BOOT_UPDATE = 801;
59
60 private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG
61 ? TimeUnit.MINUTES.toMillis(1)
62 : TimeUnit.DAYS.toMillis(1);
David Brazdil37a87692016-04-07 10:43:18 +010063
Christopher Tatecf1a2f72014-06-16 15:51:39 -070064 private static ComponentName sDexoptServiceName = new ComponentName(
Christopher Tate1b8b3aa2014-06-20 13:17:01 -070065 "android",
Christopher Tatecf1a2f72014-06-16 15:51:39 -070066 BackgroundDexOptService.class.getName());
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070067
Calin Juravle51f521c2017-01-25 18:00:05 -080068 // Possible return codes of individual optimization steps.
69
70 // Optimizations finished. All packages were processed.
71 private static final int OPTIMIZE_PROCESSED = 0;
72 // Optimizations should continue. Issued after checking the scheduler, disk space or battery.
73 private static final int OPTIMIZE_CONTINUE = 1;
74 // Optimizations should be aborted. Job scheduler requested it.
75 private static final int OPTIMIZE_ABORT_BY_JOB_SCHEDULER = 2;
76 // Optimizations should be aborted. No space left on device.
77 private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3;
78
Shubham Ajmera246dccf2017-05-24 17:46:36 -070079 // Used for calculating space threshold for downgrading unused apps.
80 private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2;
81
Brian Carlstroma00be9b2014-12-12 13:14:36 -080082 /**
83 * Set of failed packages remembered across job runs.
84 */
Calin Juravle51f521c2017-01-25 18:00:05 -080085 static final ArraySet<String> sFailedPackageNamesPrimary = new ArraySet<String>();
86 static final ArraySet<String> sFailedPackageNamesSecondary = new ArraySet<String>();
Brian Carlstroma00be9b2014-12-12 13:14:36 -080087
David Brazdil37a87692016-04-07 10:43:18 +010088 /**
89 * Atomics set to true if the JobScheduler requests an abort.
90 */
Calin Juravle51f521c2017-01-25 18:00:05 -080091 private final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
92 private final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
David Brazdil37a87692016-04-07 10:43:18 +010093
94 /**
95 * Atomic set to true if one job should exit early because another job was started.
96 */
Calin Juravle51f521c2017-01-25 18:00:05 -080097 private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
Brian Carlstrom7395a8a2014-04-28 22:11:01 -070098
Calin Juravlebe6a71a2016-12-22 18:50:05 +020099 private final File mDataDir = Environment.getDataDirectory();
Narayan Kamath8735f072016-08-16 18:25:08 +0100100
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700101 private static final long mDowngradeUnusedAppsThresholdInMillis =
102 getDowngradeUnusedAppsThresholdInMillis();
103
Calin Juravledb4a79a2015-12-23 18:55:08 +0200104 public static void schedule(Context context) {
Shubham Ajmera3067a1d2017-07-13 18:46:02 -0700105 if (isBackgroundDexoptDisabled()) {
106 return;
107 }
108
Christopher Tatecf1a2f72014-06-16 15:51:39 -0700109 JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
David Brazdil37a87692016-04-07 10:43:18 +0100110
111 // Schedule a one-off job which scans installed packages and updates
112 // out-of-date oat files.
113 js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
114 .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
115 .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
116 .build());
117
118 // Schedule a daily job which scans installed packages and compiles
119 // those with fresh profiling data.
120 js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
121 .setRequiresDeviceIdle(true)
122 .setRequiresCharging(true)
Calin Juravlea50d58e2016-12-22 18:50:05 +0200123 .setPeriodic(IDLE_OPTIMIZATION_PERIOD)
David Brazdil37a87692016-04-07 10:43:18 +0100124 .build());
125
126 if (DEBUG_DEXOPT) {
127 Log.i(TAG, "Jobs scheduled");
128 }
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700129 }
130
David Brazdilace80c52016-04-11 13:37:29 +0100131 public static void notifyPackageChanged(String packageName) {
132 // The idle maintanance job skips packages which previously failed to
133 // compile. The given package has changed and may successfully compile
134 // now. Remove it from the list of known failing packages.
Calin Juravle51f521c2017-01-25 18:00:05 -0800135 synchronized (sFailedPackageNamesPrimary) {
136 sFailedPackageNamesPrimary.remove(packageName);
137 }
138 synchronized (sFailedPackageNamesSecondary) {
139 sFailedPackageNamesSecondary.remove(packageName);
David Brazdilace80c52016-04-11 13:37:29 +0100140 }
141 }
142
David Brazdil37a87692016-04-07 10:43:18 +0100143 // Returns the current battery level as a 0-100 integer.
144 private int getBatteryLevel() {
145 IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
146 Intent intent = registerReceiver(null, filter);
147 int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
148 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
Todd Poynor1cd7cc82018-02-20 20:11:43 -0800149 boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
150
151 if (!present) {
152 // No battery, treat as if 100%, no possibility of draining battery.
153 return 100;
154 }
Christopher Tatecf1a2f72014-06-16 15:51:39 -0700155
David Brazdil37a87692016-04-07 10:43:18 +0100156 if (level < 0 || scale <= 0) {
157 // Battery data unavailable. This should never happen, so assume the worst.
158 return 0;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700159 }
David Brazdil37a87692016-04-07 10:43:18 +0100160
161 return (100 * level / scale);
162 }
163
Calin Juravlec6604752017-01-25 17:08:03 -0800164 private long getLowStorageThreshold(Context context) {
Narayan Kamath8735f072016-08-16 18:25:08 +0100165 @SuppressWarnings("deprecation")
Calin Juravlec6604752017-01-25 17:08:03 -0800166 final long lowThreshold = StorageManager.from(context).getStorageLowBytes(mDataDir);
Narayan Kamath8735f072016-08-16 18:25:08 +0100167 if (lowThreshold == 0) {
168 Log.e(TAG, "Invalid low storage threshold");
169 }
170
171 return lowThreshold;
172 }
173
David Brazdil37a87692016-04-07 10:43:18 +0100174 private boolean runPostBootUpdate(final JobParameters jobParams,
175 final PackageManagerService pm, final ArraySet<String> pkgs) {
176 if (mExitPostBootUpdate.get()) {
177 // This job has already been superseded. Do not start it.
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700178 return false;
179 }
David Brazdil37a87692016-04-07 10:43:18 +0100180 new Thread("BackgroundDexOptService_PostBootUpdate") {
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700181 @Override
182 public void run() {
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200183 postBootUpdate(jobParams, pm, pkgs);
184 }
Narayan Kamath8735f072016-08-16 18:25:08 +0100185
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200186 }.start();
187 return true;
188 }
Calin Juravle8412cf42016-05-09 17:56:18 +0100189
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200190 private void postBootUpdate(JobParameters jobParams, PackageManagerService pm,
191 ArraySet<String> pkgs) {
192 // Load low battery threshold from the system config. This is a 0-100 integer.
193 final int lowBatteryThreshold = getResources().getInteger(
194 com.android.internal.R.integer.config_lowBatteryWarningLevel);
Calin Juravlec6604752017-01-25 17:08:03 -0800195 final long lowThreshold = getLowStorageThreshold(this);
Calin Juravle8412cf42016-05-09 17:56:18 +0100196
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200197 mAbortPostBootUpdate.set(false);
198
Calin Juravle31ce3a82017-05-22 17:49:01 -0700199 ArraySet<String> updatedPackages = new ArraySet<>();
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200200 for (String pkg : pkgs) {
201 if (mAbortPostBootUpdate.get()) {
202 // JobScheduler requested an early abort.
203 return;
204 }
205 if (mExitPostBootUpdate.get()) {
206 // Different job, which supersedes this one, is running.
207 break;
208 }
209 if (getBatteryLevel() < lowBatteryThreshold) {
210 // Rather bail than completely drain the battery.
211 break;
212 }
213 long usableSpace = mDataDir.getUsableSpace();
214 if (usableSpace < lowThreshold) {
215 // Rather bail than completely fill up the disk.
216 Log.w(TAG, "Aborting background dex opt job due to low storage: " +
217 usableSpace);
218 break;
219 }
220
221 if (DEBUG_DEXOPT) {
222 Log.i(TAG, "Updating package " + pkg);
223 }
224
225 // Update package if needed. Note that there can be no race between concurrent
226 // jobs because PackageDexOptimizer.performDexOpt is synchronized.
227
228 // checkProfiles is false to avoid merging profiles during boot which
229 // might interfere with background compilation (b/28612421).
230 // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
231 // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
232 // trade-off worth doing to save boot time work.
Calin Juravle1d0e83d2017-07-17 15:12:01 -0700233 int result = pm.performDexOptWithStatus(new DexoptOptions(
234 pkg,
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200235 PackageManagerService.REASON_BOOT,
Calin Juravle1d0e83d2017-07-17 15:12:01 -0700236 DexoptOptions.DEXOPT_BOOT_COMPLETE));
Calin Juravle31ce3a82017-05-22 17:49:01 -0700237 if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
238 updatedPackages.add(pkg);
239 }
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200240 }
Calin Juravle31ce3a82017-05-22 17:49:01 -0700241 notifyPinService(updatedPackages);
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200242 // Ran to completion, so we abandon our timeslice and do not reschedule.
243 jobFinished(jobParams, /* reschedule */ false);
244 }
245
246 private boolean runIdleOptimization(final JobParameters jobParams,
247 final PackageManagerService pm, final ArraySet<String> pkgs) {
248 new Thread("BackgroundDexOptService_IdleOptimization") {
249 @Override
250 public void run() {
Calin Juravle51f521c2017-01-25 18:00:05 -0800251 int result = idleOptimization(pm, pkgs, BackgroundDexOptService.this);
252 if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
253 Log.w(TAG, "Idle optimizations aborted because of space constraints.");
Calin Juravlec6604752017-01-25 17:08:03 -0800254 // If we didn't abort we ran to completion (or stopped because of space).
255 // Abandon our timeslice and do not reschedule.
256 jobFinished(jobParams, /* reschedule */ false);
257 }
David Brazdil37a87692016-04-07 10:43:18 +0100258 }
259 }.start();
260 return true;
261 }
262
Calin Juravle51f521c2017-01-25 18:00:05 -0800263 // Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700264 private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
265 Context context) {
Calin Juravlea50d58e2016-12-22 18:50:05 +0200266 Log.i(TAG, "Performing idle optimizations");
David Brazdil37a87692016-04-07 10:43:18 +0100267 // If post-boot update is still running, request that it exits early.
268 mExitPostBootUpdate.set(true);
David Brazdil37a87692016-04-07 10:43:18 +0100269 mAbortIdleOptimization.set(false);
Narayan Kamath8735f072016-08-16 18:25:08 +0100270
Calin Juravlec6604752017-01-25 17:08:03 -0800271 long lowStorageThreshold = getLowStorageThreshold(context);
Calin Juravle51f521c2017-01-25 18:00:05 -0800272 // Optimize primary apks.
273 int result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ true,
274 sFailedPackageNamesPrimary);
Calin Juravlec6604752017-01-25 17:08:03 -0800275
Calin Juravle51f521c2017-01-25 18:00:05 -0800276 if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
277 return result;
278 }
279
Calin Juravle4466c5a2017-02-07 11:09:01 -0800280 if (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
Calin Juravle51f521c2017-01-25 18:00:05 -0800281 result = reconcileSecondaryDexFiles(pm.getDexManager());
282 if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
283 return result;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700284 }
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200285
Calin Juravle51f521c2017-01-25 18:00:05 -0800286 result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ false,
287 sFailedPackageNamesSecondary);
288 }
289 return result;
290 }
291
292 private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
293 long lowStorageThreshold, boolean is_for_primary_dex,
294 ArraySet<String> failedPackageNames) {
Calin Juravle31ce3a82017-05-22 17:49:01 -0700295 ArraySet<String> updatedPackages = new ArraySet<>();
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700296 Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
297 // Only downgrade apps when space is low on device.
298 // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
299 // up disk before user hits the actual lowStorageThreshold.
300 final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE *
301 lowStorageThreshold;
302 boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
Calin Juravle51f521c2017-01-25 18:00:05 -0800303 for (String pkg : pkgs) {
304 int abort_code = abortIdleOptimizations(lowStorageThreshold);
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700305 if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
Calin Juravle51f521c2017-01-25 18:00:05 -0800306 return abort_code;
307 }
308
309 synchronized (failedPackageNames) {
310 if (failedPackageNames.contains(pkg)) {
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200311 // Skip previously failing package
312 continue;
313 }
314 }
315
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700316 int reason;
317 boolean downgrade;
318 // Downgrade unused packages.
319 if (unusedPackages.contains(pkg) && shouldDowngrade) {
320 // This applies for system apps or if packages location is not a directory, i.e.
321 // monolithic install.
322 if (is_for_primary_dex && !pm.canHaveOatDir(pkg)) {
323 // For apps that don't have the oat directory, instead of downgrading,
324 // remove their compiler artifacts from dalvik cache.
325 pm.deleteOatArtifactsOfPackage(pkg);
326 continue;
327 } else {
328 reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
329 downgrade = true;
330 }
331 } else if (abort_code != OPTIMIZE_ABORT_NO_SPACE_LEFT) {
332 reason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
333 downgrade = false;
334 } else {
335 // can't dexopt because of low space.
336 continue;
337 }
338
339 synchronized (failedPackageNames) {
340 // Conservatively add package to the list of failing ones in case
341 // performDexOpt never returns.
342 failedPackageNames.add(pkg);
343 }
344
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200345 // Optimize package if needed. Note that there can be no race between
346 // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
Calin Juravle31ce3a82017-05-22 17:49:01 -0700347 boolean success;
Calin Juravle1d0e83d2017-07-17 15:12:01 -0700348 int dexoptFlags =
349 DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
350 DexoptOptions.DEXOPT_BOOT_COMPLETE |
David Sehr2118ec42017-10-25 14:28:29 -0700351 (downgrade ? DexoptOptions.DEXOPT_DOWNGRADE : 0) |
352 DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
Calin Juravle31ce3a82017-05-22 17:49:01 -0700353 if (is_for_primary_dex) {
Shubham Ajmerad8200862017-09-18 21:13:59 -0700354 int result = pm.performDexOptWithStatus(new DexoptOptions(pkg, reason,
Calin Juravle1d0e83d2017-07-17 15:12:01 -0700355 dexoptFlags));
Calin Juravle31ce3a82017-05-22 17:49:01 -0700356 success = result != PackageDexOptimizer.DEX_OPT_FAILED;
357 if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
358 updatedPackages.add(pkg);
359 }
360 } else {
Calin Juravle1d0e83d2017-07-17 15:12:01 -0700361 success = pm.performDexOpt(new DexoptOptions(pkg,
Shubham Ajmerad8200862017-09-18 21:13:59 -0700362 reason, dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX));
Calin Juravle31ce3a82017-05-22 17:49:01 -0700363 }
Calin Juravle51f521c2017-01-25 18:00:05 -0800364 if (success) {
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200365 // Dexopt succeeded, remove package from the list of failing ones.
Calin Juravle51f521c2017-01-25 18:00:05 -0800366 synchronized (failedPackageNames) {
367 failedPackageNames.remove(pkg);
Calin Juravlebe6a71a2016-12-22 18:50:05 +0200368 }
369 }
370 }
Calin Juravle31ce3a82017-05-22 17:49:01 -0700371 notifyPinService(updatedPackages);
Calin Juravle51f521c2017-01-25 18:00:05 -0800372 return OPTIMIZE_PROCESSED;
Calin Juravlec6604752017-01-25 17:08:03 -0800373 }
374
Calin Juravle51f521c2017-01-25 18:00:05 -0800375 private int reconcileSecondaryDexFiles(DexManager dm) {
376 // TODO(calin): should we blacklist packages for which we fail to reconcile?
377 for (String p : dm.getAllPackagesWithSecondaryDexFiles()) {
378 if (mAbortIdleOptimization.get()) {
379 return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
380 }
381 dm.reconcileSecondaryDexFiles(p);
382 }
383 return OPTIMIZE_PROCESSED;
384 }
385
386 // Evaluate whether or not idle optimizations should continue.
387 private int abortIdleOptimizations(long lowStorageThreshold) {
Calin Juravlec6604752017-01-25 17:08:03 -0800388 if (mAbortIdleOptimization.get()) {
389 // JobScheduler requested an early abort.
Calin Juravle51f521c2017-01-25 18:00:05 -0800390 return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
Calin Juravlec6604752017-01-25 17:08:03 -0800391 }
392 long usableSpace = mDataDir.getUsableSpace();
393 if (usableSpace < lowStorageThreshold) {
394 // Rather bail than completely fill up the disk.
395 Log.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
Calin Juravle51f521c2017-01-25 18:00:05 -0800396 return OPTIMIZE_ABORT_NO_SPACE_LEFT;
Calin Juravlec6604752017-01-25 17:08:03 -0800397 }
398
Calin Juravle51f521c2017-01-25 18:00:05 -0800399 return OPTIMIZE_CONTINUE;
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700400 }
401
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700402 // Evaluate whether apps should be downgraded.
403 private boolean shouldDowngrade(long lowStorageThresholdForDowngrade) {
404 long usableSpace = mDataDir.getUsableSpace();
405 if (usableSpace < lowStorageThresholdForDowngrade) {
406 return true;
407 }
408
409 return false;
410 }
411
Calin Juravlecb5f41e2017-01-25 17:16:08 -0800412 /**
Arthur Eubanks09dd1ec2017-09-15 09:28:51 -0700413 * Execute idle optimizations immediately on packages in packageNames. If packageNames is null,
414 * then execute on all packages.
Calin Juravlecb5f41e2017-01-25 17:16:08 -0800415 */
Arthur Eubanks09dd1ec2017-09-15 09:28:51 -0700416 public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context,
417 @Nullable List<String> packageNames) {
Calin Juravlecb5f41e2017-01-25 17:16:08 -0800418 // Create a new object to make sure we don't interfere with the scheduled jobs.
419 // Note that this may still run at the same time with the job scheduled by the
420 // JobScheduler but the scheduler will not be able to cancel it.
421 BackgroundDexOptService bdos = new BackgroundDexOptService();
Arthur Eubanks09dd1ec2017-09-15 09:28:51 -0700422 ArraySet<String> packagesToOptimize;
423 if (packageNames == null) {
424 packagesToOptimize = pm.getOptimizablePackages();
425 } else {
426 packagesToOptimize = new ArraySet<>(packageNames);
427 }
428 int result = bdos.idleOptimization(pm, packagesToOptimize, context);
Calin Juravle51f521c2017-01-25 18:00:05 -0800429 return result == OPTIMIZE_PROCESSED;
Calin Juravlecb5f41e2017-01-25 17:16:08 -0800430 }
431
Christopher Tatecf1a2f72014-06-16 15:51:39 -0700432 @Override
David Brazdil37a87692016-04-07 10:43:18 +0100433 public boolean onStartJob(JobParameters params) {
434 if (DEBUG_DEXOPT) {
435 Log.i(TAG, "onStartJob");
436 }
437
Narayan Kamath8735f072016-08-16 18:25:08 +0100438 // NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
439 // the checks above. This check is not "live" - the value is determined by a background
440 // restart with a period of ~1 minute.
David Brazdil37a87692016-04-07 10:43:18 +0100441 PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
442 if (pm.isStorageLow()) {
443 if (DEBUG_DEXOPT) {
444 Log.i(TAG, "Low storage, skipping this run");
445 }
446 return false;
447 }
448
449 final ArraySet<String> pkgs = pm.getOptimizablePackages();
Calin Juravlec6604752017-01-25 17:08:03 -0800450 if (pkgs.isEmpty()) {
David Brazdil37a87692016-04-07 10:43:18 +0100451 if (DEBUG_DEXOPT) {
452 Log.i(TAG, "No packages to optimize");
453 }
454 return false;
455 }
456
Carmen Jacksonf107a232017-05-16 10:37:26 -0700457 boolean result;
David Brazdil37a87692016-04-07 10:43:18 +0100458 if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
Carmen Jacksonf107a232017-05-16 10:37:26 -0700459 result = runPostBootUpdate(params, pm, pkgs);
David Brazdil37a87692016-04-07 10:43:18 +0100460 } else {
Carmen Jacksonf107a232017-05-16 10:37:26 -0700461 result = runIdleOptimization(params, pm, pkgs);
David Brazdil37a87692016-04-07 10:43:18 +0100462 }
Carmen Jacksonf107a232017-05-16 10:37:26 -0700463
Carmen Jacksonf107a232017-05-16 10:37:26 -0700464 return result;
David Brazdil37a87692016-04-07 10:43:18 +0100465 }
466
467 @Override
Christopher Tatecf1a2f72014-06-16 15:51:39 -0700468 public boolean onStopJob(JobParameters params) {
David Brazdil37a87692016-04-07 10:43:18 +0100469 if (DEBUG_DEXOPT) {
470 Log.i(TAG, "onStopJob");
471 }
472
473 if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
474 mAbortPostBootUpdate.set(true);
Andreas Gampec157be02018-03-27 15:04:19 -0700475
476 // Do not reschedule.
477 // TODO: We should reschedule if we didn't process all apps, yet.
478 return false;
David Brazdil37a87692016-04-07 10:43:18 +0100479 } else {
480 mAbortIdleOptimization.set(true);
Andreas Gampec157be02018-03-27 15:04:19 -0700481
482 // Reschedule the run.
483 // TODO: Should this be dependent on the stop reason?
484 return true;
David Brazdil37a87692016-04-07 10:43:18 +0100485 }
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700486 }
Calin Juravle31ce3a82017-05-22 17:49:01 -0700487
488 private void notifyPinService(ArraySet<String> updatedPackages) {
489 PinnerService pinnerService = LocalServices.getService(PinnerService.class);
490 if (pinnerService != null) {
491 Log.i(TAG, "Pinning optimized code " + updatedPackages);
492 pinnerService.update(updatedPackages);
493 }
494 }
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700495
496 private static long getDowngradeUnusedAppsThresholdInMillis() {
497 final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
498 String sysPropValue = SystemProperties.get(sysPropKey);
499 if (sysPropValue == null || sysPropValue.isEmpty()) {
500 Log.w(TAG, "SysProp " + sysPropKey + " not set");
501 return Long.MAX_VALUE;
502 }
503 return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue));
504 }
Shubham Ajmera3067a1d2017-07-13 18:46:02 -0700505
506 private static boolean isBackgroundDexoptDisabled() {
507 return SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt" /* key */,
508 false /* default */);
509 }
Brian Carlstrom7395a8a2014-04-28 22:11:01 -0700510}