blob: 6bc1a570b7c0a0e7777deff5f6047664e0347440 [file] [log] [blame]
Christopher Tated417d622013-08-19 16:14:25 -07001/*
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;
18
Christopher Tate115afda2014-06-06 19:06:26 -070019import java.util.Calendar;
20
Sudheer Shankadc589ac2016-11-10 15:30:17 -080021import android.app.ActivityManager;
Christopher Tate7060b042014-06-09 19:50:00 -070022import android.app.job.JobInfo;
23import android.app.job.JobParameters;
24import android.app.job.JobScheduler;
25import android.app.job.JobService;
Christopher Tate115afda2014-06-06 19:06:26 -070026import android.content.ComponentName;
27import android.content.Context;
Dianne Hackborn57a873f2014-12-04 13:58:46 -080028import android.os.RemoteException;
Christopher Tated417d622013-08-19 16:14:25 -070029import android.util.Slog;
Jaegeuk Kim8975f782019-04-25 05:00:36 -070030import java.util.concurrent.TimeUnit;
Christopher Tated417d622013-08-19 16:14:25 -070031
Christopher Tate7060b042014-06-09 19:50:00 -070032public class MountServiceIdler extends JobService {
Christopher Tated417d622013-08-19 16:14:25 -070033 private static final String TAG = "MountServiceIdler";
34
Christopher Tate115afda2014-06-06 19:06:26 -070035 private static ComponentName sIdleService =
Christopher Tatef8ad7a92014-06-17 15:07:01 -070036 new ComponentName("android", MountServiceIdler.class.getName());
Christopher Tate115afda2014-06-06 19:06:26 -070037
Christopher Tate7060b042014-06-09 19:50:00 -070038 private static int MOUNT_JOB_ID = 808;
Christopher Tate115afda2014-06-06 19:06:26 -070039
40 private boolean mStarted;
Christopher Tate7060b042014-06-09 19:50:00 -070041 private JobParameters mJobParams;
Christopher Tated417d622013-08-19 16:14:25 -070042 private Runnable mFinishCallback = new Runnable() {
43 @Override
44 public void run() {
45 Slog.i(TAG, "Got mount service completion callback");
Christopher Tate115afda2014-06-06 19:06:26 -070046 synchronized (mFinishCallback) {
47 if (mStarted) {
Christopher Tate7060b042014-06-09 19:50:00 -070048 jobFinished(mJobParams, false);
Christopher Tate115afda2014-06-06 19:06:26 -070049 mStarted = false;
50 }
51 }
Jaegeuk Kim8975f782019-04-25 05:00:36 -070052 // ... and try again right away or tomorrow
Christopher Tate115afda2014-06-06 19:06:26 -070053 scheduleIdlePass(MountServiceIdler.this);
Christopher Tated417d622013-08-19 16:14:25 -070054 }
55 };
56
57 @Override
Christopher Tate7060b042014-06-09 19:50:00 -070058 public boolean onStartJob(JobParameters params) {
Dianne Hackborn57a873f2014-12-04 13:58:46 -080059 // First have the activity manager do its idle maintenance. (Yes this job
60 // is really more than just mount, some day it should be renamed to be system
61 // idleer).
62 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -080063 ActivityManager.getService().performIdleMaintenance();
Dianne Hackborn57a873f2014-12-04 13:58:46 -080064 } catch (RemoteException e) {
65 }
Christopher Tated417d622013-08-19 16:14:25 -070066 // The mount service will run an fstrim operation asynchronously
67 // on a designated separate thread, so we provide it with a callback
68 // that lets us cleanly end our idle timeslice. It's safe to call
69 // finishIdle() from any thread.
Christopher Tate7060b042014-06-09 19:50:00 -070070 mJobParams = params;
Sudheer Shanka2250d562016-11-07 15:41:02 -080071 StorageManagerService ms = StorageManagerService.sSelf;
Christopher Tated417d622013-08-19 16:14:25 -070072 if (ms != null) {
Christopher Tate115afda2014-06-06 19:06:26 -070073 synchronized (mFinishCallback) {
74 mStarted = true;
75 }
Jin Qian49a58382017-10-24 17:48:00 -070076 ms.runIdleMaint(mFinishCallback);
Christopher Tated417d622013-08-19 16:14:25 -070077 }
78 return ms != null;
79 }
80
81 @Override
Christopher Tate7060b042014-06-09 19:50:00 -070082 public boolean onStopJob(JobParameters params) {
Christopher Tate115afda2014-06-06 19:06:26 -070083 // Once we kick off the fstrim we aren't actually interruptible; just note
Christopher Tate7060b042014-06-09 19:50:00 -070084 // that we don't need to call jobFinished(), and let everything happen in
Christopher Tate115afda2014-06-06 19:06:26 -070085 // the callback from the mount service.
Jin Qian49a58382017-10-24 17:48:00 -070086 StorageManagerService ms = StorageManagerService.sSelf;
87 if (ms != null) {
88 ms.abortIdleMaint(mFinishCallback);
89 synchronized (mFinishCallback) {
90 mStarted = false;
91 }
Christopher Tate115afda2014-06-06 19:06:26 -070092 }
93 return false;
94 }
95
96 /**
97 * Schedule the idle job that will ping the mount service
98 */
99 public static void scheduleIdlePass(Context context) {
Christopher Tate7060b042014-06-09 19:50:00 -0700100 JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
Christopher Tate115afda2014-06-06 19:06:26 -0700101
Jaegeuk Kim8975f782019-04-25 05:00:36 -0700102 final long today3AM = offsetFromTodayMidnight(0, 3).getTimeInMillis();
103 final long today4AM = offsetFromTodayMidnight(0, 4).getTimeInMillis();
104 final long tomorrow3AM = offsetFromTodayMidnight(1, 3).getTimeInMillis();
105
106 long nextScheduleTime;
107 if (System.currentTimeMillis() > today3AM && System.currentTimeMillis() < today4AM) {
108 nextScheduleTime = TimeUnit.SECONDS.toMillis(10);
109 } else {
110 nextScheduleTime = tomorrow3AM - System.currentTimeMillis(); // 3AM tomorrow
111 }
Christopher Tate115afda2014-06-06 19:06:26 -0700112
Christopher Tate7060b042014-06-09 19:50:00 -0700113 JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService);
Christopher Tate115afda2014-06-06 19:06:26 -0700114 builder.setRequiresDeviceIdle(true);
Jaegeuk Kim8975f782019-04-25 05:00:36 -0700115 builder.setRequiresBatteryNotLow(true);
116 builder.setMinimumLatency(nextScheduleTime);
Christopher Tate115afda2014-06-06 19:06:26 -0700117 tm.schedule(builder.build());
118 }
119
Jaegeuk Kim8975f782019-04-25 05:00:36 -0700120 private static Calendar offsetFromTodayMidnight(int nDays, int nHours) {
Christopher Tate115afda2014-06-06 19:06:26 -0700121 Calendar calendar = Calendar.getInstance();
122 calendar.setTimeInMillis(System.currentTimeMillis());
Jaegeuk Kim8975f782019-04-25 05:00:36 -0700123 calendar.set(Calendar.HOUR_OF_DAY, nHours);
Christopher Tate115afda2014-06-06 19:06:26 -0700124 calendar.set(Calendar.MINUTE, 0);
125 calendar.set(Calendar.SECOND, 0);
126 calendar.set(Calendar.MILLISECOND, 0);
Jaegeuk Kim8975f782019-04-25 05:00:36 -0700127 calendar.add(Calendar.DAY_OF_MONTH, nDays);
Christopher Tate115afda2014-06-06 19:06:26 -0700128 return calendar;
Christopher Tated417d622013-08-19 16:14:25 -0700129 }
130}