Add support for wake gestures.

Bug: 15137158
Change-Id: I171c3269a7a16a00083e16e1cc4c7c1c2b98c05e
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e9ffc52..bec401e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4461,6 +4461,12 @@
                 INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF;
 
         /**
+         * Whether the device should wake when the wake gesture sensor detects motion.
+         * @hide
+         */
+        public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled";
+
+        /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index bf97fc0..0e025a9 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -192,4 +192,7 @@
     <!-- Default for Settings.Global.DEVICE_NAME $1=BRAND $2=MODEL-->
     <string name="def_device_name">%1$s %2$s</string>
 
+    <!-- Default for Settings.Secure.WAKE_GESTURE_ENABLED -->
+    <bool name="def_wake_gesture_enabled">true</bool>
+
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 286921e..c4a54b7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -70,7 +70,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 103;
+    private static final int DATABASE_VERSION = 104;
 
     private Context mContext;
     private int mUserHandle;
@@ -1660,6 +1660,23 @@
             }
             upgradeVersion = 103;
         }
+
+        if (upgradeVersion == 103) {
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT OR REPLACE INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadBooleanSetting(stmt, Settings.Secure.WAKE_GESTURE_ENABLED,
+                        R.bool.def_wake_gesture_enabled);
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 104;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -2222,6 +2239,9 @@
             loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS,
                     R.bool.def_install_non_market_apps);
 
+            loadBooleanSetting(stmt, Settings.Secure.WAKE_GESTURE_ENABLED,
+                    R.bool.def_wake_gesture_enabled);
+
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 1a7c0a8..fb041fa2 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -289,6 +289,9 @@
     int mDemoHdmiRotation;
     boolean mDemoHdmiRotationLock;
 
+    boolean mWakeGestureEnabledSetting;
+    MyWakeGestureListener mWakeGestureListener;
+
     // Default display does not rotate, apps that require non-default orientation will have to
     // have the orientation emulated.
     private boolean mForceDefaultOrientation = false;
@@ -525,6 +528,9 @@
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this,
                     UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.WAKE_GESTURE_ENABLED), false, this,
+                    UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.ACCELEROMETER_ROTATION), false, this,
                     UserHandle.USER_ALL);
@@ -555,6 +561,21 @@
         }
     }
 
+    class MyWakeGestureListener extends WakeGestureListener {
+        MyWakeGestureListener(Context context, Handler handler) {
+            super(context, handler);
+        }
+
+        @Override
+        public void onWakeUp() {
+            synchronized (mLock) {
+                if (shouldEnableWakeGestureLp()) {
+                    mPowerManager.wakeUp(SystemClock.uptimeMillis());
+                }
+            }
+        }
+    }
+
     class MyOrientationListener extends WindowOrientationListener {
         MyOrientationListener(Context context, Handler handler) {
             super(context, handler);
@@ -856,6 +877,7 @@
         mWindowManager = windowManager;
         mWindowManagerFuncs = windowManagerFuncs;
         mHandler = new PolicyHandler();
+        mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
         mOrientationListener = new MyOrientationListener(mContext, mHandler);
         try {
             mOrientationListener.setCurrentRotation(windowManager.getRotation());
@@ -1142,6 +1164,15 @@
                     Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT,
                     UserHandle.USER_CURRENT);
 
+            // Configure wake gesture.
+            boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.WAKE_GESTURE_ENABLED, 0,
+                    UserHandle.USER_CURRENT) != 0;
+            if (mWakeGestureEnabledSetting != wakeGestureEnabledSetting) {
+                mWakeGestureEnabledSetting = wakeGestureEnabledSetting;
+                updateWakeGestureListenerLp();
+            }
+
             // Configure rotation lock.
             int userRotation = Settings.System.getIntForUser(resolver,
                     Settings.System.USER_ROTATION, Surface.ROTATION_0,
@@ -1189,6 +1220,20 @@
         }
     }
 
