mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Merge f-t to m-c, a=merge
This commit is contained in:
commit
b7929d83b1
@ -924,13 +924,16 @@
|
||||
|
||||
var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
|
||||
|
||||
// Check for unmodified left-click, and use default behavior
|
||||
var searchBar = BrowserSearch.searchBar;
|
||||
searchBar.telemetrySearchDetails = {
|
||||
index: controller.selection.currentIndex,
|
||||
kind: "mouse"
|
||||
};
|
||||
var popupForSearchBar = searchBar && searchBar.textbox == this.mInput;
|
||||
if (popupForSearchBar) {
|
||||
searchBar.telemetrySearchDetails = {
|
||||
index: controller.selection.currentIndex,
|
||||
kind: "mouse"
|
||||
};
|
||||
}
|
||||
|
||||
// Check for unmodified left-click, and use default behavior
|
||||
if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey &&
|
||||
!aEvent.altKey && !aEvent.metaKey) {
|
||||
controller.handleEnter(true);
|
||||
@ -938,7 +941,7 @@
|
||||
}
|
||||
|
||||
// Check for middle-click or modified clicks on the search bar
|
||||
if (searchBar && searchBar.textbox == this.mInput) {
|
||||
if (popupForSearchBar) {
|
||||
// Handle search bar popup clicks
|
||||
var search = controller.getValueAt(this.selectedIndex);
|
||||
|
||||
|
@ -187,7 +187,7 @@ public class BrowserApp extends GeckoApp
|
||||
private ViewGroup mHomePagerContainer;
|
||||
protected Telemetry.Timer mAboutHomeStartupTimer;
|
||||
private ActionModeCompat mActionMode;
|
||||
private boolean mShowActionModeEndAnimation;
|
||||
private boolean mHideDynamicToolbarOnActionModeEnd;
|
||||
private TabHistoryController tabHistoryController;
|
||||
|
||||
private static final int GECKO_TOOLS_MENU = -1;
|
||||
@ -294,6 +294,8 @@ public class BrowserApp extends GeckoApp
|
||||
if (Tabs.getInstance().isSelectedTab(tab)) {
|
||||
updateHomePagerForTab(tab);
|
||||
}
|
||||
|
||||
mHideDynamicToolbarOnActionModeEnd = false;
|
||||
break;
|
||||
case START:
|
||||
if (Tabs.getInstance().isSelectedTab(tab)) {
|
||||
@ -3322,10 +3324,11 @@ public class BrowserApp extends GeckoApp
|
||||
if (mDynamicToolbar.isEnabled() && !margins.areMarginsShown()) {
|
||||
margins.setMaxMargins(0, mBrowserChrome.getHeight(), 0, 0);
|
||||
mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
|
||||
mShowActionModeEndAnimation = true;
|
||||
mHideDynamicToolbarOnActionModeEnd = true;
|
||||
} else {
|
||||
// Otherwise, we animate the actionbar itself
|
||||
mActionBar.animateIn();
|
||||
mHideDynamicToolbarOnActionModeEnd = false;
|
||||
}
|
||||
|
||||
mDynamicToolbar.setPinned(true, PinReason.ACTION_MODE);
|
||||
@ -3355,9 +3358,8 @@ public class BrowserApp extends GeckoApp
|
||||
|
||||
// Only slide the urlbar out if it was hidden when the action mode started
|
||||
// Don't animate hiding it so that there's no flash as we switch back to url mode
|
||||
if (mShowActionModeEndAnimation) {
|
||||
if (mHideDynamicToolbarOnActionModeEnd) {
|
||||
mDynamicToolbar.setVisible(false, VisibilityTransition.IMMEDIATE);
|
||||
mShowActionModeEndAnimation = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -780,6 +780,7 @@ sync_java_files = [
|
||||
'background/common/log/writers/StringLogWriter.java',
|
||||
'background/common/log/writers/TagLogWriter.java',
|
||||
'background/common/log/writers/ThreadLocalTagLogWriter.java',
|
||||
'background/common/telemetry/TelemetryWrapper.java',
|
||||
'background/datareporting/TelemetryRecorder.java',
|
||||
'background/db/CursorDumper.java',
|
||||
'background/db/Tab.java',
|
||||
@ -1132,6 +1133,7 @@ sync_java_files = [
|
||||
'sync/synchronizer/UnbundleError.java',
|
||||
'sync/synchronizer/UnexpectedSessionException.java',
|
||||
'sync/SynchronizerConfiguration.java',
|
||||
'sync/telemetry/TelemetryContract.java',
|
||||
'sync/ThreadPool.java',
|
||||
'sync/UnexpectedJSONException.java',
|
||||
'sync/UnknownSynchronizerConfigurationVersionException.java',
|
||||
|
@ -0,0 +1,56 @@
|
||||
/* 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.background.common.telemetry;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
|
||||
/**
|
||||
* Android Background Services are normally built into Fennec, but can also be
|
||||
* built as a stand-alone APK for rapid local development. The current Telemetry
|
||||
* implementation is coupled to Gecko, and Background Services should not
|
||||
* interact with Gecko directly. To maintain this independence, Background
|
||||
* Services lazily introspects the relevant Telemetry class from the enclosing
|
||||
* package, warning but otherwise ignoring failures during introspection or
|
||||
* invocation.
|
||||
* <p>
|
||||
* It is possible that Background Services will introspect and invoke the
|
||||
* Telemetry implementation while Gecko is not running. In this case, the Fennec
|
||||
* process itself buffers Telemetry events until such time as they can be
|
||||
* flushed to disk and uploaded. <b>There is no guarantee that all Telemetry
|
||||
* events will be uploaded!</b> Depending on the volume of data and the
|
||||
* application lifecycle, Telemetry events may be dropped.
|
||||
*/
|
||||
public class TelemetryWrapper {
|
||||
private static final String LOG_TAG = TelemetryWrapper.class.getSimpleName();
|
||||
|
||||
// Marking this volatile maintains thread safety cheaply.
|
||||
private static volatile Method mAddToHistogram;
|
||||
|
||||
public static void addToHistogram(String key, int value) {
|
||||
if (mAddToHistogram == null) {
|
||||
try {
|
||||
final Class<?> telemetry = Class.forName("org.mozilla.gecko.Telemetry");
|
||||
mAddToHistogram = telemetry.getMethod("addToHistogram", String.class, int.class);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Logger.warn(LOG_TAG, "org.mozilla.gecko.Telemetry class found!");
|
||||
return;
|
||||
} catch (NoSuchMethodException e) {
|
||||
Logger.warn(LOG_TAG, "org.mozilla.gecko.Telemetry.addToHistogram(String, int) method not found!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mAddToHistogram != null) {
|
||||
try {
|
||||
mAddToHistogram.invoke(null, key, value);
|
||||
} catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
|
||||
Logger.warn(LOG_TAG, "Got exception invoking telemetry!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@
|
||||
|
||||
package org.mozilla.gecko.background.fxa;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient10.StatusResponse;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient10.TwoKeys;
|
||||
@ -11,8 +13,8 @@ import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
|
||||
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||
|
||||
public interface FxAccountClient {
|
||||
public void createAccountAndGetKeys(final byte[] emailUTF8, final PasswordStretcher passwordStretcher, final RequestDelegate<LoginResponse> delegate);
|
||||
public void loginAndGetKeys(final byte[] emailUTF8, final PasswordStretcher passwordStretcher, final RequestDelegate<LoginResponse> requestDelegate);
|
||||
public void createAccountAndGetKeys(final byte[] emailUTF8, final PasswordStretcher passwordStretcher, final Map<String, String> queryParameters, final RequestDelegate<LoginResponse> delegate);
|
||||
public void loginAndGetKeys(final byte[] emailUTF8, final PasswordStretcher passwordStretcher, final Map<String, String> queryParameters, final RequestDelegate<LoginResponse> requestDelegate);
|
||||
public void status(byte[] sessionToken, RequestDelegate<StatusResponse> requestDelegate);
|
||||
public void keys(byte[] keyFetchToken, RequestDelegate<TwoKeys> requestDelegate);
|
||||
public void sign(byte[] sessionToken, ExtendedJSONObject publicKey, long certificateDurationInMilliseconds, RequestDelegate<String> requestDelegate);
|
||||
|
@ -8,11 +8,14 @@ import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
@ -71,7 +74,6 @@ public class FxAccountClient10 {
|
||||
public static final String JSON_KEY_CODE = "code";
|
||||
public static final String JSON_KEY_ERRNO = "errno";
|
||||
|
||||
|
||||
protected static final String[] requiredErrorStringFields = { JSON_KEY_ERROR, JSON_KEY_MESSAGE, JSON_KEY_INFO };
|
||||
protected static final String[] requiredErrorLongFields = { JSON_KEY_CODE, JSON_KEY_ERRNO };
|
||||
|
||||
@ -99,6 +101,48 @@ public class FxAccountClient10 {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
protected BaseResource getBaseResource(String path, Map<String, String> queryParameters) throws UnsupportedEncodingException, URISyntaxException {
|
||||
if (queryParameters == null || queryParameters.isEmpty()) {
|
||||
return getBaseResource(path);
|
||||
}
|
||||
final String[] array = new String[2 * queryParameters.size()];
|
||||
int i = 0;
|
||||
for (Entry<String, String> entry : queryParameters.entrySet()) {
|
||||
array[i++] = entry.getKey();
|
||||
array[i++] = entry.getValue();
|
||||
}
|
||||
return getBaseResource(path, array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create <code>BaseResource</code>, encoding query parameters carefully.
|
||||
* <p>
|
||||
* This is equivalent to <code>android.net.Uri.Builder</code>, which is not
|
||||
* present in our JUnit 4 tests.
|
||||
*
|
||||
* @param path fragment.
|
||||
* @param queryParameters list of key/value query parameter pairs. Must be even length!
|
||||
* @return <code>BaseResource<instance>
|
||||
* @throws URISyntaxException
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
protected BaseResource getBaseResource(String path, String... queryParameters) throws URISyntaxException, UnsupportedEncodingException {
|
||||
final StringBuilder sb = new StringBuilder(serverURI);
|
||||
sb.append(path);
|
||||
if (queryParameters != null) {
|
||||
int i = 0;
|
||||
while (i < queryParameters.length) {
|
||||
sb.append(i > 0 ? "&" : "?");
|
||||
final String key = queryParameters[i++];
|
||||
final String val = queryParameters[i++];
|
||||
sb.append(URLEncoder.encode(key, "UTF-8"));
|
||||
sb.append("=");
|
||||
sb.append(URLEncoder.encode(val, "UTF-8"));
|
||||
}
|
||||
}
|
||||
return new BaseResource(new URI(sb.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a typed value extracted from a successful response (in an
|
||||
* endpoint-dependent way).
|
||||
@ -353,8 +397,8 @@ public class FxAccountClient10 {
|
||||
|
||||
BaseResource resource;
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + "account/create"));
|
||||
} catch (URISyntaxException e) {
|
||||
resource = getBaseResource("account/create");
|
||||
} catch (URISyntaxException | UnsupportedEncodingException e) {
|
||||
invokeHandleError(delegate, e);
|
||||
return;
|
||||
}
|
||||
@ -384,8 +428,8 @@ public class FxAccountClient10 {
|
||||
|
||||
BaseResource resource;
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + "auth/start"));
|
||||
} catch (URISyntaxException e) {
|
||||
resource = getBaseResource("auth/start");
|
||||
} catch (URISyntaxException | UnsupportedEncodingException e) {
|
||||
invokeHandleError(delegate, e);
|
||||
return;
|
||||
}
|
||||
@ -416,8 +460,8 @@ public class FxAccountClient10 {
|
||||
|
||||
BaseResource resource;
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + "auth/finish"));
|
||||
} catch (URISyntaxException e) {
|
||||
resource = getBaseResource("auth/finish");
|
||||
} catch (URISyntaxException | UnsupportedEncodingException e) {
|
||||
invokeHandleError(delegate, e);
|
||||
return;
|
||||
}
|
||||
@ -480,8 +524,8 @@ public class FxAccountClient10 {
|
||||
|
||||
BaseResource resource;
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + "session/create"));
|
||||
} catch (URISyntaxException e) {
|
||||
resource = getBaseResource("session/create");
|
||||
} catch (URISyntaxException | UnsupportedEncodingException e) {
|
||||
invokeHandleError(delegate, e);
|
||||
return;
|
||||
}
|
||||
@ -516,8 +560,8 @@ public class FxAccountClient10 {
|
||||
|
||||
BaseResource resource;
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + "session/destroy"));
|
||||
} catch (URISyntaxException e) {
|
||||
resource = getBaseResource("session/destroy");
|
||||
} catch (URISyntaxException | UnsupportedEncodingException e) {
|
||||
invokeHandleError(delegate, e);
|
||||
return;
|
||||
}
|
||||
@ -605,8 +649,8 @@ public class FxAccountClient10 {
|
||||
|
||||
BaseResource resource;
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + "account/keys"));
|
||||
} catch (URISyntaxException e) {
|
||||
resource = getBaseResource("account/keys");
|
||||
} catch (URISyntaxException | UnsupportedEncodingException e) {
|
||||
invokeHandleError(delegate, e);
|
||||
return;
|
||||
}
|
||||
@ -667,8 +711,8 @@ public class FxAccountClient10 {
|
||||
|
||||
BaseResource resource;
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + "recovery_email/status"));
|
||||
} catch (URISyntaxException e) {
|
||||
resource = getBaseResource("recovery_email/status");
|
||||
} catch (URISyntaxException | UnsupportedEncodingException e) {
|
||||
invokeHandleError(delegate, e);
|
||||
return;
|
||||
}
|
||||
@ -709,8 +753,8 @@ public class FxAccountClient10 {
|
||||
|
||||
BaseResource resource;
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + "certificate/sign"));
|
||||
} catch (URISyntaxException e) {
|
||||
resource = getBaseResource("certificate/sign");
|
||||
} catch (URISyntaxException | UnsupportedEncodingException e) {
|
||||
invokeHandleError(delegate, e);
|
||||
return;
|
||||
}
|
||||
@ -750,8 +794,8 @@ public class FxAccountClient10 {
|
||||
|
||||
BaseResource resource;
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + "recovery_email/resend_code"));
|
||||
} catch (URISyntaxException e) {
|
||||
resource = getBaseResource("recovery_email/resend_code");
|
||||
} catch (URISyntaxException | UnsupportedEncodingException e) {
|
||||
invokeHandleError(delegate, e);
|
||||
return;
|
||||
}
|
||||
@ -788,7 +832,7 @@ public class FxAccountClient10 {
|
||||
final BaseResource resource;
|
||||
final JSONObject body = new JSONObject();
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + "account/unlock/resend_code"));
|
||||
resource = getBaseResource("account/unlock/resend_code");
|
||||
body.put("email", new String(emailUTF8, "UTF-8"));
|
||||
} catch (URISyntaxException e) {
|
||||
invokeHandleError(delegate, e);
|
||||
|
@ -4,10 +4,8 @@
|
||||
|
||||
package org.mozilla.gecko.background.fxa;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
@ -53,12 +51,20 @@ public class FxAccountClient20 extends FxAccountClient10 implements FxAccountCli
|
||||
|
||||
// Public for testing only; prefer login and loginAndGetKeys (without boolean parameter).
|
||||
public void login(final byte[] emailUTF8, final byte[] quickStretchedPW, final boolean getKeys,
|
||||
final Map<String, String> queryParameters,
|
||||
final RequestDelegate<LoginResponse> delegate) {
|
||||
BaseResource resource;
|
||||
JSONObject body;
|
||||
final String path = getKeys ? "account/login?keys=true" : "account/login";
|
||||
final BaseResource resource;
|
||||
final JSONObject body;
|
||||
try {
|
||||
resource = new BaseResource(new URI(serverURI + path));
|
||||
final String path = "account/login";
|
||||
final Map<String, String> modifiedParameters = new HashMap<>();
|
||||
if (queryParameters != null) {
|
||||
modifiedParameters.putAll(queryParameters);
|
||||
}
|
||||
if (getKeys) {
|
||||
modifiedParameters.put("keys", "true");
|
||||
}
|
||||
resource = getBaseResource(path, modifiedParameters);
|
||||
body = new FxAccount20LoginDelegate(emailUTF8, quickStretchedPW).getCreateBody();
|
||||
} catch (Exception e) {
|
||||
invokeHandleError(delegate, e);
|
||||
@ -96,35 +102,23 @@ public class FxAccountClient20 extends FxAccountClient10 implements FxAccountCli
|
||||
post(resource, body, delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create account/create URI, encoding query parameters carefully.
|
||||
* <p>
|
||||
* This is equivalent to <code>android.net.Uri.Builder</code>, which is not
|
||||
* present in our JUnit 4 tests.
|
||||
*/
|
||||
protected URI getCreateAccountURI(final boolean getKeys, final String service) throws UnsupportedEncodingException, URISyntaxException {
|
||||
if (service == null) {
|
||||
throw new IllegalArgumentException("service must not be null");
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder(serverURI); // serverURI always has a trailing slash.
|
||||
sb.append("account/create?service=");
|
||||
// Be very careful that query parameters are encoded correctly!
|
||||
sb.append(URLEncoder.encode(service, "UTF-8"));
|
||||
if (getKeys) {
|
||||
sb.append("&keys=true");
|
||||
}
|
||||
return new URI(sb.toString());
|
||||
}
|
||||
|
||||
public void createAccount(final byte[] emailUTF8, final byte[] quickStretchedPW,
|
||||
final boolean getKeys,
|
||||
final boolean preVerified,
|
||||
final String service,
|
||||
final Map<String, String> queryParameters,
|
||||
final RequestDelegate<LoginResponse> delegate) {
|
||||
final BaseResource resource;
|
||||
final JSONObject body;
|
||||
try {
|
||||
resource = new BaseResource(getCreateAccountURI(getKeys, service));
|
||||
final String path = "account/create";
|
||||
final Map<String, String> modifiedParameters = new HashMap<>();
|
||||
if (queryParameters != null) {
|
||||
modifiedParameters.putAll(queryParameters);
|
||||
}
|
||||
if (getKeys) {
|
||||
modifiedParameters.put("keys", "true");
|
||||
}
|
||||
resource = getBaseResource(path, modifiedParameters);
|
||||
body = new FxAccount20CreateDelegate(emailUTF8, quickStretchedPW, preVerified).getCreateBody();
|
||||
} catch (Exception e) {
|
||||
invokeHandleError(delegate, e);
|
||||
@ -163,18 +157,18 @@ public class FxAccountClient20 extends FxAccountClient10 implements FxAccountCli
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAccountAndGetKeys(byte[] emailUTF8, PasswordStretcher passwordStretcher, RequestDelegate<LoginResponse> delegate) {
|
||||
public void createAccountAndGetKeys(byte[] emailUTF8, PasswordStretcher passwordStretcher, final Map<String, String> queryParameters, RequestDelegate<LoginResponse> delegate) {
|
||||
try {
|
||||
byte[] quickStretchedPW = passwordStretcher.getQuickStretchedPW(emailUTF8);
|
||||
createAccount(emailUTF8, quickStretchedPW, true, false, "sync", delegate);
|
||||
createAccount(emailUTF8, quickStretchedPW, true, false, queryParameters, delegate);
|
||||
} catch (Exception e) {
|
||||
invokeHandleError(delegate, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loginAndGetKeys(byte[] emailUTF8, PasswordStretcher passwordStretcher, RequestDelegate<LoginResponse> delegate) {
|
||||
login(emailUTF8, passwordStretcher, true, delegate);
|
||||
public void loginAndGetKeys(byte[] emailUTF8, PasswordStretcher passwordStretcher, final Map<String, String> queryParameters, RequestDelegate<LoginResponse> delegate) {
|
||||
login(emailUTF8, passwordStretcher, true, queryParameters, delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,10 +192,12 @@ public class FxAccountClient20 extends FxAccountClient10 implements FxAccountCli
|
||||
* @param getKeys
|
||||
* true if a <code>keyFetchToken</code> should be returned (in
|
||||
* addition to the standard <code>sessionToken</code>).
|
||||
* @param queryParameters
|
||||
* @param delegate
|
||||
* to invoke callbacks.
|
||||
*/
|
||||
public void login(final byte[] emailUTF8, final PasswordStretcher stretcher, final boolean getKeys,
|
||||
final Map<String, String> queryParameters,
|
||||
final RequestDelegate<LoginResponse> delegate) {
|
||||
byte[] quickStretchedPW;
|
||||
try {
|
||||
@ -212,7 +208,7 @@ public class FxAccountClient20 extends FxAccountClient10 implements FxAccountCli
|
||||
return;
|
||||
}
|
||||
|
||||
this.login(emailUTF8, quickStretchedPW, getKeys, new RequestDelegate<LoginResponse>() {
|
||||
this.login(emailUTF8, quickStretchedPW, getKeys, queryParameters, new RequestDelegate<LoginResponse>() {
|
||||
@Override
|
||||
public void handleSuccess(LoginResponse result) {
|
||||
delegate.handleSuccess(result);
|
||||
@ -239,7 +235,7 @@ public class FxAccountClient20 extends FxAccountClient10 implements FxAccountCli
|
||||
// signature here, which invokes a non-retrying version.
|
||||
byte[] alternateEmailUTF8 = alternateEmail.getBytes("UTF-8");
|
||||
byte[] alternateQuickStretchedPW = stretcher.getQuickStretchedPW(alternateEmailUTF8);
|
||||
login(alternateEmailUTF8, alternateQuickStretchedPW, getKeys, delegate);
|
||||
login(alternateEmailUTF8, alternateQuickStretchedPW, getKeys, queryParameters, delegate);
|
||||
} catch (Exception innerException) {
|
||||
delegate.handleError(innerException);
|
||||
return;
|
||||
|
@ -7,6 +7,7 @@ package org.mozilla.gecko.fxa.activities;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -565,4 +566,10 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
||||
ensureFindViewById(null, R.id.account_server_layout, "account server layout").setVisibility(visibility);
|
||||
ensureFindViewById(null, R.id.sync_server_layout, "sync server layout").setVisibility(visibility);
|
||||
}
|
||||
|
||||
protected Map<String, String> getQueryParameters() {
|
||||
final Map<String, String> queryParameters = new HashMap<>();
|
||||
queryParameters.put("service", "sync");
|
||||
return queryParameters;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import java.util.concurrent.Executors;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.common.telemetry.TelemetryWrapper;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient20;
|
||||
@ -22,6 +23,7 @@ import org.mozilla.gecko.fxa.login.Engaged;
|
||||
import org.mozilla.gecko.fxa.login.State;
|
||||
import org.mozilla.gecko.fxa.tasks.FxAccountSignInTask;
|
||||
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
|
||||
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
@ -150,6 +152,8 @@ public abstract class FxAccountAbstractUpdateCredentialsActivity extends FxAccou
|
||||
startActivity(successIntent);
|
||||
}
|
||||
finish();
|
||||
|
||||
TelemetryWrapper.addToHistogram(TelemetryContract.SYNC11_MIGRATIONS_COMPLETED, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,7 +165,7 @@ public abstract class FxAccountAbstractUpdateCredentialsActivity extends FxAccou
|
||||
try {
|
||||
hideRemoteError();
|
||||
RequestDelegate<LoginResponse> delegate = new UpdateCredentialsDelegate(email, passwordStretcher, serverURI);
|
||||
new FxAccountSignInTask(this, this, email, passwordStretcher, client, delegate).execute();
|
||||
new FxAccountSignInTask(this, this, email, passwordStretcher, client, getQueryParameters(), delegate).execute();
|
||||
} catch (Exception e) {
|
||||
Logger.warn(LOG_TAG, "Got exception updating credentials for account.", e);
|
||||
showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error);
|
||||
|
@ -221,7 +221,7 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
|
||||
FxAccountClient client = new FxAccountClient20(serverURI, executor);
|
||||
try {
|
||||
hideRemoteError();
|
||||
new FxAccountCreateAccountTask(this, this, email, passwordStretcher, client, delegate).execute();
|
||||
new FxAccountCreateAccountTask(this, this, email, passwordStretcher, client, getQueryParameters(), delegate).execute();
|
||||
} catch (Exception e) {
|
||||
showRemoteError(e, R.string.fxaccount_create_account_unknown_error);
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
|
||||
@ -51,4 +53,11 @@ public class FxAccountFinishMigratingActivity extends FxAccountAbstractUpdateCre
|
||||
successIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||
return successIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> getQueryParameters() {
|
||||
final Map<String, String> queryParameters = super.getQueryParameters();
|
||||
queryParameters.put("migration", "sync11");
|
||||
return queryParameters;
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ public class FxAccountSignInActivity extends FxAccountAbstractSetupActivity {
|
||||
FxAccountClient client = new FxAccountClient20(serverURI, executor);
|
||||
try {
|
||||
hideRemoteError();
|
||||
new FxAccountSignInTask(this, this, email, passwordStretcher, client, delegate).execute();
|
||||
new FxAccountSignInTask(this, this, email, passwordStretcher, client, getQueryParameters(), delegate).execute();
|
||||
} catch (Exception e) {
|
||||
showRemoteError(e, R.string.fxaccount_sign_in_unknown_error);
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ package org.mozilla.gecko.fxa.receivers;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||
import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager;
|
||||
import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
|
||||
import org.mozilla.gecko.sync.config.AccountPickler;
|
||||
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
|
||||
|
||||
@ -63,6 +65,9 @@ public class FxAccountDeletedService extends IntentService {
|
||||
// Delete client database and non-local tabs.
|
||||
Logger.info(LOG_TAG, "Deleting the entire clients database and non-local tabs");
|
||||
FennecTabsRepository.deleteNonLocalClientsAndTabs(context);
|
||||
|
||||
// Remove any displayed notifications.
|
||||
new FxAccountNotificationManager(FxAccountSyncAdapter.NOTIFICATION_ID).clear(context);
|
||||
}
|
||||
|
||||
public static void deletePickle(final Context context) {
|
||||
|
@ -7,12 +7,14 @@ package org.mozilla.gecko.fxa.sync;
|
||||
import org.mozilla.gecko.BrowserLocaleManager;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.common.telemetry.TelemetryWrapper;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountUtils;
|
||||
import org.mozilla.gecko.fxa.activities.FxAccountFinishMigratingActivity;
|
||||
import org.mozilla.gecko.fxa.activities.FxAccountStatusActivity;
|
||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||
import org.mozilla.gecko.fxa.login.State;
|
||||
import org.mozilla.gecko.fxa.login.State.Action;
|
||||
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
@ -43,6 +45,17 @@ public class FxAccountNotificationManager {
|
||||
this.notificationId = notificationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all Firefox Account related notifications from the notification manager.
|
||||
*
|
||||
* @param context
|
||||
* Android context.
|
||||
*/
|
||||
public void clear(Context context) {
|
||||
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(notificationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflect new Firefox Account state to the notification manager: show or hide
|
||||
* notifications reflecting the state of a Firefox Account.
|
||||
@ -72,6 +85,8 @@ public class FxAccountNotificationManager {
|
||||
final String text;
|
||||
final Intent notificationIntent;
|
||||
if (action == Action.NeedsFinishMigrating) {
|
||||
TelemetryWrapper.addToHistogram(TelemetryContract.SYNC11_MIGRATION_NOTIFICATIONS_OFFERED, 1);
|
||||
|
||||
title = context.getResources().getString(R.string.fxaccount_sync_finish_migrating_notification_title);
|
||||
text = context.getResources().getString(R.string.fxaccount_sync_finish_migrating_notification_text, state.email);
|
||||
notificationIntent = new Intent(context, FxAccountFinishMigratingActivity.class);
|
||||
|
@ -67,7 +67,7 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
public static final String SYNC_EXTRAS_RESPECT_LOCAL_RATE_LIMIT = "respect_local_rate_limit";
|
||||
public static final String SYNC_EXTRAS_RESPECT_REMOTE_SERVER_BACKOFF = "respect_remote_server_backoff";
|
||||
|
||||
protected static final int NOTIFICATION_ID = LOG_TAG.hashCode();
|
||||
public static final int NOTIFICATION_ID = LOG_TAG.hashCode();
|
||||
|
||||
// Tracks the last seen storage hostname for backoff purposes.
|
||||
private static final String PREF_BACKOFF_STORAGE_HOST = "backoffStorageHost";
|
||||
|
@ -17,6 +17,7 @@ import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||
import org.mozilla.gecko.fxa.login.Engaged;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
@ -32,7 +33,7 @@ public class FxAccountCodeResender {
|
||||
protected final byte[] sessionToken;
|
||||
|
||||
public FxAccountResendCodeTask(Context context, byte[] sessionToken, FxAccountClient client, RequestDelegate<Void> delegate) {
|
||||
super(context, null, client, delegate);
|
||||
super(context, null, client, null, delegate);
|
||||
this.sessionToken = sessionToken;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
package org.mozilla.gecko.fxa.tasks;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient;
|
||||
@ -20,8 +21,8 @@ public class FxAccountCreateAccountTask extends FxAccountSetupTask<LoginResponse
|
||||
protected final byte[] emailUTF8;
|
||||
protected final PasswordStretcher passwordStretcher;
|
||||
|
||||
public FxAccountCreateAccountTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
|
||||
super(context, progressDisplay, client, delegate);
|
||||
public FxAccountCreateAccountTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, Map<String, String> queryParameters, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
|
||||
super(context, progressDisplay, client, queryParameters, delegate);
|
||||
this.emailUTF8 = email.getBytes("UTF-8");
|
||||
this.passwordStretcher = passwordStretcher;
|
||||
}
|
||||
@ -29,7 +30,7 @@ public class FxAccountCreateAccountTask extends FxAccountSetupTask<LoginResponse
|
||||
@Override
|
||||
protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
|
||||
try {
|
||||
client.createAccountAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
|
||||
client.createAccountAndGetKeys(emailUTF8, passwordStretcher, queryParameters, innerDelegate);
|
||||
latch.await();
|
||||
return innerDelegate;
|
||||
} catch (Exception e) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package org.mozilla.gecko.fxa.tasks;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
@ -43,13 +44,16 @@ public abstract class FxAccountSetupTask<T> extends AsyncTask<Void, Void, InnerR
|
||||
protected final CountDownLatch latch = new CountDownLatch(1);
|
||||
protected final InnerRequestDelegate<T> innerDelegate = new InnerRequestDelegate<T>(latch);
|
||||
|
||||
protected final Map<String, String> queryParameters;
|
||||
|
||||
protected final RequestDelegate<T> delegate;
|
||||
|
||||
public FxAccountSetupTask(Context context, ProgressDisplay progressDisplay, FxAccountClient client, RequestDelegate<T> delegate) {
|
||||
public FxAccountSetupTask(Context context, ProgressDisplay progressDisplay, FxAccountClient client, Map<String, String> queryParameters, RequestDelegate<T> delegate) {
|
||||
this.context = context;
|
||||
this.client = client;
|
||||
this.delegate = delegate;
|
||||
this.progressDisplay = progressDisplay;
|
||||
this.queryParameters = queryParameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -5,6 +5,7 @@
|
||||
package org.mozilla.gecko.fxa.tasks;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient;
|
||||
@ -20,8 +21,8 @@ public class FxAccountSignInTask extends FxAccountSetupTask<LoginResponse> {
|
||||
protected final byte[] emailUTF8;
|
||||
protected final PasswordStretcher passwordStretcher;
|
||||
|
||||
public FxAccountSignInTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
|
||||
super(context, progressDisplay, client, delegate);
|
||||
public FxAccountSignInTask(Context context, ProgressDisplay progressDisplay, String email, PasswordStretcher passwordStretcher, FxAccountClient client, Map<String, String> queryParameters, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException {
|
||||
super(context, progressDisplay, client, queryParameters, delegate);
|
||||
this.emailUTF8 = email.getBytes("UTF-8");
|
||||
this.passwordStretcher = passwordStretcher;
|
||||
}
|
||||
@ -29,7 +30,7 @@ public class FxAccountSignInTask extends FxAccountSetupTask<LoginResponse> {
|
||||
@Override
|
||||
protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
|
||||
try {
|
||||
client.loginAndGetKeys(emailUTF8, passwordStretcher, innerDelegate);
|
||||
client.loginAndGetKeys(emailUTF8, passwordStretcher, queryParameters, innerDelegate);
|
||||
latch.await();
|
||||
return innerDelegate;
|
||||
} catch (Exception e) {
|
||||
|
@ -31,7 +31,7 @@ public class FxAccountUnlockCodeResender {
|
||||
protected final byte[] emailUTF8;
|
||||
|
||||
public FxAccountUnlockCodeTask(Context context, byte[] emailUTF8, FxAccountClient client, RequestDelegate<Void> delegate) {
|
||||
super(context, null, client, delegate);
|
||||
super(context, null, client, null, delegate);
|
||||
this.emailUTF8 = emailUTF8;
|
||||
}
|
||||
|
||||
|
@ -191,7 +191,7 @@
|
||||
|
||||
<!ENTITY fxaccount_finish_migrating_header 'Sign in to finish upgrading'>
|
||||
<!ENTITY fxaccount_finish_migrating_button_label 'Finish upgrading'>
|
||||
<!ENTITY fxaccount_finish_migrating_description 'Upgrading can transfer a lot of data. It\'s best to be on a WiFi network.'>
|
||||
<!ENTITY fxaccount_finish_migrating_description 'Upgrading can transfer a lot of data. It\'s best to be on a Wi-Fi network.'>
|
||||
|
||||
<!ENTITY fxaccount_status_header2 'Firefox Account'>
|
||||
<!ENTITY fxaccount_status_signed_in_as 'Signed in as'>
|
||||
|
@ -7,6 +7,7 @@ package org.mozilla.gecko.sync;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.common.telemetry.TelemetryWrapper;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountUtils;
|
||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||
@ -18,6 +19,7 @@ import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageResponse;
|
||||
import org.mozilla.gecko.sync.stage.AbstractNonRepositorySyncStage;
|
||||
import org.mozilla.gecko.sync.stage.NoSuchStageException;
|
||||
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
|
||||
|
||||
/**
|
||||
* The purpose of this class is to talk to a Sync 1.1 server and check
|
||||
@ -152,6 +154,7 @@ public class MigrationSentinelSyncStage extends AbstractNonRepositorySyncStage {
|
||||
|
||||
private void onMigrated() {
|
||||
Logger.info(LOG_TAG, "Account migrated!");
|
||||
TelemetryWrapper.addToHistogram(TelemetryContract.SYNC11_MIGRATIONS_SUCCEEDED, 1);
|
||||
session.config.persistLastMigrationSentinelCheckTimestamp(fetchTimestamp);
|
||||
session.abort(null, "Account migrated.");
|
||||
}
|
||||
@ -163,6 +166,7 @@ public class MigrationSentinelSyncStage extends AbstractNonRepositorySyncStage {
|
||||
|
||||
private void onError(Exception ex, String reason) {
|
||||
Logger.info(LOG_TAG, "Could not migrate: " + reason, ex);
|
||||
TelemetryWrapper.addToHistogram(TelemetryContract.SYNC11_MIGRATIONS_FAILED, 1);
|
||||
session.abort(ex, reason);
|
||||
}
|
||||
|
||||
@ -181,6 +185,9 @@ public class MigrationSentinelSyncStage extends AbstractNonRepositorySyncStage {
|
||||
public void handleRequestSuccess(SyncStorageResponse response) {
|
||||
Logger.info(LOG_TAG, "Found " + META_FXA_CREDENTIALS + " record; attempting migration.");
|
||||
setTimestamp(response.normalizedWeaveTimestamp());
|
||||
|
||||
TelemetryWrapper.addToHistogram(TelemetryContract.SYNC11_MIGRATION_SENTINELS_SEEN, 1);
|
||||
|
||||
try {
|
||||
final ExtendedJSONObject body = response.jsonObjectBody();
|
||||
final CryptoRecord cryptoRecord = CryptoRecord.fromJSONRecord(body);
|
||||
|
48
mobile/android/base/sync/telemetry/TelemetryContract.java
Normal file
48
mobile/android/base/sync/telemetry/TelemetryContract.java
Normal file
@ -0,0 +1,48 @@
|
||||
/* 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.sync.telemetry;
|
||||
|
||||
public class TelemetryContract {
|
||||
/**
|
||||
* We are a Sync 1.1 (legacy) client, and we downloaded a migration sentinel.
|
||||
*/
|
||||
public static final String SYNC11_MIGRATION_SENTINELS_SEEN = "FENNEC_SYNC11_MIGRATION_SENTINELS_SEEN";
|
||||
|
||||
/**
|
||||
* We are a Sync 1.1 (legacy) client and we have downloaded a migration
|
||||
* sentinel, but there was an error creating a Firefox Account from that
|
||||
* sentinel.
|
||||
* <p>
|
||||
* We have logged the error and are ignoring that sentinel.
|
||||
*/
|
||||
public static final String SYNC11_MIGRATIONS_FAILED = "FENNEC_SYNC11_MIGRATIONS_FAILED";
|
||||
|
||||
/**
|
||||
* We are a Sync 1.1 (legacy) client and we have downloaded a migration
|
||||
* sentinel, and there was no reported error creating a Firefox Account from
|
||||
* that sentinel.
|
||||
* <p>
|
||||
* We have created a Firefox Account corresponding to the sentinel and have
|
||||
* queued the existing Old Sync account for removal.
|
||||
*/
|
||||
public static final String SYNC11_MIGRATIONS_SUCCEEDED = "FENNEC_SYNC11_MIGRATIONS_SUCCEEDED";
|
||||
|
||||
/**
|
||||
* We are (now) a Sync 1.5 (Firefox Accounts-based) client that migrated from
|
||||
* Sync 1.1. We have presented the user the "complete upgrade" notification.
|
||||
* <p>
|
||||
* We will offer every time a sync is triggered, including when a notification
|
||||
* is already pending.
|
||||
*/
|
||||
public static final String SYNC11_MIGRATION_NOTIFICATIONS_OFFERED = "FENNEC_SYNC11_MIGRATION_NOTIFICATIONS_OFFERED";
|
||||
|
||||
/**
|
||||
* We are (now) a Sync 1.5 (Firefox Accounts-based) client that migrated from
|
||||
* Sync 1.1. We have presented the user the "complete upgrade" notification
|
||||
* and they have successfully completed the upgrade process by entering their
|
||||
* Firefox Account credentials.
|
||||
*/
|
||||
public static final String SYNC11_MIGRATIONS_COMPLETED = "FENNEC_SYNC11_MIGRATIONS_COMPLETED";
|
||||
}
|
@ -7157,5 +7157,32 @@
|
||||
"n_buckets" : 50,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "The number of saved signons in storage"
|
||||
},
|
||||
"FENNEC_SYNC11_MIGRATION_SENTINELS_SEEN": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "count",
|
||||
"description": "The number of Sync 1.1 -> Sync 1.5 migration sentinels seen by Android Sync."
|
||||
},
|
||||
"FENNEC_SYNC11_MIGRATIONS_FAILED": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "count",
|
||||
"description": "The number of Sync 1.1 -> Sync 1.5 migrations that failed during Android Sync."
|
||||
},
|
||||
"FENNEC_SYNC11_MIGRATIONS_SUCCEEDED": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "count",
|
||||
"description": "The number of Sync 1.1 -> Sync 1.5 migrations that succeeded during Android Sync."
|
||||
},
|
||||
"FENNEC_SYNC11_MIGRATION_NOTIFICATIONS_OFFERED": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "exponential",
|
||||
"high": 500,
|
||||
"n_buckets": 5,
|
||||
"description": "The number of Sync 1.5 'complete upgrade/migration' notifications offered by Android Sync."
|
||||
},
|
||||
"FENNEC_SYNC11_MIGRATIONS_COMPLETED": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "count",
|
||||
"description": "The number of Sync 1.5 migrations completed by Android Sync."
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user