1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.pm; 18 19import android.app.job.JobInfo; 20import android.app.job.JobParameters; 21import android.app.job.JobScheduler; 22import android.app.job.JobService; 23import android.content.ComponentName; 24import android.content.Context; 25import android.os.ServiceManager; 26import android.util.ArraySet; 27import android.util.Log; 28 29import java.util.concurrent.atomic.AtomicBoolean; 30 31/** 32 * {@hide} 33 */ 34public class BackgroundDexOptService extends JobService { 35 static final String TAG = "BackgroundDexOptService"; 36 37 static final int BACKGROUND_DEXOPT_JOB = 800; 38 private static ComponentName sDexoptServiceName = new ComponentName( 39 "android", 40 BackgroundDexOptService.class.getName()); 41 42 /** 43 * Set of failed packages remembered across job runs. 44 */ 45 static final ArraySet<String> sFailedPackageNames = new ArraySet<String>(); 46 47 final AtomicBoolean mIdleTime = new AtomicBoolean(false); 48 49 public static void schedule(Context context) { 50 JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); 51 JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName) 52 .setRequiresDeviceIdle(true) 53 .setRequiresCharging(true) 54 .build(); 55 js.schedule(job); 56 } 57 58 @Override 59 public boolean onStartJob(JobParameters params) { 60 Log.i(TAG, "onIdleStart"); 61 final PackageManagerService pm = 62 (PackageManagerService)ServiceManager.getService("package"); 63 64 if (pm.isStorageLow()) { 65 return false; 66 } 67 final ArraySet<String> pkgs = pm.getPackagesThatNeedDexOpt(); 68 if (pkgs == null) { 69 return false; 70 } 71 72 final JobParameters jobParams = params; 73 mIdleTime.set(true); 74 new Thread("BackgroundDexOptService_DexOpter") { 75 @Override 76 public void run() { 77 for (String pkg : pkgs) { 78 if (!mIdleTime.get()) { 79 // stopped while still working, so we need to reschedule 80 schedule(BackgroundDexOptService.this); 81 return; 82 } 83 if (sFailedPackageNames.contains(pkg)) { 84 // skip previously failing package 85 continue; 86 } 87 if (!pm.performDexOpt(pkg, null /* instruction set */, true)) { 88 // there was a problem running dexopt, 89 // remember this so we do not keep retrying. 90 sFailedPackageNames.add(pkg); 91 } 92 } 93 // ran to completion, so we abandon our timeslice and do not reschedule 94 jobFinished(jobParams, false); 95 } 96 }.start(); 97 return true; 98 } 99 100 @Override 101 public boolean onStopJob(JobParameters params) { 102 Log.i(TAG, "onIdleStop"); 103 mIdleTime.set(false); 104 return false; 105 } 106} 107