Bug 1380854 - Track Sign up, Sign in and sync events. r?sdaswani draft
authorVlad Baicu <vlad.baicu@softvision.ro>
Wed, 16 May 2018 19:19:24 +0300
changeset 795806 917ea3bd6f788a707f5538a69f65371edff69634
parent 795657 3c9d69736f4a421218e5eb01b6571d535d38318a
push id110086
push uservbaicu@mozilla.com
push dateWed, 16 May 2018 16:33:30 +0000
reviewerssdaswani
bugs1380854
milestone62.0a1
Bug 1380854 - Track Sign up, Sign in and sync events. r?sdaswani Created a new event passed between FxAccountsWebChannel and AccountsHelper in order to differentiate between a sign in and an account update. MozReview-Commit-ID: JMTJC693k7s
mobile/android/base/java/org/mozilla/gecko/AccountsHelper.java
mobile/android/base/java/org/mozilla/gecko/mma/MmaDelegate.java
mobile/android/docs/mma.rst
mobile/android/modules/Accounts.jsm
mobile/android/modules/FxAccountsWebChannel.jsm
mobile/android/services/src/main/java/org/mozilla/gecko/fxa/sync/FxAccountSyncStatusHelper.java
--- a/mobile/android/base/java/org/mozilla/gecko/AccountsHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/AccountsHelper.java
@@ -17,16 +17,17 @@ import android.util.Log;
 
 import org.json.JSONException;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.fxa.login.Engaged;
 import org.mozilla.gecko.fxa.login.State;
+import org.mozilla.gecko.mma.MmaDelegate;
 import org.mozilla.gecko.restrictions.Restrictable;
 import org.mozilla.gecko.restrictions.Restrictions;
 import org.mozilla.gecko.sync.SyncConfiguration;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
@@ -48,28 +49,30 @@ public class AccountsHelper implements B
     protected final GeckoProfile mProfile;
 
     public AccountsHelper(Context context, GeckoProfile profile) {
         mContext = context;
         mProfile = profile;
 
         EventDispatcher.getInstance().registerGeckoThreadListener(this,
                 "Accounts:CreateFirefoxAccountFromJSON",
+                "Accounts:LoginFirefoxAccountFromJSON",
                 "Accounts:UpdateFirefoxAccountFromJSON",
                 "Accounts:DeleteFirefoxAccount",
                 "Accounts:Exist",
                 "Accounts:ProfileUpdated");
         EventDispatcher.getInstance().registerUiThreadListener(this,
                 "Accounts:Create",
                 "Accounts:ShowSyncPreferences");
     }
 
     public synchronized void uninit() {
         EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
                 "Accounts:CreateFirefoxAccountFromJSON",
+                "Accounts:LoginFirefoxAccountFromJSON",
                 "Accounts:UpdateFirefoxAccountFromJSON",
                 "Accounts:DeleteFirefoxAccount",
                 "Accounts:Exist",
                 "Accounts:ProfileUpdated");
         EventDispatcher.getInstance().unregisterUiThreadListener(this,
                 "Accounts:Create",
                 "Accounts:ShowSyncPreferences");
     }
@@ -140,69 +143,29 @@ public class AccountsHelper implements B
                      UnsupportedEncodingException e) {
                 Log.w(LOGTAG, "Got exception creating Firefox Account from JSON; ignoring.", e);
                 if (callback != null) {
                     callback.sendError("Could not create Firefox Account from JSON: " +
                                        e.toString());
                     return;
                 }
             }
