blob: 269b4ed495e64c38e804c7657ac807e8b24b9743 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.doze;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.Vibrator;
import android.service.dreams.DozeHardware;
import android.service.dreams.DreamService;
import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import java.io.FileDescriptor;
import java.io.PrintWriter;
public class DozeService extends DreamService {
private static final boolean DEBUG = false;
private static final String TEASE_ACTION = "com.android.systemui.doze.tease";
private final String mTag = String.format("DozeService.%08x", hashCode());
private final Context mContext = this;
private Host mHost;
private DozeHardware mDozeHardware;
private SensorManager mSensors;
private Sensor mSigMotionSensor;
private PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock;
private boolean mDreaming;
private boolean mTeaseReceiverRegistered;
private boolean mSigMotionConfigured;
private boolean mSigMotionEnabled;
public DozeService() {
if (DEBUG) Log.d(mTag, "new DozeService()");
setDebug(DEBUG);
}
@Override
protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) {
super.dumpOnHandler(fd, pw, args);
pw.print(" mDreaming: "); pw.println(mDreaming);
pw.print(" mDozeHardware: "); pw.println(mDozeHardware);
pw.print(" mTeaseReceiverRegistered: "); pw.println(mTeaseReceiverRegistered);
pw.print(" mSigMotionSensor: "); pw.println(mSigMotionSensor);
pw.print(" mSigMotionConfigured: "); pw.println(mSigMotionConfigured);
pw.print(" mSigMotionEnabled: "); pw.println(mSigMotionEnabled);
}
@Override
public void onCreate() {
if (DEBUG) Log.d(mTag, "onCreate");
super.onCreate();
if (getApplication() instanceof SystemUIApplication) {
final SystemUIApplication app = (SystemUIApplication) getApplication();
mHost = app.getComponent(Host.class);
}
setWindowless(true);
mSensors = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
mSigMotionSensor = mSensors.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
mSigMotionConfigured = SystemProperties.getBoolean("doze.tease.sigmotion",
mContext.getResources().getBoolean(R.bool.doze_tease_on_significant_motion));
}
@Override
public void onAttachedToWindow() {
if (DEBUG) Log.d(mTag, "onAttachedToWindow");
super.onAttachedToWindow();
}
@Override
public void onDreamingStarted() {
super.onDreamingStarted();
mDozeHardware = getDozeHardware();
if (DEBUG) Log.d(mTag, "onDreamingStarted canDoze=" + canDoze()
+ " dozeHardware=" + mDozeHardware);
mDreaming = true;
listenForTeaseSignals(true);
requestDoze();
}
public void stayAwake(long millis) {
if (mDreaming && millis > 0) {
mWakeLock.acquire(millis);
}
}
public void startDozing() {
if (DEBUG) Log.d(mTag, "startDozing mDreaming=" + mDreaming);
if (!mDreaming) {
Log.w(mTag, "Not dozing, no longer dreaming");
return;
}
super.startDozing();
}
@Override
public void onDreamingStopped() {
if (DEBUG) Log.d(mTag, "onDreamingStopped isDozing=" + isDozing());
super.onDreamingStopped();
mDreaming = false;
mDozeHardware = null;
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
listenForTeaseSignals(false);
stopDozing();
dozingStopped();
}
@Override
public void onDetachedFromWindow() {
if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
super.onDetachedFromWindow();
dozingStopped();
}
@Override
public void onDestroy() {
if (DEBUG) Log.d(mTag, "onDestroy");
super.onDestroy();
dozingStopped();
}
private void requestDoze() {
if (mHost != null) {
mHost.requestDoze(this);
}
}
private void requestTease() {
if (mHost != null) {
mHost.requestTease(this);
}
}
private void dozingStopped() {
if (mHost != null) {
mHost.dozingStopped(this);
}
}
private void listenForTeaseSignals(boolean listen) {
if (DEBUG) Log.d(mTag, "listenForTeaseSignals: " + listen);
listenForSignificantMotion(listen);
listenForBroadcast(listen);
listenForNotifications(listen);
}
private void listenForSignificantMotion(boolean listen) {
if (!mSigMotionConfigured || mSigMotionSensor == null) return;
if (listen) {
mSigMotionEnabled =
mSensors.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
} else if (mSigMotionEnabled) {
mSensors.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
}
}
private void listenForBroadcast(boolean listen) {
if (listen) {
mContext.registerReceiver(mTeaseReceiver, new IntentFilter(TEASE_ACTION));
mTeaseReceiverRegistered = true;
} else {
if (mTeaseReceiverRegistered) {
mContext.unregisterReceiver(mTeaseReceiver);
}
mTeaseReceiverRegistered = false;
}
}
private void listenForNotifications(boolean listen) {
if (mHost == null) return;
if (listen) {
mHost.addCallback(mHostCallback);
} else {
mHost.removeCallback(mHostCallback);
}
}
private static String triggerEventToString(TriggerEvent event) {
if (event == null) return null;
final StringBuilder sb = new StringBuilder("TriggerEvent[")
.append(event.timestamp).append(',')
.append(event.sensor.getName());
if (event.values != null) {
for (int i = 0; i < event.values.length; i++) {
sb.append(',').append(event.values[i]);
}
}
return sb.append(']').toString();
}
private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
@Override
public void onTrigger(TriggerEvent event) {
if (DEBUG) Log.d(mTag, "sigMotion.onTrigger: " + triggerEventToString(event));
if (DEBUG) {
final Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
if (v != null) {
v.vibrate(1000);
}
}
requestTease();
listenForSignificantMotion(true); // reregister, this sensor only fires once
}
};
private final BroadcastReceiver mTeaseReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(mTag, "Received tease intent");
requestTease();
}
};
private final Host.Callback mHostCallback = new Host.Callback() {
@Override
public void onNewNotifications() {
if (DEBUG) Log.d(mTag, "onNewNotifications");
requestTease();
}
};
public interface Host {
void addCallback(Callback callback);
void removeCallback(Callback callback);
void requestDoze(DozeService dozeService);
void requestTease(DozeService dozeService);
void dozingStopped(DozeService dozeService);
public interface Callback {
void onNewNotifications();
}
}
}