Bug 1172740 - Implement Android HAL backend for alarms. r=snorp
authorReuben Morais <reuben.morais@gmail.com>
Thu, 01 Oct 2015 14:40:53 -0300
changeset 265538 b0b053cb93e554b0b0dc8060d8aa3e01c993b096
parent 265537 efb99939ba253b35c6a6699f83e1418464964dd6
child 265539 de6d74de668e41e227f989c276d2d10b3ad6eefe
push id15472
push usercbook@mozilla.com
push dateFri, 02 Oct 2015 11:51:34 +0000
treeherderfx-team@2c33ef6b27e0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1172740
milestone44.0a1
Bug 1172740 - Implement Android HAL backend for alarms. r=snorp
hal/android/AndroidAlarm.cpp
hal/moz.build
mobile/android/b2gdroid/app/b2gdroid.js
mobile/android/b2gdroid/app/src/main/AndroidManifest.xml
mobile/android/base/AlarmReceiver.java
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/GeckoAppShell.java
mobile/android/base/moz.build
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
new file mode 100644
--- /dev/null
+++ b/hal/android/AndroidAlarm.cpp
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "Hal.h"
+#include "AndroidBridge.h"
+
+#include "GeneratedJNINatives.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+
+class AlarmReceiver : public widget::AlarmReceiver::Natives<AlarmReceiver>
+{
+private:
+    AlarmReceiver();
+
+public:
+    static void NotifyAlarmFired() {
+        nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+            hal::NotifyAlarmFired();
+        });
+        NS_DispatchToMainThread(runnable);
+    }
+};
+
+namespace hal_impl {
+
+bool
+EnableAlarm()
+{
+    AlarmReceiver::Init();
+    return true;
+}
+
+void
+DisableAlarm()
+{
+    widget::GeckoAppShell::DisableAlarm();
+}
+
+bool
+SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
+{
+    return widget::GeckoAppShell::SetAlarm(aSeconds, aNanoseconds);
+}
+
+} // hal_impl
+} // mozilla
--- a/hal/moz.build
+++ b/hal/moz.build
@@ -28,19 +28,22 @@ UNIFIED_SOURCES += [
 ]
 
 # Hal.cpp cannot be built in unified mode because it relies on HalImpl.h.
 SOURCES += [
     'Hal.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+    LOCAL_INCLUDES += [
+        '/widget/android',
+    ]
     UNIFIED_SOURCES += [
+        'android/AndroidAlarm.cpp',
         'android/AndroidSensor.cpp',
-        'fallback/FallbackAlarm.cpp',
         'fallback/FallbackPower.cpp',
         'linux/LinuxMemory.cpp',
     ]
     # AndroidHal.cpp cannot be built in unified mode because it relies on HalImpl.h.
     SOURCES += [
         'android/AndroidHal.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
--- a/mobile/android/b2gdroid/app/b2gdroid.js
+++ b/mobile/android/b2gdroid/app/b2gdroid.js
@@ -1017,9 +1017,12 @@ pref("dom.ipc.reuse_parent_app", false);
 // behalf for this many seconds, or until it handles the system message,
 // whichever comes first.
 pref("dom.ipc.systemMessageCPULockTimeoutSec", 30);
 
 pref("dom.wakelock.enabled", true);
 
 pref("dom.webcomponents.enabled", true);
 
+// Enable the Alarms API
+pref("dom.mozAlarms.enabled", true);
+
 pref("layout.css.scroll-snap.enabled", true);
--- a/mobile/android/b2gdroid/app/src/main/AndroidManifest.xml
+++ b/mobile/android/b2gdroid/app/src/main/AndroidManifest.xml
@@ -114,11 +114,13 @@
         </activity>
 
         <service
             android:exported="false"
             android:name="org.mozilla.gecko.updater.UpdateService"
             android:process="org.mozilla.b2gdroid.UpdateService">
         </service>
 
+        <receiver android:name="org.mozilla.gecko.AlarmReceiver" >
+        </receiver>
     </application>
 
 </manifest>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/AlarmReceiver.java
@@ -0,0 +1,42 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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;
+
+import org.mozilla.gecko.annotation.WrapForJNI;
+
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.util.Log;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class AlarmReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        final WakeLock wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "GeckoAlarm");
+        wakeLock.acquire();
+
+        AlarmReceiver.notifyAlarmFired();
+        TimerTask releaseLockTask = new TimerTask() {
+            @Override
+            public void run() {
+                wakeLock.release();
+            }
+        };
+        Timer timer = new Timer();
+        // 5 seconds ought to be enough for anybody
+        timer.schedule(releaseLockTask, 5*1000);
+    }
+
+    @WrapForJNI
+    private static native void notifyAlarmFired();
+}
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -376,20 +376,23 @@
         <receiver android:name="org.mozilla.gecko.distribution.ReferrerReceiver"
                   android:exported="true">
             <intent-filter>
                 <action android:name="com.android.vending.INSTALL_REFERRER" />
             </intent-filter>
         </receiver>
 
         <service android:name="org.mozilla.gecko.Restarter"
