Bug 1365120 - Add WakeLockDelegate; r=rbarker
authorJim Chen <nchen@mozilla.com>
Thu, 18 May 2017 17:40:32 -0400
changeset 409684 f13e13645ca1acb6ed957f778252100b3f713fb8
parent 409683 623036d074b415a010a734fcb5c53c88fd0ed143
child 409685 804d217db1670427a1729a7db0cc840dd7dde219
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrbarker
bugs1365120
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1365120 - Add WakeLockDelegate; r=rbarker Add and use WakeLockDelegate within GeckoView code to replace the notifyWakeLockChanged method in GeckoInterface. Fennec will use the default implementation in GeckoView, while GeckoView users are free to override the default implementation through GeckoAppShell.setWakeLockDelegate. MozReview-Commit-ID: I8S8q2RZRMu
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/moz.build
mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/WakeLockDelegate.java
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -47,17 +47,16 @@ import org.mozilla.gecko.util.HardwareUt
 import org.mozilla.gecko.util.PrefUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.ViewUtil;
 import org.mozilla.gecko.widget.ActionModePresenter;
 import org.mozilla.gecko.widget.AnchoredPopup;
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
-import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -69,17 +68,16 @@ import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
-import android.os.PowerManager;
 import android.os.StrictMode;
 import android.provider.ContactsContract;
 import android.provider.MediaStore.Images.Media;
 import android.support.annotation.NonNull;
 import android.support.annotation.WorkerThread;
 import android.support.design.widget.Snackbar;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -211,18 +209,16 @@ public abstract class GeckoApp extends G
     protected DoorHangerPopup mDoorHangerPopup;
     protected FormAssistPopup mFormAssistPopup;
 
     protected GeckoView mLayerView;
 
     private FullScreenHolder mFullScreenPluginContainer;
     private View mFullScreenPluginView;
 
-    private final HashMap<String, PowerManager.WakeLock> mWakeLocks = new HashMap<String, PowerManager.WakeLock>();
-
     protected boolean mLastSessionCrashed;
     protected boolean mShouldRestore;
     private boolean mSessionRestoreParsingFinished = false;
 
     protected int mLastSelectedTabId = INVALID_TAB_ID;
     protected String mLastSessionUUID = null;
     protected boolean mCheckTabSelectionOnResume = false;
 
@@ -2873,47 +2869,16 @@ public abstract class GeckoApp extends G
         }
     }
 
     @Override
     public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
         Permissions.onRequestPermissionsResult(this, permissions, grantResults);
     }
 
-    private static final String CPU = "cpu";
-    private static final String SCREEN = "screen";
-
-    // Called when a Gecko Hal WakeLock is changed
-    @Override
-    // We keep the wake lock independent from the function scope, so we need to
-    // suppress the linter warning.
-    @SuppressLint("Wakelock")
-    public void notifyWakeLockChanged(String topic, String state) {
-        PowerManager.WakeLock wl = mWakeLocks.get(topic);
-        if (state.equals("locked-foreground") && wl == null) {
-            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
-
-            if (CPU.equals(topic)) {
-              wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, topic);
-            } else if (SCREEN.equals(topic)) {
-              // ON_AFTER_RELEASE is set, the user activity timer will be reset when the
-              // WakeLock is released, causing the illumination to remain on a bit longer.
-              wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, topic);
-            }
-
-            if (wl != null) {
-              wl.acquire();
-              mWakeLocks.put(topic, wl);
-            }
-        } else if (!state.equals("locked-foreground") && wl != null) {
-            wl.release();
-            mWakeLocks.remove(topic);
-        }
-    }
-
     private void geckoConnected() {
         mLayerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
     }
 
     @Override
     public void setAccessibilityEnabled(boolean enabled) {
     }
 
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -430,16 +430,17 @@ gvjar.sources += [geckoview_source_dir +
     'process/GeckoServiceChildProcess.java',
     'ScreenManagerHelper.java',
     'ScreenOrientationDelegate.java',
     'sqlite/ByteBufferInputStream.java',
     'sqlite/MatrixBlobCursor.java',
     'sqlite/SQLiteBridge.java',
     'sqlite/SQLiteBridgeException.java',
     'TouchEventInterceptor.java',
+    'WakeLockDelegate.java',
 ]]
 
 gvjar.sources += [geckoview_thirdparty_source_dir + f for f in [
     'java/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java',
     'java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java',
     'java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java',
 ]]
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
@@ -29,20 +29,16 @@ public class BaseGeckoInterface implemen
     // Bug 908786: Implement this
     @Override
     public void addAppStateListener(GeckoAppShell.AppStateListener listener) {}
 
     // Bug 908787: Implement this
     @Override
     public void removeAppStateListener(GeckoAppShell.AppStateListener listener) {}
 
