Enable tap-to-pulse only when needed

- Adds InputManager.setPulseEnabled().
- Adds a config overlay for the file controlling touch-to-pulse.
- Hooks up DreamManagerService with InputManager.setPulseEnabled().

Bug: 29253550
Change-Id: I4892311cc30e97d31f7be778930397fbe5c03945
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index 10fc8e6..01a5404 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -59,4 +59,9 @@
      * @param deviceId The id of input device.
      */
     public abstract void toggleCapsLock(int deviceId);
+
+    /**
+     * Set whether the input stack should deliver pulse gesture events when the device is asleep.
+     */
+    public abstract void setPulseGestureEnabled(boolean enabled);
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5fd3c36..a8bb0e0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2461,6 +2461,10 @@
     <!-- True if the device supports Sustained Performance Mode-->
     <bool name="config_sustainedPerformanceModeSupported">false</bool>
 
+    <!-- File used to enable the double touch gesture.
+         TODO: move to input HAL once ready. -->
+    <string name="config_doubleTouchGestureEnableFile"></string>
+
     <!-- Controls how we deal with externally connected physical keyboards.
          0 - When using this device, it is not clear for users to recognize when the physical
              keyboard is (should be) connected and when it is (should be) disconnected.  Most of
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6e937ff..706d2e6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2614,6 +2614,8 @@
   <!-- Pinner Service -->
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
 
+  <java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
+
   <java-symbol type="string" name="suspended_widget_accessibility" />
 
   <!-- Used internally for assistant to launch activity transitions -->
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 1f6616e..a783fa2 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -20,6 +20,7 @@
 
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import android.Manifest;
@@ -32,6 +33,8 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
+import android.hardware.input.InputManagerInternal;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -111,11 +114,16 @@
             mContext.registerReceiver(new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
+                    writePulseGestureEnabled();
                     synchronized (mLock) {
                         stopDreamLocked(false /*immediate*/);
                     }
                 }
             }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.DOZE_ENABLED), false,
+                    mDozeEnabledObserver, UserHandle.USER_ALL);
+            writePulseGestureEnabled();
         }
     }
 
@@ -414,6 +422,12 @@
         }
     }
 
+    private void writePulseGestureEnabled() {
+        ComponentName name = getDozeComponent();
+        boolean dozeEnabled = validateDream(name);
+        LocalServices.getService(InputManagerInternal.class).setPulseGestureEnabled(dozeEnabled);
+    }
+
     private static String componentsToString(ComponentName[] componentNames) {
         StringBuilder names = new StringBuilder();
         if (componentNames != null) {
@@ -450,6 +464,13 @@
         }
     };
 
+    private final ContentObserver mDozeEnabledObserver = new ContentObserver(null) {
+        @Override
+        public void onChange(boolean selfChange) {
+            writePulseGestureEnabled();
+        }
+    };
+
     /**
      * Handler for asynchronous operations performed by the dream manager.
      * Ensures operations to {@link DreamController} are single-threaded.
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index aa1d73f..74095ac 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -18,7 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Build;
 import android.os.LocaleList;
+import android.util.Log;
 import android.view.Display;
 import com.android.internal.inputmethod.InputMethodSubtypeHandle;
 import com.android.internal.os.SomeArgs;
@@ -98,8 +100,11 @@
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -109,6 +114,7 @@
 import java.util.List;
 import java.util.Locale;
 
+import libcore.io.IoUtils;
 import libcore.io.Streams;
 import libcore.util.Objects;
 
@@ -136,6 +142,8 @@
     private final Context mContext;
     private final InputManagerHandler mHandler;
 
+    private final File mDoubleTouchGestureEnableFile;
+
     private WindowManagerCallbacks mWindowManagerCallbacks;
     private WiredAccessoryCallbacks mWiredAccessoryCallbacks;
     private boolean mSystemReady;
@@ -301,6 +309,11 @@
                 + mUseDevInputEventForAudioJack);
         mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
 
+        String doubleTouchGestureEnablePath = context.getResources().getString(
+                R.string.config_doubleTouchGestureEnableFile);
+        mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
+            new File(doubleTouchGestureEnablePath);
+
         LocalServices.addService(InputManagerInternal.class, new LocalService());
     }
 
@@ -2279,5 +2292,20 @@
         public void toggleCapsLock(int deviceId) {
             nativeToggleCapsLock(mPtr, deviceId);
         }
+
+        @Override
+        public void setPulseGestureEnabled(boolean enabled) {
+            if (mDoubleTouchGestureEnableFile != null) {
+                FileWriter writer = null;
+                try {
+                    writer = new FileWriter(mDoubleTouchGestureEnableFile);
+                    writer.write(enabled ? "1" : "0");
+                } catch (IOException e) {
+                    Log.wtf(TAG, "Unable to setPulseGestureEnabled", e);
+                } finally {
+                    IoUtils.closeQuietly(writer);
+                }
+            }
+        }
     }
 }