1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.browserservices; 6 7 import androidx.annotation.IntDef; 8 import androidx.annotation.Nullable; 9 10 import org.chromium.base.metrics.RecordHistogram; 11 import org.chromium.base.metrics.RecordUserAction; 12 import org.chromium.base.task.PostTask; 13 import org.chromium.base.task.TaskTraits; 14 import org.chromium.chrome.browser.init.ChromeBrowserInitializer; 15 import org.chromium.chrome.browser.metrics.UkmRecorder; 16 import org.chromium.chrome.browser.tab.Tab; 17 import org.chromium.components.content_settings.ContentSettingsType; 18 19 import java.lang.annotation.Retention; 20 import java.lang.annotation.RetentionPolicy; 21 22 import javax.inject.Inject; 23 24 import dagger.Reusable; 25 26 /** 27 * Encapsulates Uma recording actions related to Trusted Web Activities. 28 */ 29 @Reusable 30 public class TrustedWebActivityUmaRecorder { 31 @IntDef({DelegatedNotificationSmallIconFallback.NO_FALLBACK, 32 DelegatedNotificationSmallIconFallback.FALLBACK_ICON_NOT_PROVIDED, 33 DelegatedNotificationSmallIconFallback.FALLBACK_FOR_STATUS_BAR, 34 DelegatedNotificationSmallIconFallback.FALLBACK_FOR_STATUS_BAR_AND_CONTENT}) 35 @Retention(RetentionPolicy.SOURCE) 36 public @interface DelegatedNotificationSmallIconFallback { 37 int NO_FALLBACK = 0; 38 int FALLBACK_ICON_NOT_PROVIDED = 1; 39 int FALLBACK_FOR_STATUS_BAR = 2; 40 int FALLBACK_FOR_STATUS_BAR_AND_CONTENT = 3; 41 int NUM_ENTRIES = 4; 42 } 43 44 @IntDef({ShareRequestMethod.GET, ShareRequestMethod.POST}) 45 @Retention(RetentionPolicy.SOURCE) 46 public @interface ShareRequestMethod { 47 int GET = 0; 48 int POST = 1; 49 int NUM_ENTRIES = 2; 50 } 51 52 @IntDef({PermissionChanged.NULL_TO_TRUE, PermissionChanged.NULL_TO_FALSE, 53 PermissionChanged.TRUE_TO_FALSE, PermissionChanged.FALSE_TO_TRUE}) 54 @Retention(RetentionPolicy.SOURCE) 55 public @interface PermissionChanged { 56 int NULL_TO_FALSE = 0; 57 int NULL_TO_TRUE = 1; 58 int TRUE_TO_FALSE = 2; 59 int FALSE_TO_TRUE = 3; 60 int NUM_ENTRIES = 4; 61 } 62 63 private final ChromeBrowserInitializer mBrowserInitializer; 64 65 @Inject TrustedWebActivityUmaRecorder(ChromeBrowserInitializer browserInitializer)66 public TrustedWebActivityUmaRecorder(ChromeBrowserInitializer browserInitializer) { 67 mBrowserInitializer = browserInitializer; 68 } 69 70 /** 71 * Records that a Trusted Web Activity has been opened. 72 */ recordTwaOpened(@ullable Tab tab)73 public void recordTwaOpened(@Nullable Tab tab) { 74 RecordUserAction.record("BrowserServices.TwaOpened"); 75 if (tab != null) { 76 new UkmRecorder.Bridge().recordEventWithBooleanMetric( 77 tab.getWebContents(), "TrustedWebActivity.Open", "HasOccurred"); 78 } 79 } 80 81 /** 82 * Records the time that a Trusted Web Activity has been in resumed state. 83 */ recordTwaOpenTime(long durationMs)84 public void recordTwaOpenTime(long durationMs) { 85 recordDuration(durationMs, "BrowserServices.TwaOpenTime.V2"); 86 } 87 88 /** 89 * Records the time spent in verified origin until navigating to unverified one or pausing 90 * the Trusted Web Activity. 91 */ recordTimeInVerifiedOrigin(long durationMs)92 public void recordTimeInVerifiedOrigin(long durationMs) { 93 recordDuration(durationMs, "TrustedWebActivity.TimeInVerifiedOrigin.V2"); 94 } 95 96 /** 97 * Records the time spent in verified origin until navigating to unverified one or pausing 98 * the Trusted Web Activity. 99 */ recordTimeOutOfVerifiedOrigin(long durationMs)100 public void recordTimeOutOfVerifiedOrigin(long durationMs) { 101 recordDuration(durationMs, "TrustedWebActivity.TimeOutOfVerifiedOrigin.V2"); 102 } 103 recordDuration(long durationMs, String histogramName)104 private void recordDuration(long durationMs, String histogramName) { 105 RecordHistogram.recordLongTimesHistogram(histogramName, durationMs); 106 } 107 108 /** 109 * Records the fact that disclosure was shown. 110 */ recordDisclosureShown()111 public void recordDisclosureShown() { 112 RecordUserAction.record("TrustedWebActivity.DisclosureShown"); 113 } 114 115 /** 116 * Records the fact that disclosure was accepted by user. 117 */ recordDisclosureAccepted()118 public void recordDisclosureAccepted() { 119 RecordUserAction.record("TrustedWebActivity.DisclosureAccepted"); 120 } 121 122 /** 123 * Records which action the user took upon seeing a clear data dialog. 124 * @param accepted Whether user proceeded to the settings from the dialog. 125 * @param triggeredByUninstall Whether the dialog was triggered by app uninstall as opposed to 126 * app data getting cleared. 127 */ recordClearDataDialogAction(boolean accepted, boolean triggeredByUninstall)128 public void recordClearDataDialogAction(boolean accepted, boolean triggeredByUninstall) { 129 String histogramName = triggeredByUninstall 130 ? "TrustedWebActivity.ClearDataDialogOnUninstallAccepted" 131 : "TrustedWebActivity.ClearDataDialogOnClearAppDataAccepted"; 132 RecordHistogram.recordBooleanHistogram(histogramName, accepted); 133 } 134 135 /** 136 * Records the fact that site settings were opened via "Manage Space" button in TWA client app's 137 * settings. 138 */ recordOpenedSettingsViaManageSpace()139 public void recordOpenedSettingsViaManageSpace() { 140 doWhenNativeLoaded(() -> 141 RecordUserAction.record("TrustedWebActivity.OpenedSettingsViaManageSpace")); 142 } 143 144 /** 145 * Records which fallback (if any) was used for the small icon of a delegated notification. 146 */ recordDelegatedNotificationSmallIconFallback( @elegatedNotificationSmallIconFallback int fallback)147 public void recordDelegatedNotificationSmallIconFallback( 148 @DelegatedNotificationSmallIconFallback int fallback) { 149 RecordHistogram.recordEnumeratedHistogram( 150 "TrustedWebActivity.DelegatedNotificationSmallIconFallback", fallback, 151 DelegatedNotificationSmallIconFallback.NUM_ENTRIES); 152 } 153 154 /** 155 * Records whether or not a splash screen has been shown when launching a TWA. 156 * Uses {@link TaskTraits#BEST_EFFORT} in order to not get in the way of loading the page. 157 */ recordSplashScreenUsage(boolean wasShown)158 public void recordSplashScreenUsage(boolean wasShown) { 159 doWhenNativeLoaded(() -> 160 PostTask.postTask(TaskTraits.BEST_EFFORT, () -> 161 RecordHistogram.recordBooleanHistogram( 162 "TrustedWebActivity.SplashScreenShown", wasShown) 163 )); 164 } 165 166 /** 167 * Records the fact that data was shared via a TWA. 168 */ recordShareTargetRequest(@hareRequestMethod int method)169 public void recordShareTargetRequest(@ShareRequestMethod int method) { 170 RecordHistogram.recordEnumeratedHistogram("TrustedWebActivity.ShareTargetRequest", 171 method, ShareRequestMethod.NUM_ENTRIES); 172 } 173 doWhenNativeLoaded(Runnable runnable)174 private void doWhenNativeLoaded(Runnable runnable) { 175 mBrowserInitializer.runNowOrAfterFullBrowserStarted(runnable); 176 } 177 recordLocationDelegationEnrolled(boolean enrolled)178 public void recordLocationDelegationEnrolled(boolean enrolled) { 179 RecordHistogram.recordBooleanHistogram( 180 "TrustedWebActivity.LocationDelegationEnrolled", enrolled); 181 } 182 recordPermissionChangedUma( @ontentSettingsType int type, Boolean last, boolean enabled)183 public void recordPermissionChangedUma( 184 @ContentSettingsType int type, Boolean last, boolean enabled) { 185 if (type == ContentSettingsType.GEOLOCATION) { 186 @Nullable 187 @PermissionChanged 188 Integer change = null; 189 if (last == null) { 190 if (enabled) { 191 change = PermissionChanged.NULL_TO_TRUE; 192 } else { 193 change = PermissionChanged.NULL_TO_FALSE; 194 } 195 } else { 196 if (last && !enabled) change = PermissionChanged.TRUE_TO_FALSE; 197 if (!last && enabled) change = PermissionChanged.FALSE_TO_TRUE; 198 } 199 if (change != null) { 200 RecordHistogram.recordEnumeratedHistogram( 201 "TrustedWebActivity.LocationPermissionChanged", change, 202 PermissionChanged.NUM_ENTRIES); 203 } 204 } 205 } 206 recordLocationPermissionRequestResult(boolean enabled)207 public void recordLocationPermissionRequestResult(boolean enabled) { 208 RecordHistogram.recordBooleanHistogram( 209 "TrustedWebActivity.LocationPermissionRequestIsGranted", enabled); 210 } 211 recordLocationUpdateError(@ocationUpdateError int error)212 public void recordLocationUpdateError(@LocationUpdateError int error) { 213 RecordHistogram.recordEnumeratedHistogram("TrustedWebActivity.LocationUpdateErrorCode", 214 error, LocationUpdateError.MAX_VALUE + 1); 215 } 216 recordQualityEnforcementViolation( Tab tab, @QualityEnforcementViolationType int type)217 public void recordQualityEnforcementViolation( 218 Tab tab, @QualityEnforcementViolationType int type) { 219 RecordHistogram.recordEnumeratedHistogram("TrustedWebActivity.QualityEnforcementViolation", 220 type, QualityEnforcementViolationType.MAX_VALUE + 1); 221 222 new UkmRecorder.Bridge().recordEventWithIntegerMetric(tab.getWebContents(), 223 /* eventName = */ "TrustedWebActivity.QualityEnforcementViolation", 224 /* metricName = */ "ViolationType", 225 /* metricValue = */ type); 226 } 227 recordQualityEnforcementViolationCrashed( @ualityEnforcementViolationType int type)228 public void recordQualityEnforcementViolationCrashed( 229 @QualityEnforcementViolationType int type) { 230 RecordHistogram.recordEnumeratedHistogram( 231 "TrustedWebActivity.QualityEnforcementViolation.Crashed", type, 232 QualityEnforcementViolationType.MAX_VALUE + 1); 233 } 234 } 235