blob: cc0d4a7c87d4707dc13d518fd543f5c7f9116ac5 [file] [log] [blame]
John Spurlockbf370992014-06-17 13:58:31 -04001/*
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.systemui.doze;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.hardware.Sensor;
24import android.hardware.SensorManager;
25import android.hardware.TriggerEvent;
26import android.hardware.TriggerEventListener;
27import android.os.PowerManager;
28import android.os.Vibrator;
29import android.service.dreams.DozeHardware;
30import android.service.dreams.DreamService;
31import android.util.Log;
32
33import com.android.systemui.SystemUIApplication;
34
35public class DozeService extends DreamService {
36 private static final boolean DEBUG = false;
37
38 private static final String TEASE_ACTION = "com.android.systemui.doze.tease";
39
40 private final String mTag = String.format("DozeService.%08x", hashCode());
41 private final Context mContext = this;
42
43 private Host mHost;
44 private DozeHardware mDozeHardware;
45 private SensorManager mSensors;
46 private Sensor mSigMotionSensor;
47 private PowerManager mPowerManager;
48 private PowerManager.WakeLock mWakeLock;
49 private boolean mDreaming;
50 private boolean mTeaseReceiverRegistered;
51
52 public DozeService() {
53 if (DEBUG) Log.d(mTag, "new DozeService()");
54 setDebug(DEBUG);
55 }
56
57 @Override
58 public void onCreate() {
59 if (DEBUG) Log.d(mTag, "onCreate");
60 super.onCreate();
61
62 if (getApplication() instanceof SystemUIApplication) {
63 final SystemUIApplication app = (SystemUIApplication) getApplication();
64 mHost = app.getComponent(Host.class);
65 }
66
67 setWindowless(true);
68
69 mSensors = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
70 mSigMotionSensor = mSensors.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
71 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
72 mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
73 }
74
75 @Override
76 public void onAttachedToWindow() {
77 if (DEBUG) Log.d(mTag, "onAttachedToWindow");
78 super.onAttachedToWindow();
79 }
80
81 @Override
82 public void onDreamingStarted() {
83 super.onDreamingStarted();
84 mDozeHardware = getDozeHardware();
85 if (DEBUG) Log.d(mTag, "onDreamingStarted canDoze=" + canDoze()
86 + " dozeHardware=" + mDozeHardware);
87 mDreaming = true;
88 listenForTeaseSignals(true);
89 requestDoze();
90 }
91
92 public void stayAwake(long millis) {
93 if (mDreaming && millis > 0) {
94 mWakeLock.acquire(millis);
95 }
96 }
97
98 public void startDozing() {
99 if (DEBUG) Log.d(mTag, "startDozing mDreaming=" + mDreaming);
100 if (!mDreaming) {
101 Log.w(mTag, "Not dozing, no longer dreaming");
102 return;
103 }
104
105 super.startDozing();
106 }
107
108 @Override
109 public void onDreamingStopped() {
110 if (DEBUG) Log.d(mTag, "onDreamingStopped isDozing=" + isDozing());
111 super.onDreamingStopped();
112
113 mDreaming = false;
114 mDozeHardware = null;
115 if (mWakeLock.isHeld()) {
116 mWakeLock.release();
117 }
118 listenForTeaseSignals(false);
119 stopDozing();
120 dozingStopped();
121 }
122
123 @Override
124 public void onDetachedFromWindow() {
125 if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
126 super.onDetachedFromWindow();
127
128 dozingStopped();
129 }
130
131 @Override
132 public void onDestroy() {
133 if (DEBUG) Log.d(mTag, "onDestroy");
134 super.onDestroy();
135
136 dozingStopped();
137 }
138
139 private void requestDoze() {
140 if (mHost != null) {
141 mHost.requestDoze(this);
142 }
143 }
144
145 private void requestTease() {
146 if (mHost != null) {
147 mHost.requestTease(this);
148 }
149 }
150
151 private void dozingStopped() {
152 if (mHost != null) {
153 mHost.dozingStopped(this);
154 }
155 }
156
157 private void listenForTeaseSignals(boolean listen) {
158 if (DEBUG) Log.d(mTag, "listenForTeaseSignals: " + listen);
159 if (mHost == null) return;
160 listenForSignificantMotion(listen);
161 if (listen) {
162 mContext.registerReceiver(mTeaseReceiver, new IntentFilter(TEASE_ACTION));
163 mTeaseReceiverRegistered = true;
164 mHost.addCallback(mHostCallback);
165 } else {
166 if (mTeaseReceiverRegistered) {
167 mContext.unregisterReceiver(mTeaseReceiver);
168 }
169 mTeaseReceiverRegistered = false;
170 mHost.removeCallback(mHostCallback);
171 }
172 }
173
174 private void listenForSignificantMotion(boolean listen) {
175 if (mSigMotionSensor == null) return;
176 if (listen) {
177 mSensors.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
178 } else {
179 mSensors.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
180 }
181 }
182
183 private static String triggerEventToString(TriggerEvent event) {
184 if (event == null) return null;
185 final StringBuilder sb = new StringBuilder("TriggerEvent[")
186 .append(event.timestamp).append(',')
187 .append(event.sensor.getName());
188 if (event.values != null) {
189 for (int i = 0; i < event.values.length; i++) {
190 sb.append(',').append(event.values[i]);
191 }
192 }
193 return sb.append(']').toString();
194 }
195
196 private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
197 @Override
198 public void onTrigger(TriggerEvent event) {
199 if (DEBUG) Log.d(mTag, "sigMotion.onTrigger: " + triggerEventToString(event));
200 if (DEBUG) {
201 final Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
202 if (v != null) {
203 v.vibrate(1000);
204 }
205 }
206 requestTease();
207 listenForSignificantMotion(true); // reregister, this sensor only fires once
208 }
209 };
210
211 private final BroadcastReceiver mTeaseReceiver = new BroadcastReceiver() {
212 @Override
213 public void onReceive(Context context, Intent intent) {
214 if (DEBUG) Log.d(mTag, "Received tease intent");
215 requestTease();
216 }
217 };
218
219 private final Host.Callback mHostCallback = new Host.Callback() {
220 @Override
221 public void onNewNotifications() {
222 if (DEBUG) Log.d(mTag, "onNewNotifications");
223 requestTease();
224 }
225 };
226
227 public interface Host {
228 void addCallback(Callback callback);
229 void removeCallback(Callback callback);
230 void requestDoze(DozeService dozeService);
231 void requestTease(DozeService dozeService);
232 void dozingStopped(DozeService dozeService);
233
234 public interface Callback {
235 void onNewNotifications();
236 }
237 }
238}