+
+            MmaDelegate.track(MmaDelegate.USER_SIGNED_UP_TO_FXA);
+
             if (callback != null) {
                 callback.sendSuccess(fxAccount != null);
             }
 
         } else if ("Accounts:UpdateFirefoxAccountFromJSON".equals(event)) {
-            final Account account = FirefoxAccounts.getFirefoxAccount(mContext);
-            if (account == null) {
-                if (callback != null) {
-                    callback.sendError("Could not update Firefox Account since none exists");
-                }
-                return;
-            }
-
-            final GeckoBundle json = message.getBundle("json");
-            final String email = json.getString("email");
-            final String uid = json.getString("uid");
-
-            // Protect against cross-connecting accounts.
-            if (account.name == null || !account.name.equals(email)) {
-                final String errorMessage = "Cannot update Firefox Account from JSON: " +
-                        "datum has different email address!";
-                Log.e(LOGTAG, errorMessage);
-                if (callback != null) {
-                    callback.sendError(errorMessage);
-                }
-                return;
+            updateFirefoxAccount(message, callback);
+        } else if ( "Acounts:LoginFirefoxAccountFromJSON".equals(event)) {
+            if (updateFirefoxAccount(message, callback)) {
+                MmaDelegate.track(MmaDelegate.USER_SIGNED_IN_TO_FXA);
             }
-
-            final boolean verified = json.getBoolean("verified", false);
-            final byte[] unwrapkB = Utils.hex2Byte(json.getString("unwrapBKey", ""));
-            final byte[] sessionToken = Utils.hex2Byte(json.getString("sessionToken", ""));
-            final byte[] keyFetchToken = Utils.hex2Byte(json.getString("keyFetchToken", ""));
-
-            if (unwrapkB.length == 0 || sessionToken.length == 0 || keyFetchToken.length == 0) {
-                final String error = "Cannot update Firefox Account from JSON: invalid key/tokens";
-                Log.e(LOGTAG, error);
-                if (callback != null) {
-                    callback.sendError(error);
-                }
-                return;
-            }
-
-            final State state = new Engaged(email, uid, verified, unwrapkB,
-                                            sessionToken, keyFetchToken);
-
-            final AndroidFxAccount fxAccount = new AndroidFxAccount(mContext, account);
-            fxAccount.setState(state);
-            fxAccount.updateFirstRunScope(mContext);
-
-            if (callback != null) {
-                callback.sendSuccess(true);
-            }
-
         } else if ("Accounts:Create".equals(event)) {
             // Do exactly the same thing as if you tapped 'Sync' in Settings.
             final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             final GeckoBundle extras = message.getBundle("extras");
             if (extras != null) {
                 try {
                     intent.putExtra("extras", extras.toJSONObject().toString());
@@ -312,9 +275,62 @@ public class AccountsHelper implements B
             }
             // We don't necessarily have an Activity context here, so we always start in a new task.
             final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_STATUS);
             intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             mContext.startActivity(intent);
         }
     }
