Bug 1205835 - Add TelemetryPingGenerator for core pings. r=rnewman

--HG--
extra : commitid : Fcv0mHGrnsq
extra : rebase_source : a7eaa66686a398d2e75000bc97346889ec107c18
This commit is contained in:
Michael Comella 2016-01-25 17:47:35 -08:00
parent 63f3fa53d1
commit 1fee6bfb4c
6 changed files with 184 additions and 0 deletions

View File

@ -38,12 +38,18 @@ import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.Log;
public final class GeckoProfile {
private static final String LOGTAG = "GeckoProfile";
// The path in the profile to the file containing the client ID.
private static final String CLIENT_ID_FILE_PATH = "datareporting/state.json";
// In the client ID file, the attribute title in the JSON object containing the client ID value.
private static final String CLIENT_ID_JSON_ATTR = "clientID";
// Only tests should need to do this.
// We can default this to AppConstants.RELEASE_BUILD once we fix Bug 1069687.
private static volatile boolean sAcceptDirectoryChanges = true;
@ -588,6 +594,39 @@ public final class GeckoProfile {
return new File(f, aFile);
}
/**
* Retrieves the Gecko client ID from the filesystem.
*
* This method assumes the client ID is located in a file at a hard-coded path within the profile. The format of
* this file is a JSONObject which at the bottom level contains a String -> String mapping containing the client ID.
*
* WARNING: the platform provides a JSM to retrieve the client ID [1] and this would be a
* robust way to access it. However, we don't want to rely on Gecko running in order to get
* the client ID so instead we access the file this module accesses directly. However, it's
* possible the format of this file (and the access calls in the jsm) will change, leaving
* this code to fail.
*
* TODO: Write tests to prevent regressions. Mention them here. Test both file location and file format.
*
* [1]: https://mxr.mozilla.org/mozilla-central/source/toolkit/modules/ClientID.jsm
*/
@WorkerThread
public String getClientId() throws IOException {
final String clientIdFileContents;
try {
clientIdFileContents = readFile(CLIENT_ID_FILE_PATH);
} catch (final IOException e) {
throw new IOException("Could not read client ID file to retrieve client ID", e);
}
try {
final org.json.JSONObject json = new org.json.JSONObject(clientIdFileContents);
return json.getString(CLIENT_ID_JSON_ATTR);
} catch (final JSONException e) {
throw new IOException("Could not parse JSON to retrieve client ID", e);
}
}
/**
* Moves the session file to the backup session file.
*

View File

@ -0,0 +1,25 @@
/* 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.telemetry;
public class TelemetryConstants {
public static class CorePing {
private CorePing() { /* To prevent instantiation */ }
public static final String NAME = "core";
public static final int VERSION_VALUE = 1;
public static final String OS_VALUE = "Android";
public static final String ARCHITECTURE = "arch";
public static final String CLIENT_ID = "clientId";
public static final String DEVICE = "device";
public static final String LOCALE = "locale";
public static final String OS_ATTR = "os";
public static final String OS_VERSION = "osversion";
public static final String SEQ = "seq";
public static final String VERSION_ATTR = "v";
}
}

View File

@ -0,0 +1,24 @@
/* -*- 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.telemetry;
import org.mozilla.gecko.sync.ExtendedJSONObject;
/**
* Container for telemetry data and the data necessary to upload it.
*/
public class TelemetryPing {
private final String url;
private final ExtendedJSONObject payload;
public TelemetryPing(final String url, final ExtendedJSONObject payload) {
this.url = url;
this.payload = payload;
}
public String getURL() { return url; }
public ExtendedJSONObject getPayload() { return payload; }
}

View File

