BackgroundDexOptService.java revision 51f521c3bf46e6040f36757bc53ea57ddc7be85e
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;
3351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravleimport android.os.SystemProperties;
348735f07a698218d311826a140d08d611d3c23db1Narayan Kamathimport android.os.storage.StorageManager;
359f837a99d48c5bb8ad7fbc133943e5bf622ce065Jeff Sharkeyimport android.util.ArraySet;
367395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstromimport android.util.Log;
377395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
3851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravleimport com.android.server.pm.dex.DexManager;
3951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
408735f07a698218d311826a140d08d611d3c23db1Narayan Kamathimport java.io.File;
417395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstromimport java.util.concurrent.atomic.AtomicBoolean;
4227c073796978106746e4a51f2100b29068ab37f6Nicolas Geoffrayimport java.util.concurrent.TimeUnit;
437395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
447395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom/**
457395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * {@hide}
467395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom */
47cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tatepublic class BackgroundDexOptService extends JobService {
48a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final String TAG = "BackgroundDexOptService";
497395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
50a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final boolean DEBUG = false;
512c9655b3d4c1fb0687baa14730c6d97ab5a56789Christopher Tate
52a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR;
53a50d58e22630cd651a815381639e70476991bdbfCalin Juravle
54a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final int JOB_IDLE_OPTIMIZE = 800;
55a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final int JOB_POST_BOOT_UPDATE = 801;
56a50d58e22630cd651a815381639e70476991bdbfCalin Juravle
57a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG
58a50d58e22630cd651a815381639e70476991bdbfCalin Juravle            ? TimeUnit.MINUTES.toMillis(1)
59a50d58e22630cd651a815381639e70476991bdbfCalin Juravle            : TimeUnit.DAYS.toMillis(1);
6037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
61cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate    private static ComponentName sDexoptServiceName = new ComponentName(
621b8b3aa265190e84467f740e99a0ade3a0e3cd67Christopher Tate            "android",
63cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate            BackgroundDexOptService.class.getName());
647395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
6551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    // Possible return codes of individual optimization steps.
6651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
6751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    // Optimizations finished. All packages were processed.
6851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private static final int OPTIMIZE_PROCESSED = 0;
6951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    // Optimizations should continue. Issued after checking the scheduler, disk space or battery.
7051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private static final int OPTIMIZE_CONTINUE = 1;
7151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    // Optimizations should be aborted. Job scheduler requested it.
7251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private static final int OPTIMIZE_ABORT_BY_JOB_SCHEDULER = 2;
7351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    // Optimizations should be aborted. No space left on device.
7451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3;
7551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
76a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom    /**
77a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom     * Set of failed packages remembered across job runs.
78a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom     */
7951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    static final ArraySet<String> sFailedPackageNamesPrimary = new ArraySet<String>();
8051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    static final ArraySet<String> sFailedPackageNamesSecondary = new ArraySet<String>();
81a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom
8237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    /**
8337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     * Atomics set to true if the JobScheduler requests an abort.
8437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     */
8551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
8651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
8737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
8837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    /**
8937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     * Atomic set to true if one job should exit early because another job was started.
9037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     */
9151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
927395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
93be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    private final File mDataDir = Environment.getDataDirectory();
948735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
95db4a79a5d7d348e9d2286d95d4e5a59dd484456fCalin Juravle    public static void schedule(Context context) {
96cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
9737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
9837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // Schedule a one-off job which scans installed packages and updates
9937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // out-of-date oat files.
10037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
10137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
10237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
10337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .build());
10437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
10537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // Schedule a daily job which scans installed packages and compiles
10637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // those with fresh profiling data.
10737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
10837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setRequiresDeviceIdle(true)
10937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setRequiresCharging(true)
110a50d58e22630cd651a815381639e70476991bdbfCalin Juravle                    .setPeriodic(IDLE_OPTIMIZATION_PERIOD)
11137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .build());
11237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
11337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (DEBUG_DEXOPT) {
11437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            Log.i(TAG, "Jobs scheduled");
11537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
1167395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom    }
1177395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
118ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil    public static void notifyPackageChanged(String packageName) {
119ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        // The idle maintanance job skips packages which previously failed to
120ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        // compile. The given package has changed and may successfully compile
121ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        // now. Remove it from the list of known failing packages.
12251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        synchronized (sFailedPackageNamesPrimary) {
12351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            sFailedPackageNamesPrimary.remove(packageName);
12451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        }
12551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        synchronized (sFailedPackageNamesSecondary) {
12651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            sFailedPackageNamesSecondary.remove(packageName);
127ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        }
128ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil    }
129ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil
13037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    // Returns the current battery level as a 0-100 integer.
13137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    private int getBatteryLevel() {
13237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
13337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        Intent intent = registerReceiver(null, filter);
13437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
13537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
136cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate
13737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (level < 0 || scale <= 0) {
13837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            // Battery data unavailable. This should never happen, so assume the worst.
13937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return 0;
1407395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom        }
14137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
14237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        return (100 * level / scale);
14337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    }
14437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
145c660475aafe91269be306c802823cf543005ce36Calin Juravle    private long getLowStorageThreshold(Context context) {
1468735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        @SuppressWarnings("deprecation")
147c660475aafe91269be306c802823cf543005ce36Calin Juravle        final long lowThreshold = StorageManager.from(context).getStorageLowBytes(mDataDir);
1488735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        if (lowThreshold == 0) {
1498735f07a698218d311826a140d08d611d3c23db1Narayan Kamath            Log.e(TAG, "Invalid low storage threshold");
1508735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        }
1518735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
1528735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        return lowThreshold;
1538735f07a698218d311826a140d08d611d3c23db1Narayan Kamath    }
1548735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
15537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    private boolean runPostBootUpdate(final JobParameters jobParams,
15637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            final PackageManagerService pm, final ArraySet<String> pkgs) {
15737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (mExitPostBootUpdate.get()) {
15837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            // This job has already been superseded. Do not start it.
1597395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            return false;
1607395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom        }
161be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        new Thread("BackgroundDexOptService_PostBootUpdate") {
162be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            @Override
163be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            public void run() {
164be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                postBootUpdate(jobParams, pm, pkgs);
165be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
166be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
167be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        }.start();
168be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        return true;
169be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    }
170cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate
171be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    private void postBootUpdate(JobParameters jobParams, PackageManagerService pm,
172be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            ArraySet<String> pkgs) {
17337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // Load low battery threshold from the system config. This is a 0-100 integer.
17437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        final int lowBatteryThreshold = getResources().getInteger(
17537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                com.android.internal.R.integer.config_lowBatteryWarningLevel);
176c660475aafe91269be306c802823cf543005ce36Calin Juravle        final long lowThreshold = getLowStorageThreshold(this);
1778735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
17837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        mAbortPostBootUpdate.set(false);
179be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
180be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        for (String pkg : pkgs) {
181be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (mAbortPostBootUpdate.get()) {
182be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // JobScheduler requested an early abort.
183be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                return;
184be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
185be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (mExitPostBootUpdate.get()) {
186be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Different job, which supersedes this one, is running.
187be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                break;
188be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
189be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (getBatteryLevel() < lowBatteryThreshold) {
190be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Rather bail than completely drain the battery.
191be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                break;
192be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
193be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            long usableSpace = mDataDir.getUsableSpace();
194be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (usableSpace < lowThreshold) {
195be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Rather bail than completely fill up the disk.
196be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                Log.w(TAG, "Aborting background dex opt job due to low storage: " +
197be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                        usableSpace);
198be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                break;
199be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
200be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
201be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (DEBUG_DEXOPT) {
202be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                Log.i(TAG, "Updating package " + pkg);
203be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
204be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
205be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // Update package if needed. Note that there can be no race between concurrent
206be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // jobs because PackageDexOptimizer.performDexOpt is synchronized.
207be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
208be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // checkProfiles is false to avoid merging profiles during boot which
209be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // might interfere with background compilation (b/28612421).
210be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
211be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
212be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // trade-off worth doing to save boot time work.
213be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            pm.performDexOpt(pkg,
214be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    /* checkProfiles */ false,
215be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    PackageManagerService.REASON_BOOT,
216be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    /* force */ false);
217be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        }
218be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        // Ran to completion, so we abandon our timeslice and do not reschedule.
219be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        jobFinished(jobParams, /* reschedule */ false);
220be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    }
221be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
222be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    private boolean runIdleOptimization(final JobParameters jobParams,
223be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            final PackageManagerService pm, final ArraySet<String> pkgs) {
224be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        new Thread("BackgroundDexOptService_IdleOptimization") {
2257395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            @Override
2267395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            public void run() {
22751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                int result = idleOptimization(pm, pkgs, BackgroundDexOptService.this);
22851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
22951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                    Log.w(TAG, "Idle optimizations aborted because of space constraints.");
230c660475aafe91269be306c802823cf543005ce36Calin Juravle                    // If we didn't abort we ran to completion (or stopped because of space).
231c660475aafe91269be306c802823cf543005ce36Calin Juravle                    // Abandon our timeslice and do not reschedule.
232c660475aafe91269be306c802823cf543005ce36Calin Juravle                    jobFinished(jobParams, /* reschedule */ false);
233c660475aafe91269be306c802823cf543005ce36Calin Juravle                }
23437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            }
23537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }.start();
23637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        return true;
23737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    }
23837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
23951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    // Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
24051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs, Context context) {
241a50d58e22630cd651a815381639e70476991bdbfCalin Juravle        Log.i(TAG, "Performing idle optimizations");
24237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // If post-boot update is still running, request that it exits early.
24337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        mExitPostBootUpdate.set(true);
24437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        mAbortIdleOptimization.set(false);
2458735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
246c660475aafe91269be306c802823cf543005ce36Calin Juravle        long lowStorageThreshold = getLowStorageThreshold(context);
24751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        // Optimize primary apks.
24851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        int result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ true,
24951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                sFailedPackageNamesPrimary);
25051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
25151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
25251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            return result;
25351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        }
25451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
25551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        if (SystemProperties.getBoolean("dalvik.vm.deopt.secondary", false)) {
25651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            result = reconcileSecondaryDexFiles(pm.getDexManager());
25751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
25851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                return result;
25951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            }
26051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
26151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ false,
26251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                    sFailedPackageNamesSecondary);
26351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        }
26451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        return result;
265c660475aafe91269be306c802823cf543005ce36Calin Juravle    }
266c660475aafe91269be306c802823cf543005ce36Calin Juravle
26751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
26851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            long lowStorageThreshold, boolean is_for_primary_dex,
26951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            ArraySet<String> failedPackageNames) {
270be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        for (String pkg : pkgs) {
27151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            int abort_code = abortIdleOptimizations(lowStorageThreshold);
27251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            if (abort_code != OPTIMIZE_CONTINUE) {
27351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                return abort_code;
274be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
2758735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
27651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            synchronized (failedPackageNames) {
27751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                if (failedPackageNames.contains(pkg)) {
278be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    // Skip previously failing package
279be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    continue;
280c660475aafe91269be306c802823cf543005ce36Calin Juravle                } else {
281c660475aafe91269be306c802823cf543005ce36Calin Juravle                    // Conservatively add package to the list of failing ones in case performDexOpt
282c660475aafe91269be306c802823cf543005ce36Calin Juravle                    // never returns.
28351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                    failedPackageNames.add(pkg);
2847395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom                }
2857395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            }
286be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
287be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // Optimize package if needed. Note that there can be no race between
288be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
28951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            boolean success = is_for_primary_dex
29051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                    ? pm.performDexOpt(pkg,
29151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                            /* checkProfiles */ true,
29251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                            PackageManagerService.REASON_BACKGROUND_DEXOPT,
29351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                            /* force */ false)
29451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                    : pm.performDexOptSecondary(pkg,
29551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                            PackageManagerServiceCompilerMapping.getFullCompilerFilter(),
29651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                            /* force */ true);
29751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            if (success) {
298be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Dexopt succeeded, remove package from the list of failing ones.
29951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                synchronized (failedPackageNames) {
30051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                    failedPackageNames.remove(pkg);
301be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                }
302be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
303be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        }
30451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        return OPTIMIZE_PROCESSED;
30551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    }
30651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
30751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private int reconcileSecondaryDexFiles(DexManager dm) {
30851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        // TODO(calin): should we blacklist packages for which we fail to reconcile?
30951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        for (String p : dm.getAllPackagesWithSecondaryDexFiles()) {
31051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            if (mAbortIdleOptimization.get()) {
31151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
31251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            }
31351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            dm.reconcileSecondaryDexFiles(p);
31451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        }
31551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        return OPTIMIZE_PROCESSED;
316c660475aafe91269be306c802823cf543005ce36Calin Juravle    }
317c660475aafe91269be306c802823cf543005ce36Calin Juravle
31851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    // Evaluate whether or not idle optimizations should continue.
31951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private int abortIdleOptimizations(long lowStorageThreshold) {
320c660475aafe91269be306c802823cf543005ce36Calin Juravle        if (mAbortIdleOptimization.get()) {
321c660475aafe91269be306c802823cf543005ce36Calin Juravle            // JobScheduler requested an early abort.
32251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
323c660475aafe91269be306c802823cf543005ce36Calin Juravle        }
324c660475aafe91269be306c802823cf543005ce36Calin Juravle        long usableSpace = mDataDir.getUsableSpace();
325c660475aafe91269be306c802823cf543005ce36Calin Juravle        if (usableSpace < lowStorageThreshold) {
326c660475aafe91269be306c802823cf543005ce36Calin Juravle            // Rather bail than completely fill up the disk.
327c660475aafe91269be306c802823cf543005ce36Calin Juravle            Log.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
32851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            return OPTIMIZE_ABORT_NO_SPACE_LEFT;
329c660475aafe91269be306c802823cf543005ce36Calin Juravle        }
330c660475aafe91269be306c802823cf543005ce36Calin Juravle
33151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        return OPTIMIZE_CONTINUE;
3327395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom    }
3337395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
334cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle    /**
335cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle     * Execute the idle optimizations immediately.
336cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle     */
337cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle    public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context) {
338cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        // Create a new object to make sure we don't interfere with the scheduled jobs.
339cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        // Note that this may still run at the same time with the job scheduled by the
340cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        // JobScheduler but the scheduler will not be able to cancel it.
341cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        BackgroundDexOptService bdos = new BackgroundDexOptService();
34251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        int result = bdos.idleOptimization(pm, pm.getOptimizablePackages(), context);
34351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        return result == OPTIMIZE_PROCESSED;
344cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle    }
345cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle
346cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate    @Override
34737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    public boolean onStartJob(JobParameters params) {
34837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (DEBUG_DEXOPT) {
34937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            Log.i(TAG, "onStartJob");
35037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
35137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
3528735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        // NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
3538735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        // the checks above. This check is not "live" - the value is determined by a background
3548735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        // restart with a period of ~1 minute.
35537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
35637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (pm.isStorageLow()) {
35737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            if (DEBUG_DEXOPT) {
35837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                Log.i(TAG, "Low storage, skipping this run");
35937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            }
36037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return false;
36137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
36237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
36337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        final ArraySet<String> pkgs = pm.getOptimizablePackages();
364c660475aafe91269be306c802823cf543005ce36Calin Juravle        if (pkgs.isEmpty()) {
36537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            if (DEBUG_DEXOPT) {
36637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                Log.i(TAG, "No packages to optimize");
36737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            }
36837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return false;
36937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
37037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
37137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
37237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return runPostBootUpdate(params, pm, pkgs);
37337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        } else {
37437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return runIdleOptimization(params, pm, pkgs);
37537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
37637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    }
37737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
37837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    @Override
379cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate    public boolean onStopJob(JobParameters params) {
38037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (DEBUG_DEXOPT) {
38137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            Log.i(TAG, "onStopJob");
38237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
38337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
38437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
38537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            mAbortPostBootUpdate.set(true);
38637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        } else {
38737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            mAbortIdleOptimization.set(true);
38837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
389cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate        return false;
3907395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom    }
3917395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom}
392