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 265651 b0b053cb93e554b0b0dc8060d8aa3e01c993b096
parent 265650 efb99939ba253b35c6a6699f83e1418464964dd6
child 265652 de6d74de668e41e227f989c276d2d10b3ad6eefe
push id66003
push usercbook@mozilla.com
push dateFri, 02 Oct 2015 11:37:40 +0000
treeherdermozilla-inbound@3fd732d24f46 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1172740
milestone44.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 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[] =