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