1b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav/*
2b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * Copyright (C) 2013 The Android Open Source Project
3b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav *
4b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * Licensed under the Apache License, Version 2.0 (the "License");
5b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * you may not use this file except in compliance with the License.
6b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * You may obtain a copy of the License at
7b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav *
8b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav *      http://www.apache.org/licenses/LICENSE-2.0
9b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav *
10b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * Unless required by applicable law or agreed to in writing, software
11b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * distributed under the License is distributed on an "AS IS" BASIS,
12b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * See the License for the specific language governing permissions and
14b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * limitations under the License.
15b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav */
16b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
17b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavpackage com.android.server;
18b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
19b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.app.Activity;
206a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslavimport android.app.AlarmManager;
216a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslavimport android.app.PendingIntent;
22b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.content.BroadcastReceiver;
23b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.content.Context;
24b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.content.Intent;
25b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.content.IntentFilter;
26b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.os.Handler;
27b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.os.Looper;
28b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.os.PowerManager;
29b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.os.PowerManager.WakeLock;
30b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.os.SystemClock;
31b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.os.UserHandle;
32b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavimport android.util.Log;
33b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
34b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav/**
35b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * This service observes the device state and when applicable sends
36b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * broadcasts at the beginning and at the end of a period during which
37b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * observers can perform idle maintenance tasks. Typical use of the
38b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * idle maintenance is to perform somehow expensive tasks that can be
39b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * postponed to a moment when they will not degrade user experience.
40b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav *
41b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * The current implementation is very simple. The start of a maintenance
42b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * window is announced if: the screen is off or showing a dream AND the
43b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * battery level is more than twenty percent AND at least one hour passed
44b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * activity).
45b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav *
46b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * The end of a maintenance window is announced only if: a start was
47b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav * announced AND the screen turned on or a dream was stopped.
48b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav */
49b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslavpublic class IdleMaintenanceService extends BroadcastReceiver {
50b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
516a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private static final boolean DEBUG = false;
52b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
53b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    private static final String LOG_TAG = IdleMaintenanceService.class.getSimpleName();
54b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
55b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
56b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
576a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
58f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav
59f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
60f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav
61f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
62b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
636a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
64f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav
656a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
66b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
676a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
68b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
696a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
706a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
716a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
726a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private static final Intent sIdleMaintenanceStartIntent;
736a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    static {
746a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        sIdleMaintenanceStartIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
756a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        sIdleMaintenanceStartIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
766a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    };
776a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
786a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private static final Intent sIdleMaintenanceEndIntent;
796a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    static {
806a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        sIdleMaintenanceEndIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
816a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        sIdleMaintenanceEndIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
826a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    }
836a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
846a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private final AlarmManager mAlarmService;
856a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
866a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private final BatteryService mBatteryService;
876a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
886a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
89b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
90b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    private final Context mContext;
91b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
92b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    private final WakeLock mWakeLock;
93b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
94b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    private final Handler mHandler;
95b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
966a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private long mLastIdleMaintenanceStartTimeMillis;
97b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
98b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
99b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
100b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    private boolean mIdleMaintenanceStarted;
101b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
1026a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    public IdleMaintenanceService(Context context, BatteryService batteryService) {
103b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        mContext = context;
1046a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        mBatteryService = batteryService;
1056a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
1066a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
107b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
108b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
109b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
110b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
111b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        mHandler = new Handler(mContext.getMainLooper());
112b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
1136a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
1146a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
1156a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
1166a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                intent, PendingIntent.FLAG_UPDATE_CURRENT);
1176a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
118b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        register(mContext.getMainLooper());
119b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    }
120b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
121b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    public void register(Looper looper) {
122b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        IntentFilter intentFilter = new IntentFilter();
123b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
1246a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        // Alarm actions.
1256a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
1266a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
127b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        // Battery actions.
128b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
129b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
130b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        // Screen actions.
131b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
132b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
133b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
134b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        // Dream actions.
135b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
136b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
137b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
138b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        mContext.registerReceiverAsUser(this, UserHandle.ALL,
139b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav                intentFilter, null, new Handler(looper));
140b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    }
141b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
1426a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
1436a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
1446a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
1456a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                mUpdateIdleMaintenanceStatePendingIntent);
1466a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    }
1476a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
1486a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private void unscheduleUpdateIdleMaintenanceState() {
1496a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
1506a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    }
1516a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
152b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    private void updateIdleMaintenanceState() {
153b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        if (mIdleMaintenanceStarted) {
1546a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // Idle maintenance can be interrupted by user activity, or duration
1556a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // time out, or low battery.
1566a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            if (!lastUserActivityPermitsIdleMaintenanceRunning()
1576a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                    || !batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
1586a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                unscheduleUpdateIdleMaintenanceState();
159b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav                mIdleMaintenanceStarted = false;
160f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav                EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
1616a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                        mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
1626a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                        isBatteryCharging() ? 1 : 0);
163b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav                sendIdleMaintenanceEndIntent();
1646a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                // We stopped since we don't have enough battery or timed out but the
1656a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                // user is not using the device, so we should be able to run maintenance
1666a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                // in the next maintenance window since the battery may be charged
1676a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                // without interaction and the min interval between maintenances passed.
1686a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                if (!batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
1696a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                    scheduleUpdateIdleMaintenanceState(
1706a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                            getNextIdleMaintenanceIntervalStartFromNow());
1716a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                }
172b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav            }
173f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav        } else if (deviceStatePermitsIdleMaintenanceStart()
174b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav                && lastUserActivityPermitsIdleMaintenanceStart()
175b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav                && lastRunPermitsIdleMaintenanceStart()) {
1766a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // Now that we started idle maintenance, we should schedule another
1776a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // update for the moment when the idle maintenance times out.
1786a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
179b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav            mIdleMaintenanceStarted = true;
180f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav            EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
1816a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                    mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
1826a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                    isBatteryCharging() ? 1 : 0);
183f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav            mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
184b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav            sendIdleMaintenanceStartIntent();
1856a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        } else if (lastUserActivityPermitsIdleMaintenanceStart()) {
1866a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav             if (lastRunPermitsIdleMaintenanceStart()) {
1876a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                // The user does not use the device and we did not run maintenance in more
1886a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                // than the min interval between runs, so schedule an update - maybe the
1896a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                // battery will be charged latter.
1906a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
1916a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav             } else {
1926a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                 // The user does not use the device but we have run maintenance in the min
1936a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                 // interval between runs, so schedule an update after the min interval ends.
1946a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                 scheduleUpdateIdleMaintenanceState(
1956a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                         getNextIdleMaintenanceIntervalStartFromNow());
1966a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav             }
197b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        }
198b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    }
199b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
2006a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private long getNextIdleMaintenanceIntervalStartFromNow() {
2016a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
2026a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                - SystemClock.elapsedRealtime();
2036a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    }
2046a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
205b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    private void sendIdleMaintenanceStartIntent() {
206b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        mWakeLock.acquire();
2076a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceStartIntent, UserHandle.ALL,
208b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav                null, this, mHandler, Activity.RESULT_OK, null, null);
209b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    }
210b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
211b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    private void sendIdleMaintenanceEndIntent() {
212b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        mWakeLock.acquire();
2136a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceEndIntent, UserHandle.ALL,
214b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav                null, this, mHandler, Activity.RESULT_OK, null, null);
215b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    }
216b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
217f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav    private boolean deviceStatePermitsIdleMaintenanceStart() {
2186a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        final int minBatteryLevel = isBatteryCharging()
219f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav                ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
220f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav                : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
221f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
2226a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                && mBatteryService.getBatteryLevel() > minBatteryLevel);
223f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav    }
224f23b64df94bbff9ac8c87832dea9c1bc0ae9a950Svetoslav
2256a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private boolean lastUserActivityPermitsIdleMaintenanceStart() {
2266a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        // The last time the user poked the device is above the threshold.
227b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
2286a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
2296a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                    > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
230b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    }
231b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
2326a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private boolean lastRunPermitsIdleMaintenanceStart() {
2336a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        // Enough time passed since the last maintenance run.
2346a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        return SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
2356a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
236b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    }
237b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
2386a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
2396a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        // The user is not using the device.
2406a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
2416a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    }
2426a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
2436a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
2446a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        // Battery not too low and the maintenance duration did not timeout.
2456a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
2466a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
2476a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                        > SystemClock.elapsedRealtime());
2486a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    }
2496a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav
2506a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav    private boolean isBatteryCharging() {
2516a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        return mBatteryService.getPlugType() > 0
2526a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                && mBatteryService.getInvalidCharger() == 0;
253b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    }
254b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav
255b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    @Override
256b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    public void onReceive(Context context, Intent intent) {
257b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        if (DEBUG) {
258b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav            Log.i(LOG_TAG, intent.getAction());
259b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        }
260b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        String action = intent.getAction();
261b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
2626a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // We care about battery only if maintenance is in progress so we can
2636a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // stop it if battery is too low. Note that here we assume that the
2646a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // maintenance clients are properly holding a wake lock. We will
2656a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // refactor the maintenance to use services instead of intents for the
2666a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // next release. The only client for this for now is internal an holds
2676a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // a wake lock correctly.
2686a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            if (mIdleMaintenanceStarted) {
2696a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav                updateIdleMaintenanceState();
2706a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            }
271b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        } else if (Intent.ACTION_SCREEN_ON.equals(action)
272b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav                || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
273b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav            mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
2746a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // Unschedule any future updates since we already know that maintenance
2756a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // cannot be performed since the user is back.
2766a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            unscheduleUpdateIdleMaintenanceState();
2776a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // If the screen went on/stopped dreaming, we know the user is using the
2786a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // device which means that idle maintenance should be stopped if running.
2796a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            updateIdleMaintenanceState();
280b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        } else if (Intent.ACTION_SCREEN_OFF.equals(action)
281b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav                || Intent.ACTION_DREAMING_STARTED.equals(action)) {
282b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav            mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
2836a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // If screen went off/started dreaming, we may be able to start idle maintenance
2846a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // after the minimal user inactivity elapses. We schedule an alarm for when
2856a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // this timeout elapses since the device may go to sleep by then.
2866a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
2876a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav        } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
2886a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            updateIdleMaintenanceState();
289b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
290b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav                || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
2916a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // We were holding a wake lock while broadcasting the idle maintenance
2926a08a12b8e8ef6fa04932c7a1c5255e3f158a3c8Svetoslav            // intents but now that we finished the broadcast release the wake lock.
293b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav            mWakeLock.release();
294b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav        }
295b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav    }
296b3038ec7cfc5d26eb0be18ae65c62825556bca0fSvetoslav}
297