+
+    private boolean updateFirefoxAccount(final GeckoBundle message,
+                                      final EventCallback callback) {
+        final Account account = FirefoxAccounts.getFirefoxAccount(mContext);
+        if (account == null) {
+            if (callback != null) {
+                callback.sendError("Could not update Firefox Account since none exists");
+            }
+            return false;
+        }
+
+        final GeckoBundle json = message.getBundle("json");
+        final String email = json.getString("email");
+        final String uid = json.getString("uid");
+
+        // Protect against cross-connecting accounts.
+        if (account.name == null || !account.name.equals(email)) {
+            final String errorMessage = "Cannot update Firefox Account from JSON: " +
+                    "datum has different email address!";
+            Log.e(LOGTAG, errorMessage);
+            if (callback != null) {
+                callback.sendError(errorMessage);
+            }
+            return true;
+        }
+
+        final boolean verified = json.getBoolean("verified", false);
+        final byte[] unwrapkB = Utils.hex2Byte(json.getString("unwrapBKey", ""));
+        final byte[] sessionToken = Utils.hex2Byte(json.getString("sessionToken", ""));
+        final byte[] keyFetchToken = Utils.hex2Byte(json.getString("keyFetchToken", ""));
+
+        if (unwrapkB.length == 0 || sessionToken.length == 0 || keyFetchToken.length == 0) {
+            final String error = "Cannot update Firefox Account from JSON: invalid key/tokens";
+            Log.e(LOGTAG, error);
+            if (callback != null) {
+                callback.sendError(error);
+            }
+            return true;
+        }
+
+        final State state = new Engaged(email, uid, verified, unwrapkB,
+                sessionToken, keyFetchToken);
+
+        final AndroidFxAccount fxAccount = new AndroidFxAccount(mContext, account);
+        fxAccount.setState(state);
+        fxAccount.updateFirstRunScope(mContext);
+
+        if (callback != null) {
+            callback.sendSuccess(true);
+        }
+
+        return true;
+    }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/mma/MmaDelegate.java
+++ b/mobile/android/base/java/org/mozilla/gecko/mma/MmaDelegate.java
@@ -43,16 +43,20 @@ public class MmaDelegate {
     public static final String OPENED_BOOKMARK = "E_Opened_Bookmark";
     public static final String INTERACT_WITH_SEARCH_URL_AREA = "E_Interact_With_Search_URL_Area";
     public static final String SCREENSHOT = "E_Screenshot";
     public static final String SAVED_LOGIN_AND_PASSWORD = "E_Saved_Login_And_Password";
     public static final String RESUMED_FROM_BACKGROUND = "E_Resumed_From_Background";
     public static final String NEW_TAB = "E_Opened_New_Tab";
     public static final String DISMISS_ONBOARDING = "E_Dismiss_Onboarding";
 
+    public static final String USER_SIGNED_UP_TO_FXA = "E_User_Signed_Up_To_FxA";
+    public static final String USER_SIGNED_IN_TO_FXA = "E_User_Signed_In_To_FxA";
+    public static final String USER_FINISHED_SYNC = "E_User_Finished_Sync";
+
     private static final String LAUNCH_BUT_NOT_DEFAULT_BROWSER = "E_Launch_But_Not_Default_Browser";
     private static final String LAUNCH_BROWSER = "E_Launch_Browser";
     private static final String CHANGED_DEFAULT_TO_FENNEC = "E_Changed_Default_To_Fennec";
     private static final String INSTALLED_FOCUS = "E_Just_Installed_Focus";
     private static final String INSTALLED_KLAR = "E_Just_Installed_Klar";
 
     private static final String USER_ATT_FOCUS_INSTALLED = "Focus Installed";
     private static final String USER_ATT_KLAR_INSTALLED = "Klar Installed";
--- a/mobile/android/docs/mma.rst
+++ b/mobile/android/docs/mma.rst
@@ -151,16 +151,28 @@ List of current Events related data that
 * General app start event
 {
   "event" : "E_Launch_Browser"
 }
 * The user just dismissed on-boarding
 {
   "event" : "E_Dismiss_Onboarding"
 }
+* Sign Up for Firefox Account
+{
+  "event" : "E_User_Signed_Up_To_FxA"
+}
+* Sign in Firefox Account
+{
+  "event" : "E_User_Signed_In_To_FxA"
+}
+* Firefox Sync finished event
+{
+  "event" : "E_User_Finished_Sync"
+}
 * The user just resumed the app from background
 {
   "event" : "E_Resumed_From_Background"
 }
 * User set Fennec as default browser and resumed the app
 {
   "event" : "E_Changed_Default_To_Fennec"
 }
--- a/mobile/android/modules/Accounts.jsm
+++ b/mobile/android/modules/Accounts.jsm
@@ -103,16 +103,33 @@ var Accounts = Object.freeze({
   updateFirefoxAccountFromJSON: function(json) {
     return EventDispatcher.instance.sendRequestForResult({
       type: "Accounts:UpdateFirefoxAccountFromJSON",
       json: this._addDefaultEndpoints(json)
     });
   },
 
   /**
+   * Sign in an existing Android Account to the "Engaged" state with the given
+   * fxa-content-server "login" JSON datum.  The account will (re)start
+   * syncing immediately, unless the user has manually configured the account
+   * to not Sync.
+   *
+   * It is an error if no Android Account exists.
+   *
+   * Returns a Promise that resolves to a boolean indicating success.
+   */
+  loginFirefoxAccountFromJSON: function(json) {
+    return EventDispatcher.instance.sendRequestForResult({
+      type: "Accounts:LoginFirefoxAccountFromJSON",
+      json: this._addDefaultEndpoints(json)
+    });
+  },
+
+  /**
    * Notify that profile for Android Account has updated.
    * The account will re-fetch the profile image.
    *
    * It is an error if no Android Account exists.
    *
    * There is no return value from this method.
    */
   notifyFirefoxAccountProfileChanged: function() {
--- a/mobile/android/modules/FxAccountsWebChannel.jsm
+++ b/mobile/android/modules/FxAccountsWebChannel.jsm
@@ -267,17 +267,17 @@ this.FxAccountsWebChannel.prototype = {
                 return Accounts.createFirefoxAccountFromJSON(data).then(success => {
                   if (!success) {
                     throw new Error("Could not create Firefox Account!");
                   }
                   UITelemetry.addEvent("action.1", "content", null, "fxaccount-create");
                   return success;
                 });
               }
-                return Accounts.updateFirefoxAccountFromJSON(data).then(success => {
+                return Accounts.loginFirefoxAccountFromJSON(data).then(success => {
                   if (!success) {
                     throw new Error("Could not update Firefox Account!");
                   }
                   UITelemetry.addEvent("action.1", "content", null, "fxaccount-login");
                   return success;
                 });
 
             })
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/sync/FxAccountSyncStatusHelper.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/sync/FxAccountSyncStatusHelper.java
@@ -5,16 +5,17 @@
 package org.mozilla.gecko.fxa.sync;
 
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.WeakHashMap;
 
 import org.mozilla.gecko.fxa.SyncStatusListener;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
+import org.mozilla.gecko.mma.MmaDelegate;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.ContentResolver;
 import android.content.SyncStatusObserver;
 
 /**
  * Abstract away some details of Android's SyncStatusObserver.
  * <p>
@@ -62,16 +63,17 @@ public class FxAccountSyncStatusHelper i
       }
 
       if (!active && wasActiveLastTime) {
         // We've finished a sync.
         ThreadUtils.postToUiThread(new Runnable() {
           @Override
           public void run() {
             delegate.onSyncFinished();
+            MmaDelegate.track(MmaDelegate.USER_FINISHED_SYNC);
           }
         });
       }
     }
   }
 
   protected void addListener() {
     final int mask = ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE;