MountServiceIdler.java revision f8ad7a909436250fed920a8c4c56f69cd97b1b60
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
217060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobInfo;
227060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobParameters;
237060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobScheduler;
247060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobService;
25115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tateimport android.content.ComponentName;
26115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tateimport android.content.Context;
27d417d625d244356bc770e2692fd59e754a72f59fChristopher Tateimport android.util.Slog;
28d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate
297060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tatepublic class MountServiceIdler extends JobService {
30d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    private static final String TAG = "MountServiceIdler";
31d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate
32115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    private static ComponentName sIdleService =
33f8ad7a909436250fed920a8c4c56f69cd97b1b60Christopher Tate            new ComponentName("android", MountServiceIdler.class.getName());
34115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
357060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    private static int MOUNT_JOB_ID = 808;
36115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
37115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    private boolean mStarted;
387060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    private JobParameters mJobParams;
39d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    private Runnable mFinishCallback = new Runnable() {
40d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        @Override
41d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        public void run() {
42d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate            Slog.i(TAG, "Got mount service completion callback");
43115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            synchronized (mFinishCallback) {
44115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate                if (mStarted) {
457060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                    jobFinished(mJobParams, false);
46115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate                    mStarted = false;
47115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate                }
48115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            }
49115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            // ... and try again tomorrow
50115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            scheduleIdlePass(MountServiceIdler.this);
51d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        }
52d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    };
53d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate
54d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    @Override
557060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    public boolean onStartJob(JobParameters params) {
56d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        // The mount service will run an fstrim operation asynchronously
57d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        // on a designated separate thread, so we provide it with a callback
58d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        // that lets us cleanly end our idle timeslice.  It's safe to call
59d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        // finishIdle() from any thread.
607060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        mJobParams = params;
61d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        MountService ms = MountService.sSelf;
62d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        if (ms != null) {
63115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            synchronized (mFinishCallback) {
64115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate                mStarted = true;
65115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            }
66d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate            ms.runIdleMaintenance(mFinishCallback);
67d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        }
68d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate        return ms != null;
69d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    }
70d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate
71d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    @Override
727060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    public boolean onStopJob(JobParameters params) {
73115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        // Once we kick off the fstrim we aren't actually interruptible; just note
747060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        // that we don't need to call jobFinished(), and let everything happen in
75115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        // the callback from the mount service.
76115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        synchronized (mFinishCallback) {
77115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate            mStarted = false;
78115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        }
79115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        return false;
80115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    }
81115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
82115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    /**
83115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate     * Schedule the idle job that will ping the mount service
84115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate     */
85115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    public static void scheduleIdlePass(Context context) {
867060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
87115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
88115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        Calendar calendar = tomorrowMidnight();
89115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        final long timeToMidnight = calendar.getTimeInMillis() - System.currentTimeMillis();
90115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
917060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService);
92115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        builder.setRequiresDeviceIdle(true);
93115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        builder.setRequiresCharging(true);
94115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        builder.setMinimumLatency(timeToMidnight);
95115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        tm.schedule(builder.build());
96115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    }
97115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate
98115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate    private static Calendar tomorrowMidnight() {
99115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        Calendar calendar = Calendar.getInstance();
100115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.setTimeInMillis(System.currentTimeMillis());
101115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.set(Calendar.HOUR, 0);
102115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.set(Calendar.MINUTE, 0);
103115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.set(Calendar.SECOND, 0);
104115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.set(Calendar.MILLISECOND, 0);
105115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        calendar.add(Calendar.DAY_OF_MONTH, 1);
106115afdadb5863a02f0b0daefcc0511bfd35b531eChristopher Tate        return calendar;
107d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate    }
108d417d625d244356bc770e2692fd59e754a72f59fChristopher Tate}
109