BackgroundDexOptService.java revision 246dccf9327631597767afe418ce43ae6d07d102
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
21cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tateimport android.app.job.JobInfo;
22cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tateimport android.app.job.JobParameters;
23cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tateimport android.app.job.JobScheduler;
24cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tateimport android.app.job.JobService;
25cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tateimport android.content.ComponentName;
267395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstromimport android.content.Context;
2737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdilimport android.content.Intent;
2837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdilimport android.content.IntentFilter;
2937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdilimport android.os.BatteryManager;
308735f07a698218d311826a140d08d611d3c23db1Narayan Kamathimport android.os.Environment;
317395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstromimport android.os.ServiceManager;
3251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravleimport android.os.SystemProperties;
338735f07a698218d311826a140d08d611d3c23db1Narayan Kamathimport android.os.storage.StorageManager;
349f837a99d48c5bb8ad7fbc133943e5bf622ce065Jeff Sharkeyimport android.util.ArraySet;
357395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstromimport android.util.Log;
367395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
3751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravleimport com.android.server.pm.dex.DexManager;
38f107a23ff165bbb7f0ae145f9ac85ddca8b43ed7Carmen Jacksonimport com.android.server.LocalServices;
39f107a23ff165bbb7f0ae145f9ac85ddca8b43ed7Carmen Jacksonimport com.android.server.PinnerService;
4051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
418735f07a698218d311826a140d08d611d3c23db1Narayan Kamathimport java.io.File;
42246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmeraimport java.util.Set;
437395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstromimport java.util.concurrent.atomic.AtomicBoolean;
4427c073796978106746e4a51f2100b29068ab37f6Nicolas Geoffrayimport java.util.concurrent.TimeUnit;
457395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
467395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom/**
477395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom * {@hide}
487395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom */
49cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tatepublic class BackgroundDexOptService extends JobService {
50a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final String TAG = "BackgroundDexOptService";
517395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
52a50d58e22630cd651a815381639e70476991bdbfCalin Juravle    private static final boolean DEBUG = false;
532c9655b3d4c1fb0687baa14730c6d97ab5a56789Christopher Tate
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
76246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera    // Used for calculating space threshold for downgrading unused apps.
77246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera    private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2;
78246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera
79a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom    /**
80a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom     * Set of failed packages remembered across job runs.
81a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom     */
8251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    static final ArraySet<String> sFailedPackageNamesPrimary = new ArraySet<String>();
8351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    static final ArraySet<String> sFailedPackageNamesSecondary = new ArraySet<String>();
84a00be9b4d521287fdf4678fb196c3e3a0053c3e4Brian Carlstrom
8537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    /**
8637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     * Atomics set to true if the JobScheduler requests an abort.
8737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     */
8851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
8951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
9037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
9137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    /**
9237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     * Atomic set to true if one job should exit early because another job was started.
9337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil     */
9451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
957395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
96be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    private final File mDataDir = Environment.getDataDirectory();
978735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
98246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera    private static final long mDowngradeUnusedAppsThresholdInMillis =
99246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            getDowngradeUnusedAppsThresholdInMillis();
100246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera
101db4a79a5d7d348e9d2286d95d4e5a59dd484456fCalin Juravle    public static void schedule(Context context) {
102cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
10337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
10437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // Schedule a one-off job which scans installed packages and updates
10537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // out-of-date oat files.
10637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
10737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
10837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
10937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .build());
11037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
11137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // Schedule a daily job which scans installed packages and compiles
11237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // those with fresh profiling data.
11337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
11437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setRequiresDeviceIdle(true)
11537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .setRequiresCharging(true)
116a50d58e22630cd651a815381639e70476991bdbfCalin Juravle                    .setPeriodic(IDLE_OPTIMIZATION_PERIOD)
11737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                    .build());
11837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
11937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (DEBUG_DEXOPT) {
12037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            Log.i(TAG, "Jobs scheduled");
12137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
1227395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom    }
1237395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
124ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil    public static void notifyPackageChanged(String packageName) {
125ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        // The idle maintanance job skips packages which previously failed to
126ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        // compile. The given package has changed and may successfully compile
127ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        // now. Remove it from the list of known failing packages.
12851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        synchronized (sFailedPackageNamesPrimary) {
12951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            sFailedPackageNamesPrimary.remove(packageName);
13051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        }
13151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        synchronized (sFailedPackageNamesSecondary) {
13251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            sFailedPackageNamesSecondary.remove(packageName);
133ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil        }
134ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil    }
135ace80c56d7c63dadead34539b643f69a1b7336e8David Brazdil
13637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    // Returns the current battery level as a 0-100 integer.
13737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    private int getBatteryLevel() {
13837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
13937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        Intent intent = registerReceiver(null, filter);
14037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
14137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
142cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate
14337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (level < 0 || scale <= 0) {
14437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            // Battery data unavailable. This should never happen, so assume the worst.
14537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return 0;
1467395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom        }
14737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
14837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        return (100 * level / scale);
14937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    }
15037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
151c660475aafe91269be306c802823cf543005ce36Calin Juravle    private long getLowStorageThreshold(Context context) {
1528735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        @SuppressWarnings("deprecation")
153c660475aafe91269be306c802823cf543005ce36Calin Juravle        final long lowThreshold = StorageManager.from(context).getStorageLowBytes(mDataDir);
1548735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        if (lowThreshold == 0) {
1558735f07a698218d311826a140d08d611d3c23db1Narayan Kamath            Log.e(TAG, "Invalid low storage threshold");
1568735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        }
1578735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
1588735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        return lowThreshold;
1598735f07a698218d311826a140d08d611d3c23db1Narayan Kamath    }
1608735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
16137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    private boolean runPostBootUpdate(final JobParameters jobParams,
16237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            final PackageManagerService pm, final ArraySet<String> pkgs) {
16337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (mExitPostBootUpdate.get()) {
16437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            // This job has already been superseded. Do not start it.
1657395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            return false;
1667395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom        }
167be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        new Thread("BackgroundDexOptService_PostBootUpdate") {
168be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            @Override
169be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            public void run() {
170be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                postBootUpdate(jobParams, pm, pkgs);
171be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
172be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
173be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        }.start();
174be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        return true;
175be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    }
176cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate
177be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    private void postBootUpdate(JobParameters jobParams, PackageManagerService pm,
178be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            ArraySet<String> pkgs) {
17937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // Load low battery threshold from the system config. This is a 0-100 integer.
18037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        final int lowBatteryThreshold = getResources().getInteger(
18137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                com.android.internal.R.integer.config_lowBatteryWarningLevel);
182c660475aafe91269be306c802823cf543005ce36Calin Juravle        final long lowThreshold = getLowStorageThreshold(this);
1838735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
18437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        mAbortPostBootUpdate.set(false);
185be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
18631ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle        ArraySet<String> updatedPackages = new ArraySet<>();
187be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        for (String pkg : pkgs) {
188be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (mAbortPostBootUpdate.get()) {
189be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // JobScheduler requested an early abort.
190be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                return;
191be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
192be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (mExitPostBootUpdate.get()) {
193be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Different job, which supersedes this one, is running.
194be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                break;
195be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
196be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (getBatteryLevel() < lowBatteryThreshold) {
197be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Rather bail than completely drain the battery.
198be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                break;
199be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
200be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            long usableSpace = mDataDir.getUsableSpace();
201be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (usableSpace < lowThreshold) {
202be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Rather bail than completely fill up the disk.
203be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                Log.w(TAG, "Aborting background dex opt job due to low storage: " +
204be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                        usableSpace);
205be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                break;
206be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
207be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
208be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            if (DEBUG_DEXOPT) {
209be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                Log.i(TAG, "Updating package " + pkg);
210be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
211be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
212be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // Update package if needed. Note that there can be no race between concurrent
213be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // jobs because PackageDexOptimizer.performDexOpt is synchronized.
214be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
215be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // checkProfiles is false to avoid merging profiles during boot which
216be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // might interfere with background compilation (b/28612421).
217be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
218be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
219be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // trade-off worth doing to save boot time work.
22031ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle            int result = pm.performDexOptWithStatus(pkg,
221be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    /* checkProfiles */ false,
222be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    PackageManagerService.REASON_BOOT,
2233b4359a06c482095338080fa908a67441ad23a2bNicolas Geoffray                    /* force */ false,
224246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                    /* bootComplete */ true,
225246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                    /* downgrade */ false);
22631ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle            if (result == PackageDexOptimizer.DEX_OPT_PERFORMED)  {
22731ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle                updatedPackages.add(pkg);
22831ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle            }
229be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        }
23031ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle        notifyPinService(updatedPackages);
231be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        // Ran to completion, so we abandon our timeslice and do not reschedule.
232be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        jobFinished(jobParams, /* reschedule */ false);
233be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    }
234be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
235be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle    private boolean runIdleOptimization(final JobParameters jobParams,
236be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            final PackageManagerService pm, final ArraySet<String> pkgs) {
237be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        new Thread("BackgroundDexOptService_IdleOptimization") {
2387395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            @Override
2397395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            public void run() {
24051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                int result = idleOptimization(pm, pkgs, BackgroundDexOptService.this);
24151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
24251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                    Log.w(TAG, "Idle optimizations aborted because of space constraints.");
243c660475aafe91269be306c802823cf543005ce36Calin Juravle                    // If we didn't abort we ran to completion (or stopped because of space).
244c660475aafe91269be306c802823cf543005ce36Calin Juravle                    // Abandon our timeslice and do not reschedule.
245c660475aafe91269be306c802823cf543005ce36Calin Juravle                    jobFinished(jobParams, /* reschedule */ false);
246c660475aafe91269be306c802823cf543005ce36Calin Juravle                }
24737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            }
24837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }.start();
24937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        return true;
25037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    }
25137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
25251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    // Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
253246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera    private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
254246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            Context context) {
255a50d58e22630cd651a815381639e70476991bdbfCalin Juravle        Log.i(TAG, "Performing idle optimizations");
25637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        // If post-boot update is still running, request that it exits early.
25737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        mExitPostBootUpdate.set(true);
25837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        mAbortIdleOptimization.set(false);
2598735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
260c660475aafe91269be306c802823cf543005ce36Calin Juravle        long lowStorageThreshold = getLowStorageThreshold(context);
26151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        // Optimize primary apks.
26251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        int result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ true,
26351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                sFailedPackageNamesPrimary);
26451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
26551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
26651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            return result;
26751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        }
26851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
2694466c5ad84e4429284b7d5e13ad8a87c924ea7fcCalin Juravle        if (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
27051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            result = reconcileSecondaryDexFiles(pm.getDexManager());
27151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
27251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                return result;
27351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            }
27451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
27551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ false,
27651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                    sFailedPackageNamesSecondary);
27751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        }
27851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        return result;
279c660475aafe91269be306c802823cf543005ce36Calin Juravle    }
280c660475aafe91269be306c802823cf543005ce36Calin Juravle
28151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
28251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            long lowStorageThreshold, boolean is_for_primary_dex,
28351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            ArraySet<String> failedPackageNames) {
28431ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle        ArraySet<String> updatedPackages = new ArraySet<>();
285246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
286246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        // Only downgrade apps when space is low on device.
287246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
288246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        // up disk before user hits the actual lowStorageThreshold.
289246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE *
290246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                lowStorageThreshold;
291246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
292be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        for (String pkg : pkgs) {
29351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            int abort_code = abortIdleOptimizations(lowStorageThreshold);
294246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
29551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                return abort_code;
296be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
2978735f07a698218d311826a140d08d611d3c23db1Narayan Kamath
29851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            synchronized (failedPackageNames) {
29951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                if (failedPackageNames.contains(pkg)) {
300be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    // Skip previously failing package
301be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                    continue;
302246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                }
303246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            }
304246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera
305246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            int reason;
306246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            boolean downgrade;
307246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            // Downgrade unused packages.
308246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            if (unusedPackages.contains(pkg) && shouldDowngrade) {
309246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                // This applies for system apps or if packages location is not a directory, i.e.
310246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                // monolithic install.
311246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                if (is_for_primary_dex && !pm.canHaveOatDir(pkg)) {
312246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                    // For apps that don't have the oat directory, instead of downgrading,
313246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                    // remove their compiler artifacts from dalvik cache.
314246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                    pm.deleteOatArtifactsOfPackage(pkg);
315246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                    continue;
316c660475aafe91269be306c802823cf543005ce36Calin Juravle                } else {
317246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                    reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
318246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                    downgrade = true;
3197395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom                }
320246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            } else if (abort_code != OPTIMIZE_ABORT_NO_SPACE_LEFT) {
321246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                reason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
322246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                downgrade = false;
323246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            } else {
324246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                // can't dexopt because of low space.
325246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                continue;
326246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            }
327246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera
328246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            synchronized (failedPackageNames) {
329246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                // Conservatively add package to the list of failing ones in case
330246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                // performDexOpt never returns.
331246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                failedPackageNames.add(pkg);
3327395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom            }
333be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle
334be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // Optimize package if needed. Note that there can be no race between
335be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
33631ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle            boolean success;
33731ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle            if (is_for_primary_dex) {
33831ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle                int result = pm.performDexOptWithStatus(pkg,
33931ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle                        /* checkProfiles */ true,
340246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                        reason,
341246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                        false /* forceCompile*/,
342246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                        true /* bootComplete */,
343246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                        downgrade);
34431ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle                success = result != PackageDexOptimizer.DEX_OPT_FAILED;
34531ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle                if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
34631ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle                    updatedPackages.add(pkg);
34731ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle                }
34831ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle            } else {
34931ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle                success = pm.performDexOptSecondary(pkg,
350246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                        reason,
351246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                        false /* force */,
352246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera                        downgrade);
35331ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle            }
35451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            if (success) {
355be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                // Dexopt succeeded, remove package from the list of failing ones.
35651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                synchronized (failedPackageNames) {
35751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                    failedPackageNames.remove(pkg);
358be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle                }
359be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle            }
360be6a71a0b3f369843a26c91dd5123d0499f00e7eCalin Juravle        }
36131ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle        notifyPinService(updatedPackages);
36251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        return OPTIMIZE_PROCESSED;
36351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    }
36451f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle
36551f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private int reconcileSecondaryDexFiles(DexManager dm) {
36651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        // TODO(calin): should we blacklist packages for which we fail to reconcile?
36751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        for (String p : dm.getAllPackagesWithSecondaryDexFiles()) {
36851f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            if (mAbortIdleOptimization.get()) {
36951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle                return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
37051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            }
37151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            dm.reconcileSecondaryDexFiles(p);
37251f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        }
37351f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        return OPTIMIZE_PROCESSED;
374c660475aafe91269be306c802823cf543005ce36Calin Juravle    }
375c660475aafe91269be306c802823cf543005ce36Calin Juravle
37651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    // Evaluate whether or not idle optimizations should continue.
37751f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle    private int abortIdleOptimizations(long lowStorageThreshold) {
378c660475aafe91269be306c802823cf543005ce36Calin Juravle        if (mAbortIdleOptimization.get()) {
379c660475aafe91269be306c802823cf543005ce36Calin Juravle            // JobScheduler requested an early abort.
38051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
381c660475aafe91269be306c802823cf543005ce36Calin Juravle        }
382c660475aafe91269be306c802823cf543005ce36Calin Juravle        long usableSpace = mDataDir.getUsableSpace();
383c660475aafe91269be306c802823cf543005ce36Calin Juravle        if (usableSpace < lowStorageThreshold) {
384c660475aafe91269be306c802823cf543005ce36Calin Juravle            // Rather bail than completely fill up the disk.
385c660475aafe91269be306c802823cf543005ce36Calin Juravle            Log.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
38651f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle            return OPTIMIZE_ABORT_NO_SPACE_LEFT;
387c660475aafe91269be306c802823cf543005ce36Calin Juravle        }
388c660475aafe91269be306c802823cf543005ce36Calin Juravle
38951f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        return OPTIMIZE_CONTINUE;
3907395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom    }
3917395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom
392246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera    // Evaluate whether apps should be downgraded.
393246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera    private boolean shouldDowngrade(long lowStorageThresholdForDowngrade) {
394246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        long usableSpace = mDataDir.getUsableSpace();
395246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        if (usableSpace < lowStorageThresholdForDowngrade) {
396246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            return true;
397246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        }
398246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera
399246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        return false;
400246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera    }
401246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera
402cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle    /**
403cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle     * Execute the idle optimizations immediately.
404cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle     */
405cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle    public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context) {
406cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        // Create a new object to make sure we don't interfere with the scheduled jobs.
407cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        // Note that this may still run at the same time with the job scheduled by the
408cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        // JobScheduler but the scheduler will not be able to cancel it.
409cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle        BackgroundDexOptService bdos = new BackgroundDexOptService();
41051f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        int result = bdos.idleOptimization(pm, pm.getOptimizablePackages(), context);
41151f521c3bf46e6040f36757bc53ea57ddc7be85eCalin Juravle        return result == OPTIMIZE_PROCESSED;
412cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle    }
413cb5f41ea11b1a6fcd0977a64ee146dde8f537076Calin Juravle
414cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate    @Override
41537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    public boolean onStartJob(JobParameters params) {
41637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (DEBUG_DEXOPT) {
41737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            Log.i(TAG, "onStartJob");
41837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
41937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
4208735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        // NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
4218735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        // the checks above. This check is not "live" - the value is determined by a background
4228735f07a698218d311826a140d08d611d3c23db1Narayan Kamath        // restart with a period of ~1 minute.
42337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
42437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (pm.isStorageLow()) {
42537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            if (DEBUG_DEXOPT) {
42637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                Log.i(TAG, "Low storage, skipping this run");
42737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            }
42837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return false;
42937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
43037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
43137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        final ArraySet<String> pkgs = pm.getOptimizablePackages();
432c660475aafe91269be306c802823cf543005ce36Calin Juravle        if (pkgs.isEmpty()) {
43337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            if (DEBUG_DEXOPT) {
43437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil                Log.i(TAG, "No packages to optimize");
43537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            }
43637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            return false;
43737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
43837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
439f107a23ff165bbb7f0ae145f9ac85ddca8b43ed7Carmen Jackson        boolean result;
44037a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
441f107a23ff165bbb7f0ae145f9ac85ddca8b43ed7Carmen Jackson            result = runPostBootUpdate(params, pm, pkgs);
44237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        } else {
443f107a23ff165bbb7f0ae145f9ac85ddca8b43ed7Carmen Jackson            result = runIdleOptimization(params, pm, pkgs);
44437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
445f107a23ff165bbb7f0ae145f9ac85ddca8b43ed7Carmen Jackson
446f107a23ff165bbb7f0ae145f9ac85ddca8b43ed7Carmen Jackson        return result;
44737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    }
44837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
44937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil    @Override
450cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate    public boolean onStopJob(JobParameters params) {
45137a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (DEBUG_DEXOPT) {
45237a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            Log.i(TAG, "onStopJob");
45337a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
45437a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil
45537a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
45637a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            mAbortPostBootUpdate.set(true);
45737a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        } else {
45837a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil            mAbortIdleOptimization.set(true);
45937a87698f5b13e8d24475cb05d48a3da7339192cDavid Brazdil        }
460cf1a2f73fc102be2ac7060ac97d4682bb2565ca5Christopher Tate        return false;
4617395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom    }
46231ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle
46331ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle    private void notifyPinService(ArraySet<String> updatedPackages) {
46431ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle        PinnerService pinnerService = LocalServices.getService(PinnerService.class);
46531ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle        if (pinnerService != null) {
46631ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle            Log.i(TAG, "Pinning optimized code " + updatedPackages);
46731ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle            pinnerService.update(updatedPackages);
46831ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle        }
46931ce3a8a56e78ecc17d9befbc64a1e529b6b78e9Calin Juravle    }
470246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera
471246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera    private static long getDowngradeUnusedAppsThresholdInMillis() {
472246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
473246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        String sysPropValue = SystemProperties.get(sysPropKey);
474246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        if (sysPropValue == null || sysPropValue.isEmpty()) {
475246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            Log.w(TAG, "SysProp " + sysPropKey + " not set");
476246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera            return Long.MAX_VALUE;
477246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        }
478246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera        return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue));
479246dccf9327631597767afe418ce43ae6d07d102Shubham Ajmera    }
4807395a8ab8a7c6b03c32500c934694dde80b9ade1Brian Carlstrom}
481