@ -0,0 +1,86 @@
/* -*- 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.telemetry;
import android.os.Build;
import java.io.IOException;
import java.util.Locale;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.Locales;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.telemetry.TelemetryConstants.CorePing;
import org.mozilla.gecko.util.StringUtils;
/**
* A class with static methods to generate the various Java-created Telemetry pings to upload to the telemetry server.
*/
public class TelemetryPingGenerator {
// In the server url, the initial path directly after the "scheme://host:port/"
private static final String SERVER_INITIAL_PATH = "submit/telemetry";
/**
* Returns a url of the format:
* http://hostname/submit/telemetry/docId/docType/appName/appVersion/appUpdateChannel/appBuildID
*
* @param docId A unique document ID for the ping associated with the upload to this server
* @param serverURLSchemeHostPort The server url with the scheme, host, and port (e.g. "http://mozilla.org:80")
* @param docType The name of the ping (e.g. "main")
* @return a url at which to POST the telemetry data to
*/
private static String getTelemetryServerURL(final String docId, final String serverURLSchemeHostPort,
final String docType) {
final String appName = AppConstants.MOZ_APP_BASENAME;
final String appVersion = AppConstants.MOZ_APP_VERSION;
final String appUpdateChannel = AppConstants.MOZ_UPDATE_CHANNEL;
final String appBuildId = AppConstants.MOZ_APP_BUILDID;
// The compiler will optimize a single String concatenation into a StringBuilder statement.
// If you change this `return`, be sure to keep it as a single statement to keep it optimized!
return serverURLSchemeHostPort + '/' +
SERVER_INITIAL_PATH + '/' +
docId + '/' +
docType + '/' +
appName + '/' +
appVersion + '/' +
appUpdateChannel + '/' +
appBuildId + '/';
}
/**
* @param docId A unique document ID for the ping associated with the upload to this server
* @param clientId The client ID of this profile (from Gecko)
* @param serverURLSchemeHostPort The server url with the scheme, host, and port (e.g. "http://mozilla.org:80")
* @throws IOException when client ID could not be created
*/
public static TelemetryPing createCorePing(final String docId, final String clientId,
final String serverURLSchemeHostPort, final int seq) {
final String serverURL = getTelemetryServerURL(docId, serverURLSchemeHostPort, CorePing.NAME);
final ExtendedJSONObject payload = createCorePingPayload(clientId, seq);
return new TelemetryPing(serverURL, payload);
}
private static ExtendedJSONObject createCorePingPayload(final String clientId, final int seq) {
final ExtendedJSONObject ping = new ExtendedJSONObject();
ping.put(CorePing.VERSION_ATTR, CorePing.VERSION_VALUE);
ping.put(CorePing.OS_ATTR, CorePing.OS_VALUE);
// We limit the device descriptor to 32 characters because it can get long. We give fewer characters to the
// manufacturer because we're less likely to have manufacturers with similar names than we are for a
// manufacturer to have two devices with the similar names (e.g. Galaxy S6 vs. Galaxy Note 6).
final String deviceDescriptor =
StringUtils.safeSubstring(Build.MANUFACTURER, 0, 12) + '-' + StringUtils.safeSubstring(Build.MODEL, 0, 19);
ping.put(CorePing.ARCHITECTURE, AppConstants.ANDROID_CPU_ARCH);
ping.put(CorePing.CLIENT_ID, clientId);
ping.put(CorePing.DEVICE, deviceDescriptor);
ping.put(CorePing.LOCALE, Locales.getLanguageTag(Locale.getDefault()));
ping.put(CorePing.OS_VERSION, Integer.toString(Build.VERSION.SDK_INT)); // A String for cross-platform reasons.
ping.put(CorePing.SEQ, seq);
return ping;
}
}

View File

@ -6,6 +6,7 @@
package org.mozilla.gecko.util;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.mozilla.gecko.AppConstants.Versions;
@ -240,4 +241,10 @@ public class StringUtils {
return Collections.unmodifiableSet(names);
}
public static String safeSubstring(@NonNull final String str, final int start, final int end) {
return str.substring(
Math.max(0, start),
Math.min(end, str.length()));
}
}

View File

@ -540,6 +540,9 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'tabs/TabsPanel.java',
'tabs/TabsPanelThumbnailView.java',
'Telemetry.java',
'telemetry/TelemetryConstants.java',
'telemetry/TelemetryPing.java',
'telemetry/TelemetryPingGenerator.java',
'TelemetryContract.java',
'TextSelection.java',
'TextSelectionHandle.java',