Bug 1288838 - Add telemetry native methods to Android telemetry module; r=snorp
authorJim Chen <nchen@mozilla.com>
Thu, 04 Aug 2016 09:16:04 -0400
changeset 308199 9a90e9fe5bf1c99dc1f049b259155c5f89e5e26d
parent 308198 32966f97f82e60c65372de3a2ee7bb11d3c1cb10
child 308200 d404ad0723c0585341d3f2400b6e0f672bc1b441
push id31092
push usercbook@mozilla.com
push dateFri, 05 Aug 2016 10:16:59 +0000
treeherderautoland@b97dd7dd3cb9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1288838
milestone51.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 1288838 - Add telemetry native methods to Android telemetry module; r=snorp Add and use native method calls in Telemetry.java to replace the telemetry events in GeckoEvent.
mobile/android/base/java/org/mozilla/gecko/Telemetry.java
mobile/android/tests/browser/robocop/testUITelemetry.js
widget/android/Telemetry.h
widget/android/nsAppShell.cpp
widget/android/nsIAndroidBridge.idl
--- a/mobile/android/base/java/org/mozilla/gecko/Telemetry.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Telemetry.java
@@ -1,16 +1,17 @@
 /* -*- 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;
 
 import org.mozilla.gecko.annotation.RobocopTarget;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.TelemetryContract.Event;
 import org.mozilla.gecko.TelemetryContract.Method;
 import org.mozilla.gecko.TelemetryContract.Reason;
 import org.mozilla.gecko.TelemetryContract.Session;
 
 import android.os.SystemClock;
 import android.util.Log;
 
@@ -23,35 +24,54 @@ import android.util.Log;
  *   avoid timing a user activity when their phone is in their pocket!
  *
  * The majority of methods in this class are defined in terms of real time.
  */
 @RobocopTarget
 public class Telemetry {
     private static final String LOGTAG = "Telemetry";
 
+    @WrapForJNI(stubName="AddHistogram")
+    private static native void nativeAddHistogram(String name, int value);
+    @WrapForJNI(stubName="AddKeyedHistogram")
+    private static native void nativeAddKeyedHistogram(String name, String key, int value);
+    @WrapForJNI(stubName="StartUISession")
+    private static native void nativeStartUiSession(String name, long timestamp);
+    @WrapForJNI(stubName="StopUISession")
+    private static native void nativeStopUiSession(String name, String reason, long timestamp);
+    @WrapForJNI(stubName="AddUIEvent")
+    private static native void nativeAddUiEvent(String action, String method,
+                                                long timestamp, String extras);
+
     public static long uptime() {
         return SystemClock.uptimeMillis();
     }
 
     public static long realtime() {
         return SystemClock.elapsedRealtime();
     }
 
     // Define new histograms in:
     // toolkit/components/telemetry/Histograms.json
     public static void addToHistogram(String name, int value) {
-        GeckoEvent event = GeckoEvent.createTelemetryHistogramAddEvent(name, value);
-        GeckoAppShell.sendEventToGecko(event);
+        if (GeckoThread.isRunning()) {
+            nativeAddHistogram(name, value);
+        } else {
+            GeckoThread.queueNativeCall(Telemetry.class, "nativeAddHistogram",
+                                        String.class, name, value);
+        }
     }
 
-    public static void addToKeyedHistogram(String histogram, String keyName, int value) {
-        GeckoEvent event = GeckoEvent.createTelemetryKeyedHistogramAddEvent(histogram,
-                keyName, value);
-        GeckoAppShell.sendEventToGecko(event);
+    public static void addToKeyedHistogram(String name, String key, int value) {
+        if (GeckoThread.isRunning()) {
+            nativeAddKeyedHistogram(name, key, value);
+        } else {
+            GeckoThread.queueNativeCall(Telemetry.class, "nativeAddKeyedHistogram",
+                                        String.class, name, String.class, key, value);
+        }
     }
 
     public abstract static class Timer {
         private final long mStartTime;
         private final String mName;
 
         private volatile boolean mHasFinished;
         private volatile long mElapsed = -1;
@@ -116,33 +136,40 @@ public class Telemetry {
             return Telemetry.uptime();
         }
     }
 
     public static void startUISession(final Session session, final String sessionNameSuffix) {
         final String sessionName = getSessionName(session, sessionNameSuffix);
 
         Log.d(LOGTAG, "StartUISession: " + sessionName);
-        final GeckoEvent geckoEvent =
-                GeckoEvent.createTelemetryUISessionStartEvent(sessionName, realtime());
-        GeckoAppShell.sendEventToGecko(geckoEvent);
+        if (GeckoThread.isRunning()) {
+            nativeStartUiSession(sessionName, realtime());
+        } else {
+            GeckoThread.queueNativeCall(Telemetry.class, "nativeStartUiSession",
+                                        String.class, sessionName, realtime());
+        }
     }
 
     public static void startUISession(final Session session) {
         startUISession(session, null);
     }
 
     public static void stopUISession(final Session session, final String sessionNameSuffix,
             final Reason reason) {
         final String sessionName = getSessionName(session, sessionNameSuffix);
 
         Log.d(LOGTAG, "StopUISession: " + sessionName + ", reason=" + reason);
-        final GeckoEvent geckoEvent = GeckoEvent.createTelemetryUISessionStopEvent(
-                sessionName, reason.toString(), realtime());
-        GeckoAppShell.sendEventToGecko(geckoEvent);
+        if (GeckoThread.isRunning()) {
+            nativeStopUiSession(sessionName, reason.toString(), realtime());
+        } else {
+            GeckoThread.queueNativeCall(Telemetry.class, "nativeStopUiSession",
+                                        String.class, sessionName,
+                                        String.class, reason.toString(), realtime());
+        }
     }
 
     public static void stopUISession(final Session session, final Reason reason) {
         stopUISession(session, null, reason);
     }
 
     public static void stopUISession(final Session session, final String sessionNameSuffix) {
         stopUISession(session, sessionNameSuffix, Reason.NONE);
@@ -169,19 +196,23 @@ public class Telemetry {
             throw new IllegalArgumentException("Expected non-null method - use Method.NONE?");
         }
 
         if (!AppConstants.RELEASE_BUILD) {
             final String logString = "SendUIEvent: event = " + eventName + " method = " + method + " timestamp = " +
                     timestamp + " extras = " + extras;
             Log.d(LOGTAG, logString);
         }
-        final GeckoEvent geckoEvent = GeckoEvent.createTelemetryUIEvent(
-                eventName, method.toString(), timestamp, extras);
-        GeckoAppShell.sendEventToGecko(geckoEvent);
+        if (GeckoThread.isRunning()) {
+            nativeAddUiEvent(eventName, method.toString(), timestamp, extras);
+        } else {
+            GeckoThread.queueNativeCall(Telemetry.class, "nativeAddUiEvent",
+                                        String.class, eventName, String.class, method.toString(),
+                                        timestamp, String.class, extras);
+        }
     }
 
     public static void sendUIEvent(final Event event, final Method method, final long timestamp,
             final String extras) {
         sendUIEvent(event.toString(), method, timestamp, extras);
     }
 
     public static void sendUIEvent(final Event event, final Method method, final long timestamp) {
--- a/mobile/android/tests/browser/robocop/testUITelemetry.js
+++ b/mobile/android/tests/browser/robocop/testUITelemetry.js
@@ -12,18 +12,17 @@ Cu.import("resource://gre/modules/Servic
 const EVENT_TEST1 = "_test_event_1.1";
 const EVENT_TEST2 = "_test_event_2.1";
 const EVENT_TEST3 = "_test_event_3.1";
 const EVENT_TEST4 = "_test_event_4.1";
 
 const METHOD_TEST1 = "_test_method_1";
 const METHOD_TEST2 = "_test_method_2";
 
-// Method.NONE is converted to an empty string after a few JSON stringifications
-const METHOD_NONE = "";
+const METHOD_NONE = null;
 
 const REASON_TEST1 = "_test_reason_1";
 const REASON_TEST2 = "_test_reason_2";
 
 const SESSION_STARTED_TWICE = "_test_session_started_twice.1";
 const SESSION_STOPPED_TWICE = "_test_session_stopped_twice.1";
 
 function do_check_array_eq(a1, a2) {
new file mode 100644
--- /dev/null
+++ b/widget/android/Telemetry.h
@@ -0,0 +1,101 @@
+/* -*- Mode: c++; 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/. */
+
+#ifndef mozilla_widget_Telemetry_h__
+#define mozilla_widget_Telemetry_h__
+
+#include "GeneratedJNINatives.h"
+#include "nsAppShell.h"
+#include "nsIAndroidBridge.h"
+
+#include "mozilla/Telemetry.h"
+
+namespace mozilla {
+namespace widget {
+
+class Telemetry final
+    : public java::Telemetry::Natives<Telemetry>
+    , public UsesGeckoThreadProxy
+{
+    Telemetry() = delete;
+
+    static already_AddRefed<nsIUITelemetryObserver>
+    GetObserver()
+    {
+        nsAppShell* const appShell = nsAppShell::Get();
+        if (!appShell) {
+            return nullptr;
+        }
+
+        nsCOMPtr<nsIAndroidBrowserApp> browserApp = appShell->GetBrowserApp();
+        nsCOMPtr<nsIUITelemetryObserver> obs;
+
+        if (!browserApp || NS_FAILED(browserApp->GetUITelemetryObserver(
+                getter_AddRefs(obs))) || !obs) {
+            return nullptr;
+        }
+
+        return obs.forget();
+    }
+
+public:
+    static void
+    AddHistogram(jni::String::Param aName, int32_t aValue)
+    {
+        MOZ_ASSERT(aName);
+        mozilla::Telemetry::Accumulate(aName->ToCString().get(), aValue);
+    }
+
+    static void
+    AddKeyedHistogram(jni::String::Param aName, jni::String::Param aKey,
+                      int32_t aValue)
+    {
+        MOZ_ASSERT(aName && aKey);
+        mozilla::Telemetry::Accumulate(aName->ToCString().get(),
+                                       aKey->ToCString(), aValue);
+    }
+
+    static void
+    StartUISession(jni::String::Param aName, int64_t aTimestamp)
+    {
+        MOZ_ASSERT(aName);
+        nsCOMPtr<nsIUITelemetryObserver> obs = GetObserver();
+        if (obs) {
+            obs->StartSession(aName->ToString().get(), aTimestamp);
+        }
+    }
+
+    static void
+    StopUISession(jni::String::Param aName, jni::String::Param aReason,
+                  int64_t aTimestamp)
+    {
+        MOZ_ASSERT(aName);
+        nsCOMPtr<nsIUITelemetryObserver> obs = GetObserver();
+        if (obs) {
+            obs->StopSession(aName->ToString().get(),
+                             aReason ? aReason->ToString().get() : nullptr,
+                             aTimestamp);
+        }
+    }
+
+    static void
+    AddUIEvent(jni::String::Param aAction, jni::String::Param aMethod,
+               int64_t aTimestamp, jni::String::Param aExtras)
+    {
+        MOZ_ASSERT(aAction);
+        nsCOMPtr<nsIUITelemetryObserver> obs = GetObserver();
+        if (obs) {
+            obs->AddEvent(aAction->ToString().get(),
+                          aMethod ? aMethod->ToString().get() : nullptr,
+                          aTimestamp,
+                          aExtras ? aExtras->ToString().get() : nullptr);
+        }
+    }
+};
+
+} // namespace widget
+} // namespace mozilla
+
+#endif // mozilla_widget_Telemetry_h__
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -57,16 +57,17 @@
 #ifdef MOZ_LOGGING
 #include "mozilla/Logging.h"
 #endif
 
 #include "ANRReporter.h"
 #include "GeckoNetworkManager.h"
 #include "GeckoScreenOrientation.h"
 #include "PrefsHelper.h"
+#include "Telemetry.h"
 #include "ThumbnailHelper.h"
 
 #ifdef DEBUG_ANDROID_EVENTS
 #define EVLOG(args...)  ALOG(args)
 #else
 #define EVLOG(args...) do { } while (0)
 #endif
 
@@ -347,16 +348,17 @@ nsAppShell::nsAppShell()
         // Initialize JNI and Set the corresponding state in GeckoThread.
         AndroidBridge::ConstructBridge();
         GeckoAppShellSupport::Init();
         GeckoThreadSupport::Init();
         mozilla::ANRReporter::Init();
         mozilla::GeckoNetworkManager::Init();
         mozilla::GeckoScreenOrientation::Init();
         mozilla::PrefsHelper::Init();
+        mozilla::widget::Telemetry::Init();
         mozilla::ThumbnailHelper::Init();
         nsWindow::InitNatives();
 
         java::GeckoThread::SetState(java::GeckoThread::State::JNI_READY());
     }
 
     sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID);
 
--- a/widget/android/nsIAndroidBridge.idl
+++ b/widget/android/nsIAndroidBridge.idl
@@ -10,23 +10,23 @@ interface mozIDOMWindowProxy;
 interface nsIBrowserTab : nsISupports {
   readonly attribute mozIDOMWindowProxy window;
   readonly attribute float scale;
 };
 
 [scriptable, uuid(08426a73-e70b-4680-9282-630932e2b2bb)]
 interface nsIUITelemetryObserver : nsISupports {
   void startSession(in wstring name,
-                    in unsigned long timestamp);
+                    in long long timestamp);
   void stopSession(in wstring name,
                    in wstring reason,
-                   in unsigned long timestamp);
+                   in long long timestamp);
   void addEvent(in wstring action,
                 in wstring method,
-                in unsigned long timestamp,
+                in long long timestamp,
                 in wstring extras);
 };
 
 [scriptable, uuid(0370450f-2e9c-4d16-b333-8ca6ce31a5ff)]
 interface nsIAndroidBrowserApp : nsISupports {
   readonly attribute nsIBrowserTab selectedTab;
   nsIBrowserTab getBrowserTab(in int32_t tabId);
   nsIUITelemetryObserver getUITelemetryObserver();