Commit 71943ac0 authored by Peter Kotwicz's avatar Peter Kotwicz Committed by Commit Bot

[Android WebAPK] Add UKM metric for WebAPK launches

This CL:
- Adds recording for the WebAPK.Visit UKM metric
- Changes LaunchMetrics#recordHomeScreenLaunchIntoStandaloneActivity()
to take WebappInfo as a parameter

Privacy doc: https://docs.google.com/document/d/10m4NwkTFVjQ7uboJP9rUQM0Yh7ULD6dVvGlL3eQtD3g/edit?usp=sharing

BUG=982362
TEST=None

Change-Id: I93900c9c448d491540b3f857cc4643f23f25cafd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1783709Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarRobert Kaplow <rkaplow@chromium.org>
Commit-Queue: Peter Kotwicz <pkotwicz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#695019}
parent 7de27e55
...@@ -4,9 +4,16 @@ ...@@ -4,9 +4,16 @@
package org.chromium.chrome.browser.metrics; package org.chromium.chrome.browser.metrics;
import org.chromium.base.StrictModeContext;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.metrics.CachedMetrics; import org.chromium.base.metrics.CachedMetrics;
import org.chromium.blink_public.platform.WebDisplayMode; import org.chromium.blink_public.platform.WebDisplayMode;
import org.chromium.chrome.browser.ShortcutSource;
import org.chromium.chrome.browser.webapps.WebApkInfo;
import org.chromium.chrome.browser.webapps.WebApkUkmRecorder;
import org.chromium.chrome.browser.webapps.WebappDataStorage;
import org.chromium.chrome.browser.webapps.WebappInfo;
import org.chromium.chrome.browser.webapps.WebappRegistry;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -24,15 +31,13 @@ public class LaunchMetrics { ...@@ -24,15 +31,13 @@ public class LaunchMetrics {
public final boolean mIsShortcut; public final boolean mIsShortcut;
// Corresponds to C++ ShortcutInfo::Source // Corresponds to C++ ShortcutInfo::Source
public final int mSource; public final int mSource;
@WebDisplayMode public final WebappInfo mWebappInfo;
public final int mDisplayMode;
public HomeScreenLaunch( public HomeScreenLaunch(String url, boolean isShortcut, int source, WebappInfo webappInfo) {
String url, boolean isShortcut, int source, @WebDisplayMode int displayMode) {
mUrl = url; mUrl = url;
mIsShortcut = isShortcut; mIsShortcut = isShortcut;
mSource = source; mSource = source;
mDisplayMode = displayMode; mWebappInfo = webappInfo;
} }
} }
...@@ -41,13 +46,21 @@ public class LaunchMetrics { ...@@ -41,13 +46,21 @@ public class LaunchMetrics {
/** /**
* Records the launch of a standalone Activity for a URL (i.e. a WebappActivity) * Records the launch of a standalone Activity for a URL (i.e. a WebappActivity)
* added from a specific source. * added from a specific source.
* @param url URL that kicked off the Activity's creation. * @param webappInfo WebappInfo for launched activity.
* @param source integer id of the source from where the URL was added.
* @param displayMode integer id of the {@link WebDisplayMode} of the web app.
*/ */
public static void recordHomeScreenLaunchIntoStandaloneActivity( public static void recordHomeScreenLaunchIntoStandaloneActivity(WebappInfo webappInfo) {
String url, int source, @WebDisplayMode int displayMode) { int source = webappInfo.source();
sHomeScreenLaunches.add(new HomeScreenLaunch(url, false, source, displayMode));
if (webappInfo.isForWebApk() && source == ShortcutSource.UNKNOWN) {
// WebApkInfo#source() identifies how the WebAPK was launched (e.g. via deep link).
// When the WebAPK is launched from the app list (ShortcutSource#UNKNOWN), query
// WebappDataStorage to determine how the WebAPK was installed (SOURCE_APP_BANNER_WEBAPK
// vs SOURCE_ADD_TO_HOMESCREEN_PWA). WebAPKs set WebappDataStorage#getSource() at
// install time.
source = getSourceForWebApkFromWebappDataStorage(webappInfo);
}
sHomeScreenLaunches.add(new HomeScreenLaunch(webappInfo.url(), false, source, webappInfo));
} }
/** /**
...@@ -56,7 +69,7 @@ public class LaunchMetrics { ...@@ -56,7 +69,7 @@ public class LaunchMetrics {
* @param source integer id of the source from where the URL was added. * @param source integer id of the source from where the URL was added.
*/ */
public static void recordHomeScreenLaunchIntoTab(String url, int source) { public static void recordHomeScreenLaunchIntoTab(String url, int source) {
sHomeScreenLaunches.add(new HomeScreenLaunch(url, true, source, WebDisplayMode.UNDEFINED)); sHomeScreenLaunches.add(new HomeScreenLaunch(url, true, source, null));
} }
/** /**
...@@ -67,8 +80,17 @@ public class LaunchMetrics { ...@@ -67,8 +80,17 @@ public class LaunchMetrics {
*/ */
public static void commitLaunchMetrics(WebContents webContents) { public static void commitLaunchMetrics(WebContents webContents) {
for (HomeScreenLaunch launch : sHomeScreenLaunches) { for (HomeScreenLaunch launch : sHomeScreenLaunches) {
nativeRecordLaunch(launch.mIsShortcut, launch.mUrl, launch.mSource, launch.mDisplayMode, WebappInfo webappInfo = launch.mWebappInfo;
webContents); @WebDisplayMode
int displayMode =
(webappInfo == null) ? WebDisplayMode.UNDEFINED : webappInfo.displayMode();
nativeRecordLaunch(
launch.mIsShortcut, launch.mUrl, launch.mSource, displayMode, webContents);
if (webappInfo != null && webappInfo.isForWebApk()) {
WebApkInfo webApkInfo = (WebApkInfo) webappInfo;
WebApkUkmRecorder.recordWebApkLaunch(webApkInfo.manifestUrl(),
webApkInfo.distributor(), webApkInfo.webApkVersionCode(), launch.mSource);
}
} }
sHomeScreenLaunches.clear(); sHomeScreenLaunches.clear();
...@@ -91,6 +113,26 @@ public class LaunchMetrics { ...@@ -91,6 +113,26 @@ public class LaunchMetrics {
nativeRecordHomePageLaunchMetrics(showHomeButton, homepageIsNtp, homepageUrl); nativeRecordHomePageLaunchMetrics(showHomeButton, homepageIsNtp, homepageUrl);
} }
/**
* Returns the source from the WebappDataStorage if the source has been stored before. Returns
* {@link ShortcutSource.WEBAPK_UNKNOWN} otherwise.
*/
private static int getSourceForWebApkFromWebappDataStorage(WebappInfo webappInfo) {
WebappDataStorage storage = null;
try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
WebappRegistry.warmUpSharedPrefsForId(webappInfo.id());
storage = WebappRegistry.getInstance().getWebappDataStorage(webappInfo.id());
}
if (storage == null) {
return ShortcutSource.WEBAPK_UNKNOWN;
}
int source = storage.getSource();
return (source == ShortcutSource.UNKNOWN) ? ShortcutSource.WEBAPK_UNKNOWN : source;
}
private static native void nativeRecordLaunch(boolean isShortcut, String url, int source, private static native void nativeRecordLaunch(boolean isShortcut, String url, int source,
@WebDisplayMode int displayMode, WebContents webContents); @WebDisplayMode int displayMode, WebContents webContents);
private static native void nativeRecordHomePageLaunchMetrics( private static native void nativeRecordHomePageLaunchMetrics(
......
...@@ -18,6 +18,16 @@ public class WebApkUkmRecorder { ...@@ -18,6 +18,16 @@ public class WebApkUkmRecorder {
nativeRecordSessionDuration(manifestUrl, distributor, versionCode, duration); nativeRecordSessionDuration(manifestUrl, distributor, versionCode, duration);
} }
/**
* Records that WebAPK was launched and the reason for the launch.
*/
public static void recordWebApkLaunch(
String manifestUrl, @WebApkDistributor int distributor, int versionCode, int source) {
nativeRecordVisit(manifestUrl, distributor, versionCode, source);
}
private static native void nativeRecordSessionDuration( private static native void nativeRecordSessionDuration(
String manifestUrl, int distributor, int versionCode, long duration); String manifestUrl, int distributor, int versionCode, long duration);
private static native void nativeRecordVisit(
String manifestUrl, int distributor, int versionCode, int source);
} }
...@@ -12,7 +12,6 @@ import android.net.Uri; ...@@ -12,7 +12,6 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.StrictMode;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
...@@ -178,18 +177,7 @@ public class WebappLauncherActivity extends Activity { ...@@ -178,18 +177,7 @@ public class WebappLauncherActivity extends Activity {
private static void launchWebapp(Activity launchingActivity, Intent intent, private static void launchWebapp(Activity launchingActivity, Intent intent,
@NonNull WebappInfo webappInfo, long createTimestamp) { @NonNull WebappInfo webappInfo, long createTimestamp) {
String webappUrl = webappInfo.url(); LaunchMetrics.recordHomeScreenLaunchIntoStandaloneActivity(webappInfo);
int webappSource = webappInfo.source();
// Retrieves the source of the WebAPK from WebappDataStorage if it is unknown. The
// {@link webappSource} is only known in the cases of an external intent or a
// notification that launches a WebAPK. Otherwise, it's not trustworthy and we must read
// the SharedPreference to get the installation source.
if (webappInfo.isForWebApk() && webappSource == ShortcutSource.UNKNOWN) {
webappSource = getWebApkSource(webappInfo);
}
LaunchMetrics.recordHomeScreenLaunchIntoStandaloneActivity(
webappUrl, webappSource, webappInfo.displayMode());
// Add all information needed to launch WebappActivity without {@link // Add all information needed to launch WebappActivity without {@link
// WebappActivity#sWebappInfoMap} to launch intent. When the Android OS has killed a // WebappActivity#sWebappInfoMap} to launch intent. When the Android OS has killed a
...@@ -207,27 +195,6 @@ public class WebappLauncherActivity extends Activity { ...@@ -207,27 +195,6 @@ public class WebappLauncherActivity extends Activity {
} }
} }
// Gets the source of a WebAPK from the WebappDataStorage if the source has been stored before.
private static int getWebApkSource(WebappInfo webappInfo) {
WebappDataStorage storage = null;
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
try {
WebappRegistry.warmUpSharedPrefsForId(webappInfo.id());
storage = WebappRegistry.getInstance().getWebappDataStorage(webappInfo.id());
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
if (storage != null) {
int source = storage.getSource();
if (source != ShortcutSource.UNKNOWN) {
return source;
}
}
return ShortcutSource.WEBAPK_UNKNOWN;
}
/** /**
* Returns whether {@link sourceIntent} was sent by a WebAPK to relaunch itself. * Returns whether {@link sourceIntent} was sent by a WebAPK to relaunch itself.
* *
......
...@@ -15,6 +15,18 @@ ...@@ -15,6 +15,18 @@
using base::android::JavaParamRef; using base::android::JavaParamRef;
namespace {
// Converts Java string to GURL. Returns an empty GURL if the Java string is
// null.
GURL ConvertNullableJavaStringToGURL(JNIEnv* env,
const JavaParamRef<jstring>& java_url) {
return java_url ? GURL(base::android::ConvertJavaStringToUTF8(env, java_url))
: GURL();
}
} // namespace
// static // static
void WebApkUkmRecorder::RecordInstall(const GURL& manifest_url, void WebApkUkmRecorder::RecordInstall(const GURL& manifest_url,
int version_code) { int version_code) {
...@@ -52,6 +64,25 @@ void WebApkUkmRecorder::RecordSessionDuration(const GURL& manifest_url, ...@@ -52,6 +64,25 @@ void WebApkUkmRecorder::RecordSessionDuration(const GURL& manifest_url,
.Record(ukm_recorder); .Record(ukm_recorder);
} }
// static
void WebApkUkmRecorder::RecordVisit(const GURL& manifest_url,
int64_t distributor,
int64_t version_code,
int source) {
if (!manifest_url.is_valid())
return;
ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
ukm_recorder->UpdateSourceURL(source_id, manifest_url);
ukm::builders::WebAPK_Visit(source_id)
.SetDistributor(distributor)
.SetAppVersion(version_code)
.SetLaunchSource(source)
.SetLaunch(true)
.Record(ukm_recorder);
}
// Called by the Java counterpart to record the Session Duration UKM metric. // Called by the Java counterpart to record the Session Duration UKM metric.
void JNI_WebApkUkmRecorder_RecordSessionDuration( void JNI_WebApkUkmRecorder_RecordSessionDuration(
JNIEnv* env, JNIEnv* env,
...@@ -59,10 +90,19 @@ void JNI_WebApkUkmRecorder_RecordSessionDuration( ...@@ -59,10 +90,19 @@ void JNI_WebApkUkmRecorder_RecordSessionDuration(
jint distributor, jint distributor,
jint version_code, jint version_code,
jlong duration) { jlong duration) {
if (!manifest_url)
return;
WebApkUkmRecorder::RecordSessionDuration( WebApkUkmRecorder::RecordSessionDuration(
GURL(base::android::ConvertJavaStringToUTF8(env, manifest_url)), ConvertNullableJavaStringToGURL(env, manifest_url), distributor,
distributor, version_code, duration); version_code, duration);
}
// Called by the Java counterpart to record the Visit UKM metric.
void JNI_WebApkUkmRecorder_RecordVisit(
JNIEnv* env,
const JavaParamRef<jstring>& manifest_url,
jint distributor,
jint version_code,
jint source) {
WebApkUkmRecorder::RecordVisit(
ConvertNullableJavaStringToGURL(env, manifest_url), distributor,
version_code, source);
} }
...@@ -26,6 +26,11 @@ class WebApkUkmRecorder { ...@@ -26,6 +26,11 @@ class WebApkUkmRecorder {
int64_t version_code, int64_t version_code,
int64_t duration); int64_t duration);
static void RecordVisit(const GURL& manifest_url,
int64_t distributor,
int64_t version_code,
int source);
private: private:
DISALLOW_IMPLICIT_CONSTRUCTORS(WebApkUkmRecorder); DISALLOW_IMPLICIT_CONSTRUCTORS(WebApkUkmRecorder);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment