blob: 0c90de43d8a3ca023856751b0fa538c57751db5c [file] [log] [blame]
Svetoslavb3038ec2013-02-13 14:39:30 -08001/*
2 * Copyright (C) 2013 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
19import android.app.Activity;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.os.BatteryManager;
25import android.os.Handler;
26import android.os.Looper;
27import android.os.PowerManager;
28import android.os.PowerManager.WakeLock;
29import android.os.SystemClock;
30import android.os.UserHandle;
31import android.util.Log;
32
Svetoslavb3038ec2013-02-13 14:39:30 -080033/**
34 * This service observes the device state and when applicable sends
35 * broadcasts at the beginning and at the end of a period during which
36 * observers can perform idle maintenance tasks. Typical use of the
37 * idle maintenance is to perform somehow expensive tasks that can be
38 * postponed to a moment when they will not degrade user experience.
39 *
40 * The current implementation is very simple. The start of a maintenance
41 * window is announced if: the screen is off or showing a dream AND the
42 * battery level is more than twenty percent AND at least one hour passed
43 * since the screen went off or a dream started (i.e. since the last user
44 * activity).
45 *
46 * The end of a maintenance window is announced only if: a start was
47 * announced AND the screen turned on or a dream was stopped.
48 */
49public class IdleMaintenanceService extends BroadcastReceiver {
50
51 private final boolean DEBUG = false;
52
53 private static final String LOG_TAG = IdleMaintenanceService.class.getSimpleName();
54
55 private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
56
Svetoslavf23b64d2013-04-25 14:45:54 -070057 private static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
Svetoslavb3038ec2013-02-13 14:39:30 -080058
Svetoslavf23b64d2013-04-25 14:45:54 -070059 private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
60
61 private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
62
63 private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 10; // percent
64
65 private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 60 * 60 * 1000; // 1 hour
Svetoslavb3038ec2013-02-13 14:39:30 -080066
67 private final Intent mIdleMaintenanceStartIntent =
68 new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
69
70 private final Intent mIdleMaintenanceEndIntent =
71 new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
72
73 private final Context mContext;
74
75 private final WakeLock mWakeLock;
76
77 private final Handler mHandler;
78
Svetoslavf23b64d2013-04-25 14:45:54 -070079 private long mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
Svetoslavb3038ec2013-02-13 14:39:30 -080080
81 private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
82
83 private int mBatteryLevel;
84
Svetoslavf23b64d2013-04-25 14:45:54 -070085 private boolean mBatteryCharging;
86
Svetoslavb3038ec2013-02-13 14:39:30 -080087 private boolean mIdleMaintenanceStarted;
88
89 public IdleMaintenanceService(Context context) {
90 mContext = context;
91
92 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
93 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
94
95 mHandler = new Handler(mContext.getMainLooper());
96
Svetoslavb3038ec2013-02-13 14:39:30 -080097 register(mContext.getMainLooper());
98 }
99
100 public void register(Looper looper) {
101 IntentFilter intentFilter = new IntentFilter();
102
103 // Battery actions.
104 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
105
106 // Screen actions.
107 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
108 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
109
110 // Dream actions.
111 intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
112 intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
113
114 mContext.registerReceiverAsUser(this, UserHandle.ALL,
115 intentFilter, null, new Handler(looper));
116 }
117
118 private void updateIdleMaintenanceState() {
119 if (mIdleMaintenanceStarted) {
120 // Idle maintenance can be interrupted only by
121 // a change of the device state.
Svetoslavf23b64d2013-04-25 14:45:54 -0700122 if (!deviceStatePermitsIdleMaintenanceRunning()) {
Svetoslavb3038ec2013-02-13 14:39:30 -0800123 mIdleMaintenanceStarted = false;
Svetoslavf23b64d2013-04-25 14:45:54 -0700124 EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
125 mLastUserActivityElapsedTimeMillis, mBatteryLevel,
126 mBatteryCharging ? 1 : 0);
Svetoslavb3038ec2013-02-13 14:39:30 -0800127 sendIdleMaintenanceEndIntent();
128 }
Svetoslavf23b64d2013-04-25 14:45:54 -0700129 } else if (deviceStatePermitsIdleMaintenanceStart()
Svetoslavb3038ec2013-02-13 14:39:30 -0800130 && lastUserActivityPermitsIdleMaintenanceStart()
131 && lastRunPermitsIdleMaintenanceStart()) {
132 mIdleMaintenanceStarted = true;
Svetoslavf23b64d2013-04-25 14:45:54 -0700133 EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
134 mLastUserActivityElapsedTimeMillis, mBatteryLevel,
135 mBatteryCharging ? 1 : 0);
136 mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
Svetoslavb3038ec2013-02-13 14:39:30 -0800137 sendIdleMaintenanceStartIntent();
138 }
139 }
140
141 private void sendIdleMaintenanceStartIntent() {
142 if (DEBUG) {
143 Log.i(LOG_TAG, "Broadcasting " + Intent.ACTION_IDLE_MAINTENANCE_START);
144 }
145 mWakeLock.acquire();
146 mContext.sendOrderedBroadcastAsUser(mIdleMaintenanceStartIntent, UserHandle.ALL,
147 null, this, mHandler, Activity.RESULT_OK, null, null);
148 }
149
150 private void sendIdleMaintenanceEndIntent() {
151 if (DEBUG) {
152 Log.i(LOG_TAG, "Broadcasting " + Intent.ACTION_IDLE_MAINTENANCE_END);
153 }
154 mWakeLock.acquire();
155 mContext.sendOrderedBroadcastAsUser(mIdleMaintenanceEndIntent, UserHandle.ALL,
156 null, this, mHandler, Activity.RESULT_OK, null, null);
157 }
158
Svetoslavf23b64d2013-04-25 14:45:54 -0700159 private boolean deviceStatePermitsIdleMaintenanceStart() {
160 final int minBatteryLevel = mBatteryCharging
161 ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
162 : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
Svetoslavb3038ec2013-02-13 14:39:30 -0800163 return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
Svetoslavf23b64d2013-04-25 14:45:54 -0700164 && mBatteryLevel > minBatteryLevel);
165 }
166
167 private boolean deviceStatePermitsIdleMaintenanceRunning() {
168 return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
169 && mBatteryLevel > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING);
Svetoslavb3038ec2013-02-13 14:39:30 -0800170 }
171
172 private boolean lastUserActivityPermitsIdleMaintenanceStart() {
173 return (SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
Svetoslavf23b64d2013-04-25 14:45:54 -0700174 > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
Svetoslavb3038ec2013-02-13 14:39:30 -0800175 }
176
177 private boolean lastRunPermitsIdleMaintenanceStart() {
Svetoslavf23b64d2013-04-25 14:45:54 -0700178 return SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis > MILLIS_IN_DAY;
Svetoslavb3038ec2013-02-13 14:39:30 -0800179 }
180
181 @Override
182 public void onReceive(Context context, Intent intent) {
183 if (DEBUG) {
184 Log.i(LOG_TAG, intent.getAction());
185 }
186 String action = intent.getAction();
187 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
188 final int maxBatteryLevel = intent.getExtras().getInt(BatteryManager.EXTRA_SCALE);
189 final int currBatteryLevel = intent.getExtras().getInt(BatteryManager.EXTRA_LEVEL);
190 mBatteryLevel = (int) (((float) maxBatteryLevel / 100) * currBatteryLevel);
Svetoslavf23b64d2013-04-25 14:45:54 -0700191 final int pluggedState = intent.getExtras().getInt(BatteryManager.EXTRA_PLUGGED);
192 final int chargerState = intent.getExtras().getInt(
193 BatteryManager.EXTRA_INVALID_CHARGER, 0);
194 mBatteryCharging = (pluggedState > 0 && chargerState == 0);
Svetoslavb3038ec2013-02-13 14:39:30 -0800195 } else if (Intent.ACTION_SCREEN_ON.equals(action)
196 || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
197 mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
198 } else if (Intent.ACTION_SCREEN_OFF.equals(action)
199 || Intent.ACTION_DREAMING_STARTED.equals(action)) {
200 mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
201 } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
202 || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
203 mWakeLock.release();
204 return;
205 }
206 updateIdleMaintenanceState();
207 }
208}