BackgroundDexOptService.java revision cb5f41ea11b1a6fcd0977a64ee146dde8f537076
17395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom/*
27395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * Copyright (C) 2014 The Android Open Source Project
37395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom *
47395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * Licensed under the Apache License, Version 2.0 (the "License");
57395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * you may not use this file except in compliance with the License.
67395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * You may obtain a copy of the License at
77395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom *
87395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom *      http://www.apache.org/licenses/LICENSE-2.0
97395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom *
107395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * Unless required by applicable law or agreed to in writing, software
117395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * distributed under the License is distributed on an "AS IS" BASIS,
127395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * See the License for the specific language governing permissions and
147395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * limitations under the License.
157395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom */
167395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
177395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrompackage com.android.server.pm;
187395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
1937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdilimport static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
2037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
212c9655b3d4c1fb0687baa14730c6d97ab5a56789Christopher Tateimport android.app.AlarmManager;
22cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tateimport android.app.job.JobInfo;
23cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tateimport android.app.job.JobParameters;
24cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tateimport android.app.job.JobScheduler;
25cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tateimport android.app.job.JobService;
26cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tateimport android.content.ComponentName;
277395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstromimport android.content.Context;
2837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdilimport android.content.Intent;
2937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdilimport android.content.IntentFilter;
3037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdilimport android.os.BatteryManager;
318735f07a698218d311826a140d08d611d3c23db1Narayan Kamathimport android.os.Environment;
327395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstromimport android.os.ServiceManager;
338735f07a698218d311826a140d08d611d3c23db1Narayan Kamathimport android.os.storage.StorageManager;
349f837a99d48c5bb8ad7fbc133943e5bf622ce065Jeff Sharkeyimport android.util.ArraySet;
357395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstromimport android.util.Log;
367395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
378735f07a698218d311826a140d08d611d3c23db1Narayan Kamathimport java.io.File;
387395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstromimport java.util.concurrent.atomic.AtomicBoolean;
3927c073796978106746e4a51f2100b29068ab37f6Nicolas Geoffrayimport java.util.concurrent.TimeUnit;
407395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
417395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom/**
427395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * {@hide}
437395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom */
44cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tatepublic class BackgroundDexOptService extends JobService {
45a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final String TAG = "BackgroundDexOptService";
467395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
47a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final boolean DEBUG = false;
482c9655b3d4c1fb0687baa14730c6d97ab5a56789Christopher Tate
49a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR;
50a50d58e22630cd651a815381639e70476991bdbfCalin Juravle
51a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final int JOB_IDLE_OPTIMIZE = 800;
52a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final int JOB_POST_BOOT_UPDATE = 801;
53a50d58e22630cd651a815381639e70476991bdbfCalin Juravle
54a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG
55a50d58e22630cd651a815381639e70476991bdbfCalin Juravle            ? TimeUnit.MINUTES.toMillis(1)
56a50d58e22630cd651a815381639e70476991bdbfCalin Juravle            : TimeUnit.DAYS.toMillis(1);
5737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
58cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate    private static ComponentName sDexoptServiceName = new ComponentName(
591b8b3aa265190e84467f740e99a0ade3a0e3cd67Christopher Tate            "android",
60cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate            BackgroundDexOptService.class.getName());
617395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
62a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom    /**
63a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom     * Set of failed packages remembered across job runs.
64a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom     */
65a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom    static final ArraySet<String> sFailedPackageNames = new ArraySet<String>();
66a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom
6737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    /**
6837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     * Atomics set to true if the JobScheduler requests an abort.
6937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     */
7037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
7137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
7237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
7337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    /**
7437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     * Atomic set to true if one job should exit early because another job was started.
7537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     */
7637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
777395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
78be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    private final File mDataDir = Environment.getDataDirectory();
798735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
80db4a79a5d7d348e9d2286d95d4e5a59dd484456fCalin Juravle    public static void schedule(Context context) {
81cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
8237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
8337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // Schedule a one-off job which scans installed packages and updates
8437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // out-of-date oat files.
8537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
8637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
8737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
8837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .build());
8937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
9037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // Schedule a daily job which scans installed packages and compiles
9137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // those with fresh profiling data.
9237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
9337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setRequiresDeviceIdle(true)
9437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setRequiresCharging(true)
95a50d58e22630cd651a815381639e70476991bdbfCalin Juravle                    .setPeriodic(IDLE_OPTIMIZATION_PERIOD)
9637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .build());
9737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
9837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (DEBUG_DEXOPT) {
9937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            Log.i(TAG, "Jobs scheduled");
10037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
1017395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom    }
1027395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
103ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil    public static void notifyPackageChanged(String packageName) {
104ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        // The idle maintanance job skips packages which previously failed to
105ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        // compile. The given package has changed and may successfully compile
106ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        // now. Remove it from the list of known failing packages.
107ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        synchronized (sFailedPackageNames) {
108ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil            sFailedPackageNames.remove(packageName);
109ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        }
110ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil    }
111ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil
11237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    // Returns the current battery level as a 0-100 integer.
11337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    private int getBatteryLevel() {
11437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
11537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        Intent intent = registerReceiver(null, filter);
11637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
11737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
118cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate
11937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (level < 0 || scale <= 0) {
12037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            // Battery data unavailable. This should never happen, so assume the worst.
12137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return 0;
1227395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom        }
12337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
12437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        return (100 * level / scale);
12537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    }
12637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
127c660475aafe91269be306c802823cf543005ce36Calin Juravle    private long getLowStorageThreshold(Context context) {
1288735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        @SuppressWarnings("deprecation")
129c660475aafe91269be306c802823cf543005ce36Calin Juravle        final long lowThreshold = StorageManager.from(context).getStorageLowBytes(mDataDir);
1308735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        if (lowThreshold == 0) {
1318735f07a698218d311826a140d08d611d3c23db1Narayan Kamath            Log.e(TAG, "Invalid low storage threshold");
1328735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        }
1338735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
1348735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        return lowThreshold;
1358735f07a698218d311826a140d08d611d3c23db1Narayan Kamath    }
1368735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
13737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    private boolean runPostBootUpdate(final JobParameters jobParams,
13837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            final PackageManagerService pm, final ArraySet<String> pkgs) {
13937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (mExitPostBootUpdate.get()) {
14037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            // This job has already been superseded. Do not start it.
1417395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            return false;
1427395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom        }
143be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        new Thread("BackgroundDexOptService_PostBootUpdate") {
144be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            @Override
145be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            public void run() {
146be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                postBootUpdate(jobParams, pm, pkgs);
147be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
148be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
149be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        }.start();
150be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        return true;
151be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    }
152cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate
153be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    private void postBootUpdate(JobParameters jobParams, PackageManagerService pm,
154be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            ArraySet<String> pkgs) {
15537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // Load low battery threshold from the system config. This is a 0-100 integer.
15637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        final int lowBatteryThreshold = getResources().getInteger(
15737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                com.android.internal.R.integer.config_lowBatteryWarningLevel);
158c660475aafe91269be306c802823cf543005ce36Calin Juravle        final long lowThreshold = getLowStorageThreshold(this);
1598735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
16037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        mAbortPostBootUpdate.set(false);
161be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
162be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        for (String pkg : pkgs) {
163be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (mAbortPostBootUpdate.get()) {
164be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // JobScheduler requested an early abort.
165be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                return;
166be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
167be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (mExitPostBootUpdate.get()) {
168be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Different job, which supersedes this one, is running.
169be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                break;
170be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
171be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (getBatteryLevel() < lowBatteryThreshold) {
172be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Rather bail than completely drain the battery.
173be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                break;
174be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
175be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            long usableSpace = mDataDir.getUsableSpace();
176be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (usableSpace < lowThreshold) {
177be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Rather bail than completely fill up the disk.
178be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                Log.w(TAG, "Aborting background dex opt job due to low storage: " +
179be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                        usableSpace);
180be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                break;
181be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
182be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
183be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (DEBUG_DEXOPT) {
184be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                Log.i(TAG, "Updating package " + pkg);
185be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
186be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
187be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // Update package if needed. Note that there can be no race between concurrent
188be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // jobs because PackageDexOptimizer.performDexOpt is synchronized.
189be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
190be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // checkProfiles is false to avoid merging profiles during boot which
191be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // might interfere with background compilation (b/28612421).
192be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
193be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
194be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // trade-off worth doing to save boot time work.
195be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            pm.performDexOpt(pkg,
196be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    /* checkProfiles */ false,
197be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    PackageManagerService.REASON_BOOT,
198be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    /* force */ false);
199be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        }
200be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        // Ran to completion, so we abandon our timeslice and do not reschedule.
201be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        jobFinished(jobParams, /* reschedule */ false);
202be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    }
203be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
204be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    private boolean runIdleOptimization(final JobParameters jobParams,
205be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            final PackageManagerService pm, final ArraySet<String> pkgs) {
206be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        new Thread("BackgroundDexOptService_IdleOptimization") {
2077395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            @Override
2087395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            public void run() {
209c660475aafe91269be306c802823cf543005ce36Calin Juravle                idleOptimization(pm, pkgs, BackgroundDexOptService.this);
210c660475aafe91269be306c802823cf543005ce36Calin Juravle                if (!mAbortIdleOptimization.get()) {
211c660475aafe91269be306c802823cf543005ce36Calin Juravle                    // If we didn't abort we ran to completion (or stopped because of space).
212c660475aafe91269be306c802823cf543005ce36Calin Juravle                    // Abandon our timeslice and do not reschedule.
213c660475aafe91269be306c802823cf543005ce36Calin Juravle                    jobFinished(jobParams, /* reschedule */ false);
214c660475aafe91269be306c802823cf543005ce36Calin Juravle                }
21537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            }
21637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }.start();
21737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        return true;
21837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    }
21937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
220c660475aafe91269be306c802823cf543005ce36Calin Juravle    // Optimize the given packages and return true if the process was not aborted.
221c660475aafe91269be306c802823cf543005ce36Calin Juravle    // The abort can happen either because of job scheduler or because of lack of space.
222c660475aafe91269be306c802823cf543005ce36Calin Juravle    private boolean idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
223c660475aafe91269be306c802823cf543005ce36Calin Juravle            Context context) {
224a50d58e22630cd651a815381639e70476991bdbfCalin Juravle        Log.i(TAG, "Performing idle optimizations");
22537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // If post-boot update is still running, request that it exits early.
22637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        mExitPostBootUpdate.set(true);
22737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        mAbortIdleOptimization.set(false);
2288735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
229c660475aafe91269be306c802823cf543005ce36Calin Juravle        long lowStorageThreshold = getLowStorageThreshold(context);
230c660475aafe91269be306c802823cf543005ce36Calin Juravle        return optimizePackages(pm, pkgs, lowStorageThreshold);
231c660475aafe91269be306c802823cf543005ce36Calin Juravle    }
232c660475aafe91269be306c802823cf543005ce36Calin Juravle
233c660475aafe91269be306c802823cf543005ce36Calin Juravle    private boolean optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
234c660475aafe91269be306c802823cf543005ce36Calin Juravle            long lowStorageThreshold) {
235be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        for (String pkg : pkgs) {
236c660475aafe91269be306c802823cf543005ce36Calin Juravle            if (abortIdleOptimizations(lowStorageThreshold)) {
237c660475aafe91269be306c802823cf543005ce36Calin Juravle                return false;
238be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
2398735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
240be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            synchronized (sFailedPackageNames) {
241be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                if (sFailedPackageNames.contains(pkg)) {
242be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    // Skip previously failing package
243be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    continue;
244c660475aafe91269be306c802823cf543005ce36Calin Juravle                } else {
245c660475aafe91269be306c802823cf543005ce36Calin Juravle                    // Conservatively add package to the list of failing ones in case performDexOpt
246c660475aafe91269be306c802823cf543005ce36Calin Juravle                    // never returns.
247c660475aafe91269be306c802823cf543005ce36Calin Juravle                    sFailedPackageNames.add(pkg);
2487395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom                }
2497395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            }
250be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
251be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // Optimize package if needed. Note that there can be no race between
252be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
253be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (pm.performDexOpt(pkg,
254be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    /* checkProfiles */ true,
255be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    PackageManagerService.REASON_BACKGROUND_DEXOPT,
256be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    /* force */ false)) {
257be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Dexopt succeeded, remove package from the list of failing ones.
258be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                synchronized (sFailedPackageNames) {
259be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    sFailedPackageNames.remove(pkg);
260be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                }
261be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
262be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        }
263c660475aafe91269be306c802823cf543005ce36Calin Juravle        return true;
264c660475aafe91269be306c802823cf543005ce36Calin Juravle    }
265c660475aafe91269be306c802823cf543005ce36Calin Juravle
266c660475aafe91269be306c802823cf543005ce36Calin Juravle    // Return true if the idle optimizations should be aborted because of a space constraints
267c660475aafe91269be306c802823cf543005ce36Calin Juravle    // or because the JobScheduler requested so.
268c660475aafe91269be306c802823cf543005ce36Calin Juravle    private boolean abortIdleOptimizations(long lowStorageThreshold) {
269c660475aafe91269be306c802823cf543005ce36Calin Juravle        if (mAbortIdleOptimization.get()) {
270c660475aafe91269be306c802823cf543005ce36Calin Juravle            // JobScheduler requested an early abort.
271c660475aafe91269be306c802823cf543005ce36Calin Juravle            return true;
272c660475aafe91269be306c802823cf543005ce36Calin Juravle        }
273c660475aafe91269be306c802823cf543005ce36Calin Juravle        long usableSpace = mDataDir.getUsableSpace();
274c660475aafe91269be306c802823cf543005ce36Calin Juravle        if (usableSpace < lowStorageThreshold) {
275c660475aafe91269be306c802823cf543005ce36Calin Juravle            // Rather bail than completely fill up the disk.
276c660475aafe91269be306c802823cf543005ce36Calin Juravle            Log.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
277c660475aafe91269be306c802823cf543005ce36Calin Juravle            return true;
278c660475aafe91269be306c802823cf543005ce36Calin Juravle        }
279c660475aafe91269be306c802823cf543005ce36Calin Juravle
280c660475aafe91269be306c802823cf543005ce36Calin Juravle        return false;
2817395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom    }
2827395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
283cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle    /**
284cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle     * Execute the idle optimizations immediately.
285cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle     */
286cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle    public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context) {
287cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        // Create a new object to make sure we don't interfere with the scheduled jobs.
288cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        // Note that this may still run at the same time with the job scheduled by the
289cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        // JobScheduler but the scheduler will not be able to cancel it.
290cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        BackgroundDexOptService bdos = new BackgroundDexOptService();
291cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        return bdos.idleOptimization(pm, pm.getOptimizablePackages(), context);
292cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle    }
293cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle
294cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate    @Override
29537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    public boolean onStartJob(JobParameters params) {
29637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (DEBUG_DEXOPT) {
29737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            Log.i(TAG, "onStartJob");
29837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
29937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
3008735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        // NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
3018735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        // the checks above. This check is not "live" - the value is determined by a background
3028735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        // restart with a period of ~1 minute.
30337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
30437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (pm.isStorageLow()) {
30537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            if (DEBUG_DEXOPT) {
30637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                Log.i(TAG, "Low storage, skipping this run");
30737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            }
30837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return false;
30937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
31037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
31137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        final ArraySet<String> pkgs = pm.getOptimizablePackages();
312c660475aafe91269be306c802823cf543005ce36Calin Juravle        if (pkgs.isEmpty()) {
31337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            if (DEBUG_DEXOPT) {
31437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                Log.i(TAG, "No packages to optimize");
31537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            }
31637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return false;
31737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
31837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
31937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
32037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return runPostBootUpdate(params, pm, pkgs);
32137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        } else {
32237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return runIdleOptimization(params, pm, pkgs);
32337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
32437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    }
32537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
32637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    @Override
327cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate    public boolean onStopJob(JobParameters params) {
32837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (DEBUG_DEXOPT) {
32937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            Log.i(TAG, "onStopJob");
33037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
33137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
33237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
33337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            mAbortPostBootUpdate.set(true);
33437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        } else {
33537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            mAbortIdleOptimization.set(true);
33637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
337cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate        return false;
3387395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom    }
3397395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom}
340