-    // Bug 908789: Implement this
-    @Override
-    public void notifyWakeLockChanged(String topic, String state) {}
-
     @Override
     public boolean areTabsShown() {
         return false;
     }
 
     // Bug 908792: Implement this
     @Override
     public void invalidateOptionsMenu() {}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -19,31 +19,31 @@ import java.net.URLConnection;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
 
-import android.annotation.SuppressLint;
 import org.mozilla.gecko.annotation.JNITarget;
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.process.GeckoProcessManager;
 import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.IOUtils;
 import org.mozilla.gecko.util.ProxySelector;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.Manifest;
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -73,20 +73,22 @@ import android.location.LocationManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.support.annotation.NonNull;
+import android.support.v4.util.SimpleArrayMap;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.Surface;
@@ -396,18 +398,21 @@ public class GeckoAppShell
     /* package */ static native void onSensorChanged(int hal_type, float x, float y, float z,
                                                      float w, int accuracy, long time);
 
     @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
     /* package */ static native void onLocationChanged(double latitude, double longitude,
                                                        double altitude, float accuracy,
                                                        float bearing, float speed, long time);
 
-    private static class DefaultListeners
-            implements SensorEventListener, LocationListener, NotificationListener, ScreenOrientationDelegate {
+    private static class DefaultListeners implements SensorEventListener,
+                                                     LocationListener,
+                                                     NotificationListener,
+                                                     ScreenOrientationDelegate,
+                                                     WakeLockDelegate {
         @Override
         public void onAccuracyChanged(Sensor sensor, int accuracy) {
         }
 
         private static int HalSensorAccuracyFor(int androidAccuracy) {
             switch (androidAccuracy) {
             case SensorManager.SENSOR_STATUS_UNRELIABLE:
                 return GeckoHalDefines.SENSOR_ACCURACY_UNRELIABLE;
@@ -528,27 +533,64 @@ public class GeckoAppShell
             GeckoAppShell.onNotificationClose(name, cookie);
         }
 
         @Override // NotificationListener
         public void closeNotification(String name) {
             // Do nothing.
         }
 
-        @Override
+        @Override // ScreenOrientationDelegate
         public boolean setRequestedOrientationForCurrentActivity(int requestedActivityInfoOrientation) {
             // Do nothing, and report that the orientation was not set.
             return false;
         }
+
+        private SimpleArrayMap<String, PowerManager.WakeLock> mWakeLocks;
+
+        @Override // WakeLockDelegate
+        @SuppressLint("Wakelock") // We keep the wake lock independent from the function
+                                  // scope, so we need to suppress the linter warning.
+        public void setWakeLockState(final String lock, final int state) {
+            if (mWakeLocks == null) {
+                mWakeLocks = new SimpleArrayMap<>(WakeLockDelegate.LOCKS_COUNT);
+            }
+
+            PowerManager.WakeLock wl = mWakeLocks.get(lock);
+
+            if (state == WakeLockDelegate.STATE_LOCKED_FOREGROUND && wl == null) {
+                final PowerManager pm = (PowerManager)
+                        getApplicationContext().getSystemService(Context.POWER_SERVICE);
+
+                if (WakeLockDelegate.LOCK_CPU.equals(lock)) {
+                  wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lock);
+                } else if (WakeLockDelegate.LOCK_SCREEN.equals(lock)) {
+                  // ON_AFTER_RELEASE is set, the user activity timer will be reset when the
+                  // WakeLock is released, causing the illumination to remain on a bit longer.
+                  wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK |
+                                      PowerManager.ON_AFTER_RELEASE, lock);
+                } else {
+                    Log.w(LOGTAG, "Unsupported wake-lock: " + lock);
+                    return;
+                }
+
+                wl.acquire();
+                mWakeLocks.put(lock, wl);
+            } else if (state != WakeLockDelegate.STATE_LOCKED_FOREGROUND && wl != null) {
+                wl.release();
+                mWakeLocks.remove(lock);
+            }
+        }
     }
 
     private static final DefaultListeners DEFAULT_LISTENERS = new DefaultListeners();
     private static SensorEventListener sSensorListener = DEFAULT_LISTENERS;
     private static LocationListener sLocationListener = DEFAULT_LISTENERS;
     private static NotificationListener sNotificationListener = DEFAULT_LISTENERS;