+    private void updateWakeGestureListenerLp() {
+        if (shouldEnableWakeGestureLp()) {
+            mWakeGestureListener.requestWakeUpTrigger();
+        } else {
+            mWakeGestureListener.cancelWakeUpTrigger();
+        }
+    }
+
+    private boolean shouldEnableWakeGestureLp() {
+        return mWakeGestureEnabledSetting && !mScreenOnEarly
+                && (!mLidControlsSleep || mLidState != LID_CLOSED)
+                && mWakeGestureListener.isSupported();
+    }
+
     private void enablePointerLocation() {
         if (mPointerLocationView == null) {
             mPointerLocationView = new PointerLocationView(mContext);
@@ -4406,6 +4451,7 @@
             mKeyguardDelegate.onScreenTurnedOff(why);
         }
         synchronized (mLock) {
+            updateWakeGestureListenerLp();
             updateOrientationListenerLp();
             updateLockScreenTimeout();
         }
@@ -4422,6 +4468,7 @@
 
         synchronized (mLock) {
             mScreenOnEarly = true;
+            updateWakeGestureListenerLp();
             updateOrientationListenerLp();
             updateLockScreenTimeout();
         }
@@ -5022,6 +5069,10 @@
                     PowerManager.GO_TO_SLEEP_REASON_USER,
                     PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
         }
+
+        synchronized (mLock) {
+            updateWakeGestureListenerLp();
+        }
     }
 
     void updateRotation(boolean alwaysSendConfiguration) {
@@ -5482,6 +5533,9 @@
             pw.print(prefix); pw.print("mLastFocusNeedsMenu=");
                     pw.println(mLastFocusNeedsMenu);
         }
+        pw.print(prefix); pw.print("mWakeGestureEnabledSetting=");
+                pw.println(mWakeGestureEnabledSetting);
+
         pw.print(prefix); pw.print("mSupportAutoRotation="); pw.println(mSupportAutoRotation);
         pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode);
                 pw.print(" mDockMode="); pw.print(mDockMode);
@@ -5628,6 +5682,9 @@
         mNavigationBarController.dump(pw, prefix);
         PolicyControl.dump(prefix, pw);
 
+        if (mWakeGestureListener != null) {
+            mWakeGestureListener.dump(pw, prefix);
+        }
         if (mOrientationListener != null) {
             mOrientationListener.dump(pw, prefix);
         }
diff --git a/policy/src/com/android/internal/policy/impl/WakeGestureListener.java b/policy/src/com/android/internal/policy/impl/WakeGestureListener.java
new file mode 100644
index 0000000..9396c2c
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/WakeGestureListener.java
@@ -0,0 +1,100 @@
+/*
+ * 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.internal.policy.impl;
+
+import android.os.Handler;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+
+import java.io.PrintWriter;
+
+/**
+ * Watches for wake gesture sensor events then invokes the listener.
+ */
+public abstract class WakeGestureListener {
+    private static final String TAG = "WakeGestureListener";
+
+    private final SensorManager mSensorManager;
+    private final Handler mHandler;
+
+    private final Object mLock = new Object();
+
+    private boolean mTriggerRequested;
+    private Sensor mSensor;
+
+    public WakeGestureListener(Context context, Handler handler) {
+        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+        mHandler = handler;
+
+        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_WAKE_GESTURE);
+    }
+
+    public abstract void onWakeUp();
+
+    public boolean isSupported() {
+        synchronized (mLock) {
+            return mSensor != null;
+        }
+    }
+
+    public void requestWakeUpTrigger() {
+        synchronized (mLock) {
+            if (mSensor != null && !mTriggerRequested) {
+                mTriggerRequested = true;
+                mSensorManager.requestTriggerSensor(mListener, mSensor);
+            }
+        }
+    }
+
+    public void cancelWakeUpTrigger() {
+        synchronized (mLock) {
+            if (mSensor != null && mTriggerRequested) {
+                mTriggerRequested = false;
+                mSensorManager.cancelTriggerSensor(mListener, mSensor);
+            }
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        synchronized (mLock) {
+            pw.println(prefix + TAG);
+            prefix += "  ";
+            pw.println(prefix + "mTriggerRequested=" + mTriggerRequested);
+            pw.println(prefix + "mSensor=" + mSensor);
+        }
+    }
+
+    private final TriggerEventListener mListener = new TriggerEventListener() {
+        @Override
+        public void onTrigger(TriggerEvent event) {
+            synchronized (mLock) {
+                mTriggerRequested = false;
+                mHandler.post(mWakeUpRunnable);
+            }
+        }
+    };
+
+    private final Runnable mWakeUpRunnable = new Runnable() {
+        @Override
+        public void run() {
+            onWakeUp();
+        }
+    };
+}