-                  android:exported="false"
-                  android:process="@MANGLED_ANDROID_PACKAGE_NAME@.Restarter">
+                 android:exported="false"
+                 android:process="@MANGLED_ANDROID_PACKAGE_NAME@.Restarter">
         </service>
 
+        <receiver android:name="org.mozilla.gecko.AlarmReceiver" >
+        </receiver>
+
 #include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
 #include ../services/manifests/HealthReportAndroidManifest_activities.xml.in
 #include ../services/manifests/SyncAndroidManifest_activities.xml.in
 #ifdef MOZ_ANDROID_SEARCH_ACTIVITY
 #include ../search/manifests/SearchAndroidManifest_activities.xml.in
 #endif
 
 #if MOZ_CRASHREPORTER
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -54,16 +54,17 @@ import org.mozilla.gecko.util.NativeEven
 import org.mozilla.gecko.util.NativeJSContainer;
 import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.util.ProxySelector;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 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;
@@ -599,16 +600,41 @@ public class GeckoAppShell
     }
 
     @WrapForJNI
     public static void enableLocationHighAccuracy(final boolean enable) {
         locationHighAccuracyEnabled = enable;
     }
 
     @WrapForJNI
+    public static boolean setAlarm(int aSeconds, int aNanoSeconds) {
+        AlarmManager am = (AlarmManager)
+            getContext().getSystemService(Context.ALARM_SERVICE);
+
+        Intent intent = new Intent(getContext(), AlarmReceiver.class);
+        PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // AlarmManager only supports millisecond precision
+        long time = ((long)aSeconds * 1000) + ((long)aNanoSeconds/1_000_000L);
+        am.setExact(AlarmManager.RTC_WAKEUP, time, pi);
+
+        return true;
+    }
+
+    @WrapForJNI
+    public static void disableAlarm() {
+        AlarmManager am = (AlarmManager)
+            getContext().getSystemService(Context.ALARM_SERVICE);
+
+        Intent intent = new Intent(getContext(), AlarmReceiver.class);
+        PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+        am.cancel(pi);
+    }
+
+    @WrapForJNI
     public static void enableSensor(int aSensortype) {
         GeckoInterface gi = getGeckoInterface();
         if (gi == null)
             return;
         SensorManager sm = (SensorManager)
             getContext().getSystemService(Context.SENSOR_SERVICE);
 
         switch(aSensortype) {
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -155,16 +155,17 @@ if CONFIG['MOZ_WEBRTC']:
 
 gbjar = add_java_jar('gecko-browser')
 gbjar.sources += [
     'AboutPages.java',
     'AccountsHelper.java',
     'ActionModeCompat.java',
     'ActionModeCompatView.java',
     'ActivityHandlerHelper.java',
+    'AlarmReceiver.java',
     'AndroidGamepadManager.java',
     'animation/AnimationUtils.java',
     'animation/AnimatorProxy.java',
     'animation/BounceAnimatorBuilder.java',
     'animation/HeightChangeAnimation.java',
     'animation/PropertyAnimator.java',
     'animation/Rotate3DAnimation.java',
     'animation/TransitionsTracker.java',
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -32,16 +32,31 @@ public:
                 ::template Wrap<&Impl::RequestNativeStack>)
     };
 };
 
 template<class Impl>
 constexpr JNINativeMethod ANRReporter::Natives<Impl>::methods[];
 
 template<class Impl>