+    private static WakeLockDelegate sWakeLockDelegate = DEFAULT_LISTENERS;
 
     /**
      * A delegate for supporting the Screen Orientation API.
      */
     private static ScreenOrientationDelegate sScreenOrientationDelegate = DEFAULT_LISTENERS;
 
     public static SensorEventListener getSensorListener() {
         return sSensorListener;
@@ -577,16 +619,24 @@ public class GeckoAppShell
     public static ScreenOrientationDelegate getScreenOrientationDelegate() {
         return sScreenOrientationDelegate;
     }
 
     public static void setScreenOrientationDelegate(ScreenOrientationDelegate screenOrientationDelegate) {
         sScreenOrientationDelegate = (screenOrientationDelegate != null) ? screenOrientationDelegate : DEFAULT_LISTENERS;
     }
 
+    public static WakeLockDelegate getWakeLockDelegate() {
+        return sWakeLockDelegate;
+    }
+
+    public void setWakeLockDelegate(final WakeLockDelegate delegate) {
+        sWakeLockDelegate = (delegate != null) ? delegate : DEFAULT_LISTENERS;
+    }
+
     @WrapForJNI(calledFrom = "gecko")
     private static void enableSensor(int aSensortype) {
         GeckoInterface gi = getGeckoInterface();
         if (gi == null) {
             return;
         }
         SensorManager sm = (SensorManager)
             getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
@@ -1644,17 +1694,16 @@ public class GeckoAppShell
         public void onOrientationChanged();
     }
 
     public interface GeckoInterface {
         public void enableOrientationListener();
         public void disableOrientationListener();
         public void addAppStateListener(AppStateListener listener);
         public void removeAppStateListener(AppStateListener listener);
-        public void notifyWakeLockChanged(String topic, String state);
         public boolean areTabsShown();
         public void invalidateOptionsMenu();
         public boolean isForegrounded();
 
         /**
          * Create a shortcut -- generally a home-screen icon -- linking the given title to the given URI.
          * <p>
          * This method is always invoked on the Gecko thread.
@@ -1968,19 +2017,28 @@ public class GeckoAppShell
 
     @WrapForJNI(calledFrom = "gecko")
     private static void unlockScreenOrientation() {
         // TODO: don't vector through GeckoAppShell.
         GeckoScreenOrientation.getInstance().unlock();
     }
 
     @WrapForJNI(calledFrom = "gecko")
-    private static void notifyWakeLockChanged(String topic, String state) {
-        if (getGeckoInterface() != null)
-            getGeckoInterface().notifyWakeLockChanged(topic, state);
+    private static void notifyWakeLockChanged(final String topic, final String state) {
+        final int intState;
+        if ("unlocked".equals(state)) {
+            intState = WakeLockDelegate.STATE_UNLOCKED;
+        } else if ("locked-foreground".equals(state)) {
+            intState = WakeLockDelegate.STATE_LOCKED_FOREGROUND;
+        } else if ("locked-background".equals(state)) {
+            intState = WakeLockDelegate.STATE_LOCKED_BACKGROUND;
+        } else {
+            throw new IllegalArgumentException();
+        }
+        getWakeLockDelegate().setWakeLockState(topic, intState);
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private static boolean unlockProfile() {
         // Try to kill any zombie Fennec's that might be running
         GeckoAppShell.killAnyZombies();
 
         // Then force unlock this profile
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/WakeLockDelegate.java
@@ -0,0 +1,43 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko;
+
+/**
+ * A <code>WakeLockDelegate</code> is responsible for acquiring and release wake-locks.
+ */
+public interface WakeLockDelegate {
+    /**
+     * Wake-lock for the CPU.
+     */
+    final String LOCK_CPU = "cpu";
+    /**
+     * Wake-lock for the screen.
+     */
+    final String LOCK_SCREEN = "screen";
+
+    final int LOCKS_COUNT = 2;
+
+    /**
+     * No one holds the wake-lock.
+     */
+    final int STATE_UNLOCKED = 0;
+    /**
+     * The wake-lock is held by a foreground window.
+     */
+    final int STATE_LOCKED_FOREGROUND = 1;
+    /**
+     * The wake-lock is held by a background window.
+     */
+    final int STATE_LOCKED_BACKGROUND = 2;
+
+    /**
+     * Set a wake-lock to a specified state. Called from the Gecko thread.
+     *
+     * @param lock Wake-lock to set from one of the LOCK_* constants.
+     * @param state New wake-lock state from one of the STATE_* constants.
+     */
+    void setWakeLockState(String lock, int state);
+}