1d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate/*
2d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate * Copyright (C) 2014 The Android Open Source Project
3d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate *
4d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate * Licensed under the Apache License, Version 2.0 (the "License");
5d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate * you may not use this file except in compliance with the License.
6d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate * You may obtain a copy of the License at
7d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate *
8d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate *      http://www.apache.org/licenses/LICENSE-2.0
9d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate *
10d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate * Unless required by applicable law or agreed to in writing, software
11d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate * distributed under the License is distributed on an "AS IS" BASIS,
12d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate * See the License for the specific language governing permissions and
14d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate * limitations under the License.
15d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate */
16d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate
17d417d625d244356bc770e2692fd59e754a72f59fChristopher Tatepackage com.android.server;
18d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate
19115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tateimport java.util.Calendar;
20115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
2157a873fcaa4dd95fc844887b0c255f650b9159e5Dianne Hackbornimport android.app.ActivityManagerNative;
227060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobInfo;
237060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobParameters;
247060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobScheduler;
257060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobService;
26115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tateimport android.content.ComponentName;
27115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tateimport android.content.Context;
2857a873fcaa4dd95fc844887b0c255f650b9159e5Dianne Hackbornimport android.os.RemoteException;
29d417d625d244356bc770e2692fd59e754a72f59fChristopher Tateimport android.util.Slog;
30d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate
317060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tatepublic class MountServiceIdler extends JobService {
32d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    private static final String TAG = "MountServiceIdler";
33d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate
34115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    private static ComponentName sIdleService =
35f8ad7a909436250fed920a8c4c56f69cd97b1b60Christopher Tate            new ComponentName("android", MountServiceIdler.class.getName());
36115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
377060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    private static int MOUNT_JOB_ID = 808;
38115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
39115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    private boolean mStarted;
407060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    private JobParameters mJobParams;
41d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    private Runnable mFinishCallback = new Runnable() {
42d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        @Override
43d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        public void run() {
44d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate            Slog.i(TAG, "Got mount service completion callback");
45115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            synchronized (mFinishCallback) {
46115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate                if (mStarted) {
477060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                    jobFinished(mJobParams, false);
48115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate                    mStarted = false;
49115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate                }
50115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            }
51115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            // ... and try again tomorrow
52115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            scheduleIdlePass(MountServiceIdler.this);
53d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        }
54d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    };
55d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate
56d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    @Override
577060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    public boolean onStartJob(JobParameters params) {
5857a873fcaa4dd95fc844887b0c255f650b9159e5Dianne Hackborn        // First have the activity manager do its idle maintenance.  (Yes this job
5957a873fcaa4dd95fc844887b0c255f650b9159e5Dianne Hackborn        // is really more than just mount, some day it should be renamed to be system
6057a873fcaa4dd95fc844887b0c255f650b9159e5Dianne Hackborn        // idleer).
6157a873fcaa4dd95fc844887b0c255f650b9159e5Dianne Hackborn        try {
6257a873fcaa4dd95fc844887b0c255f650b9159e5Dianne Hackborn            ActivityManagerNative.getDefault().performIdleMaintenance();
6357a873fcaa4dd95fc844887b0c255f650b9159e5Dianne Hackborn        } catch (RemoteException e) {
6457a873fcaa4dd95fc844887b0c255f650b9159e5Dianne Hackborn        }
65d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        // The mount service will run an fstrim operation asynchronously
66d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        // on a designated separate thread, so we provide it with a callback
67d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        // that lets us cleanly end our idle timeslice.  It's safe to call
68d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        // finishIdle() from any thread.
697060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        mJobParams = params;
70d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        MountService ms = MountService.sSelf;
71d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        if (ms != null) {
72115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            synchronized (mFinishCallback) {
73115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate                mStarted = true;
74115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            }
75d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate            ms.runIdleMaintenance(mFinishCallback);
76d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        }
77d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        return ms != null;
78d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    }
79d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate
80d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    @Override
817060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    public boolean onStopJob(JobParameters params) {
82115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        // Once we kick off the fstrim we aren't actually interruptible; just note
837060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        // that we don't need to call jobFinished(), and let everything happen in
84115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        // the callback from the mount service.
85115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        synchronized (mFinishCallback) {
86115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            mStarted = false;
87115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        }
88115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        return false;
89115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    }
90115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
91115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    /**
92115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate     * Schedule the idle job that will ping the mount service
93115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate     */
94115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    public static void scheduleIdlePass(Context context) {
957060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
96115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
97115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        Calendar calendar = tomorrowMidnight();
98115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        final long timeToMidnight = calendar.getTimeInMillis() - System.currentTimeMillis();
99115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
1007060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService);
101115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        builder.setRequiresDeviceIdle(true);
102115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        builder.setRequiresCharging(true);
103115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        builder.setMinimumLatency(timeToMidnight);
104115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        tm.schedule(builder.build());
105115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    }
106115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
107115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    private static Calendar tomorrowMidnight() {
108115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        Calendar calendar = Calendar.getInstance();
109115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.setTimeInMillis(System.currentTimeMillis());
11057a873fcaa4dd95fc844887b0c255f650b9159e5Dianne Hackborn        calendar.set(Calendar.HOUR_OF_DAY, 3);
111115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.set(Calendar.MINUTE, 0);
112115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.set(Calendar.SECOND, 0);
113115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.set(Calendar.MILLISECOND, 0);
114115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.add(Calendar.DAY_OF_MONTH, 1);
115115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        return calendar;
116d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    }
117d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate}
118