+class AlarmReceiver::Natives : public mozilla::jni::NativeImpl<AlarmReceiver, Impl>
+{
+public:
+    static constexpr JNINativeMethod methods[] = {
+
+        mozilla::jni::MakeNativeMethod<AlarmReceiver::NotifyAlarmFired_t>(
+                mozilla::jni::NativeStub<AlarmReceiver::NotifyAlarmFired_t, Impl>
+                ::template Wrap<&Impl::NotifyAlarmFired>)
+    };
+};
+
+template<class Impl>
+constexpr JNINativeMethod AlarmReceiver::Natives<Impl>::methods[];
+
+template<class Impl>
 class GeckoJavaSampler::Natives : public mozilla::jni::NativeImpl<GeckoJavaSampler, Impl>
 {
 public:
     static constexpr JNINativeMethod methods[] = {
 
         mozilla::jni::MakeNativeMethod<GeckoJavaSampler::GetProfilerTime_t>(
                 mozilla::jni::NativeStub<GeckoJavaSampler::GetProfilerTime_t, Impl>
                 ::template Wrap<&Impl::GetProfilerTime>)
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -16,16 +16,21 @@ constexpr char ANRReporter::GetNativeSta
 constexpr char ANRReporter::GetNativeStack_t::signature[];
 
 constexpr char ANRReporter::ReleaseNativeStack_t::name[];
 constexpr char ANRReporter::ReleaseNativeStack_t::signature[];
 
 constexpr char ANRReporter::RequestNativeStack_t::name[];
 constexpr char ANRReporter::RequestNativeStack_t::signature[];
 
+constexpr char AlarmReceiver::name[];
+
+constexpr char AlarmReceiver::NotifyAlarmFired_t::name[];
+constexpr char AlarmReceiver::NotifyAlarmFired_t::signature[];
+
 constexpr char DownloadsIntegration::name[];
 
 constexpr char DownloadsIntegration::ScanMedia_t::name[];
 constexpr char DownloadsIntegration::ScanMedia_t::signature[];
 
 auto DownloadsIntegration::ScanMedia(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1) -> void
 {
     return mozilla::jni::Method<ScanMedia_t>::Call(nullptr, nullptr, a0, a1);
@@ -132,16 +137,24 @@ auto GeckoAppShell::CreateShortcut(mozil
 constexpr char GeckoAppShell::DeleteMessageWrapper_t::name[];
 constexpr char GeckoAppShell::DeleteMessageWrapper_t::signature[];
 
 auto GeckoAppShell::DeleteMessageWrapper(int32_t a0, int32_t a1) -> void
 {
     return mozilla::jni::Method<DeleteMessageWrapper_t>::Call(nullptr, nullptr, a0, a1);
 }
 
+constexpr char GeckoAppShell::DisableAlarm_t::name[];
+constexpr char GeckoAppShell::DisableAlarm_t::signature[];
+
+auto GeckoAppShell::DisableAlarm() -> void
+{
+    return mozilla::jni::Method<DisableAlarm_t>::Call(nullptr, nullptr);
+}
+
 constexpr char GeckoAppShell::DisableBatteryNotifications_t::name[];
 constexpr char GeckoAppShell::DisableBatteryNotifications_t::signature[];
 
 auto GeckoAppShell::DisableBatteryNotifications() -> void
 {
     return mozilla::jni::Method<DisableBatteryNotifications_t>::Call(nullptr, nullptr);
 }
 
@@ -612,16 +625,24 @@ auto GeckoAppShell::ScheduleRestart() ->
 constexpr char GeckoAppShell::SendMessageWrapper_t::name[];
 constexpr char GeckoAppShell::SendMessageWrapper_t::signature[];
 
 auto GeckoAppShell::SendMessageWrapper(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1, int32_t a2) -> void
 {
     return mozilla::jni::Method<SendMessageWrapper_t>::Call(nullptr, nullptr, a0, a1, a2);
 }
 
+constexpr char GeckoAppShell::SetAlarm_t::name[];
+constexpr char GeckoAppShell::SetAlarm_t::signature[];
+
+auto GeckoAppShell::SetAlarm(int32_t a0, int32_t a1) -> bool
+{
+    return mozilla::jni::Method<SetAlarm_t>::Call(nullptr, nullptr, a0, a1);
+}
+
 constexpr char GeckoAppShell::SetFullScreen_t::name[];
 constexpr char GeckoAppShell::SetFullScreen_t::signature[];
 
 auto GeckoAppShell::SetFullScreen(bool a0) -> void
 {
     return mozilla::jni::Method<SetFullScreen_t>::Call(nullptr, nullptr, a0);
 }
 
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -71,16 +71,49 @@ public:
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
 public:
     template<class Impl> class Natives;
 };
 
+class AlarmReceiver : public mozilla::jni::Class<AlarmReceiver>
+{
+public:
+    typedef mozilla::jni::Ref<AlarmReceiver> Ref;
+    typedef mozilla::jni::LocalRef<AlarmReceiver> LocalRef;
+    typedef mozilla::jni::GlobalRef<AlarmReceiver> GlobalRef;
+    typedef const mozilla::jni::Param<AlarmReceiver>& Param;
+
+    static constexpr char name[] =
+            "org/mozilla/gecko/AlarmReceiver";
+
+protected:
+    AlarmReceiver(jobject instance) : Class(instance) {}
+
+public:
+    struct NotifyAlarmFired_t {
+        typedef AlarmReceiver Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "notifyAlarmFired";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    template<class Impl> class Natives;
+};
+
 class DownloadsIntegration : public mozilla::jni::Class<DownloadsIntegration>
 {
 public:
     typedef mozilla::jni::Ref<DownloadsIntegration> Ref;
     typedef mozilla::jni::LocalRef<DownloadsIntegration> LocalRef;
     typedef mozilla::jni::GlobalRef<DownloadsIntegration> GlobalRef;
     typedef const mozilla::jni::Param<DownloadsIntegration>& Param;
 
@@ -372,16 +405,33 @@ public:
         static const bool isMultithreaded = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
     static auto DeleteMessageWrapper(int32_t, int32_t) -> void;
 
 public:
+    struct DisableAlarm_t {
+        typedef GeckoAppShell Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "disableAlarm";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+    static auto DisableAlarm() -> void;
+
+public:
     struct DisableBatteryNotifications_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<> Args;
         static constexpr char name[] = "disableBatteryNotifications";
         static constexpr char signature[] =
                 "()V";
@@ -1454,16 +1504,35 @@ public:
         static const bool isMultithreaded = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
     static auto SendMessageWrapper(mozilla::jni::String::Param, mozilla::jni::String::Param, int32_t) -> void;
 
 public:
+    struct SetAlarm_t {
+        typedef GeckoAppShell Owner;
+        typedef bool ReturnType;
+        typedef bool SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int32_t> Args;
+        static constexpr char name[] = "setAlarm";
+        static constexpr char signature[] =
+                "(II)Z";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+    static auto SetAlarm(int32_t, int32_t) -> bool;
+
+public:
     struct SetFullScreen_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 bool> Args;
         static constexpr char name[] = "setFullScreen";
         static constexpr char signature[] =