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 17 package com.android.server; 18 19 import java.util.Calendar; 20 21 import android.app.ActivityManagerNative; 22 import android.app.job.JobInfo; 23 import android.app.job.JobParameters; 24 import android.app.job.JobScheduler; 25 import android.app.job.JobService; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.os.RemoteException; 29 import android.util.Slog; 30 31 public class MountServiceIdler extends JobService { 32 private static final String TAG = "MountServiceIdler"; 33 34 private static ComponentName sIdleService = 35 new ComponentName("android", MountServiceIdler.class.getName()); 36 37 private static int MOUNT_JOB_ID = 808; 38 39 private boolean mStarted; 40 private JobParameters mJobParams; 41 private Runnable mFinishCallback = new Runnable() { 42 @Override 43 public void run() { 44 Slog.i(TAG, "Got mount service completion callback"); 45 synchronized (mFinishCallback) { 46 if (mStarted) { 47 jobFinished(mJobParams, false); 48 mStarted = false; 49 } 50 } 51 // ... and try again tomorrow 52 scheduleIdlePass(MountServiceIdler.this); 53 } 54 }; 55 56 @Override 57 public boolean onStartJob(JobParameters params) { 58 // First have the activity manager do its idle maintenance. (Yes this job 59 // is really more than just mount, some day it should be renamed to be system 60 // idleer). 61 try { 62 ActivityManagerNative.getDefault().performIdleMaintenance(); 63 } catch (RemoteException e) { 64 } 65 // The mount service will run an fstrim operation asynchronously 66 // on a designated separate thread, so we provide it with a callback 67 // that lets us cleanly end our idle timeslice. It's safe to call 68 // finishIdle() from any thread. 69 mJobParams = params; 70 MountService ms = MountService.sSelf; 71 if (ms != null) { 72 synchronized (mFinishCallback) { 73 mStarted = true; 74 } 75 ms.runIdleMaintenance(mFinishCallback); 76 } 77 return ms != null; 78 } 79 80 @Override 81 public boolean onStopJob(JobParameters params) { 82 // Once we kick off the fstrim we aren't actually interruptible; just note 83 // that we don't need to call jobFinished(), and let everything happen in 84 // the callback from the mount service. 85 synchronized (mFinishCallback) { 86 mStarted = false; 87 } 88 return false; 89 } 90 91 /** 92 * Schedule the idle job that will ping the mount service 93 */ 94 public static void scheduleIdlePass(Context context) { 95 JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); 96 97 Calendar calendar = tomorrowMidnight(); 98 final long timeToMidnight = calendar.getTimeInMillis() - System.currentTimeMillis(); 99 100 JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService); 101 builder.setRequiresDeviceIdle(true); 102 builder.setRequiresCharging(true); 103 builder.setMinimumLatency(timeToMidnight); 104 tm.schedule(builder.build()); 105 } 106 107 private static Calendar tomorrowMidnight() { 108 Calendar calendar = Calendar.getInstance(); 109 calendar.setTimeInMillis(System.currentTimeMillis()); 110 calendar.set(Calendar.HOUR_OF_DAY, 3); 111 calendar.set(Calendar.MINUTE, 0); 112 calendar.set(Calendar.SECOND, 0); 113 calendar.set(Calendar.MILLISECOND, 0); 114 calendar.add(Calendar.DAY_OF_MONTH, 1); 115 return calendar; 116 } 117 } 118