Skip to content

Commit

Permalink
[Subscriptions] Add metrics for account WAA status
Browse files Browse the repository at this point in the history
Currently we disable the explicit price tracking when user turns off web
and app activity (WAA), and this CL helps to know how many Chrome users
we are excluding due to WAA.

In order to record the sign-out bucket, we update the flow in
CommerceSubscriptionsService slightly. This should play little effect
on existing metrics because all of them are recorded at most once a day
and their current traffic is very low (almost zero).

(cherry picked from commit e5f25c8)

Bug: 1314406
Change-Id: I62a0fc4071e46b4fe7e26f9e016d972a4a6f83da
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3615135
Commit-Queue: Zhiyuan Cai <zhiyuancai@chromium.org>
Reviewed-by: Wei-Yin Chen <wychen@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1001198}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3642316
Commit-Queue: Wei-Yin Chen <wychen@chromium.org>
Auto-Submit: Zhiyuan Cai <zhiyuancai@chromium.org>
Cr-Commit-Position: refs/branch-heads/5005@{#670}
Cr-Branched-From: 5b4d945-refs/heads/main@{#992738}
  • Loading branch information
Zhiyuan Cai authored and Chromium LUCI CQ committed May 11, 2022
1 parent cb26967 commit dffd177
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 8 deletions.
Expand Up @@ -4,11 +4,20 @@

package org.chromium.chrome.browser.subscriptions;

import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;

import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.browser.subscriptions.CommerceSubscription.SubscriptionManagementType;
import org.chromium.components.prefs.PrefService;
import org.chromium.components.signin.identitymanager.ConsentLevel;
import org.chromium.components.user_prefs.UserPrefs;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;

/**
Expand All @@ -21,6 +30,28 @@ public class CommerceSubscriptionsMetrics {
@VisibleForTesting
public static final String SUBSCRIPTION_USER_MANAGED_COUNT_HISTOGRAM =
"Commerce.Subscriptions.UserManaged.Count";
@VisibleForTesting
public static final String ACCOUNT_WAA_STATUS_HISTOGRAM = "Commerce.SignIn.AccountWaaStatus";

/**
* The account web and app activity enabled status.
*
* Needs to stay in sync with AccountWaaStatusForCommerce in enums.xml. These values are
* persisted to logs. Entries should not be renumbered and numeric values should never be
* reused.
*/
@IntDef({AccountWaaStatus.SIGN_OUT, AccountWaaStatus.SIGN_IN_WAA_DISABLED,
AccountWaaStatus.SIGN_IN_WAA_ENABLED, AccountWaaStatus.NUM_ENTRIES})
@Retention(RetentionPolicy.SOURCE)
public @interface AccountWaaStatus {
int SIGN_OUT = 0;
int SIGN_IN_WAA_DISABLED = 1;
int SIGN_IN_WAA_ENABLED = 2;

// Must be the last one.
int NUM_ENTRIES = 3;
}

/**
* Record the number of subscriptions per management type.
*/
Expand All @@ -41,4 +72,32 @@ void recordSubscriptionCounts(List<CommerceSubscription> subscriptions) {
RecordHistogram.recordCount1000Histogram(
SUBSCRIPTION_USER_MANAGED_COUNT_HISTOGRAM, userManaged);
}

void recordAccountWaaStatus() {
RecordHistogram.recordEnumeratedHistogram(
ACCOUNT_WAA_STATUS_HISTOGRAM, getAccountWaaStatus(), AccountWaaStatus.NUM_ENTRIES);
}

@AccountWaaStatus
private int getAccountWaaStatus() {
if (!isSignedIn()) {
return AccountWaaStatus.SIGN_OUT;
} else if (isWebAndAppActivityEnabled()) {
return AccountWaaStatus.SIGN_IN_WAA_ENABLED;
} else {
return AccountWaaStatus.SIGN_IN_WAA_DISABLED;
}
}

private boolean isSignedIn() {
return IdentityServicesProvider.get()
.getIdentityManager(Profile.getLastUsedRegularProfile())
.hasPrimaryAccount(ConsentLevel.SYNC);
}

private boolean isWebAndAppActivityEnabled() {
PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile());
return prefService != null
&& prefService.getBoolean(Pref.WEB_AND_APP_ACTIVITY_ENABLED_FOR_SHOPPING);
}
}
Expand Up @@ -96,23 +96,24 @@ public void destroy() {
}

private void maybeRecordMetricsAndInitializeSubscriptions() {
if ((!PriceTrackingUtilities.isPriceDropNotificationEligible())
|| (System.currentTimeMillis()
- mSharedPreferencesManager.readLong(
CHROME_MANAGED_SUBSCRIPTIONS_TIMESTAMP, -1)
< TimeUnit.SECONDS.toMillis(CommerceSubscriptionsServiceConfig
.getStaleTabLowerBoundSeconds()))) {
if (System.currentTimeMillis()
- mSharedPreferencesManager.readLong(
CHROME_MANAGED_SUBSCRIPTIONS_TIMESTAMP, -1)
< TimeUnit.SECONDS.toMillis(
CommerceSubscriptionsServiceConfig.getStaleTabLowerBoundSeconds())) {
return;
}
mSharedPreferencesManager.writeLong(
CHROME_MANAGED_SUBSCRIPTIONS_TIMESTAMP, System.currentTimeMillis());
recordMetrics();
mMetrics.recordAccountWaaStatus();
if (!PriceTrackingUtilities.isPriceDropNotificationEligible()) return;
recordMetricsForEligibleAccount();
if (mImplicitPriceDropSubscriptionsManager != null) {
mImplicitPriceDropSubscriptionsManager.initializeSubscriptions();
}
}

private void recordMetrics() {
private void recordMetricsForEligibleAccount() {
// Record notification opt-in metrics.
mPriceDropNotificationManager.canPostNotificationWithMetricsRecorded();
mPriceDropNotificationManager.recordMetricsForNotificationCounts();
Expand Down
Expand Up @@ -8,11 +8,13 @@
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import androidx.test.filters.SmallTest;

Expand All @@ -33,18 +35,26 @@
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.test.ShadowRecordHistogram;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManager;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsMetrics.AccountWaaStatus;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tasks.tab_management.PriceTrackingUtilities;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.components.browser_ui.notifications.MockNotificationManagerProxy;
import org.chromium.components.prefs.PrefService;
import org.chromium.components.signin.identitymanager.IdentityManager;
import org.chromium.components.signin.identitymanager.PrimaryAccountChangeEvent;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.components.user_prefs.UserPrefsJni;

import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -60,6 +70,9 @@ public class CommerceSubscriptionsServiceUnitTest {
@Rule
public TestRule mProcessor = new Features.JUnitProcessor();

@Rule
public JniMocker mJniMocker = new JniMocker();

@Mock
private SubscriptionsManagerImpl mSubscriptionsManager;
@Mock
Expand All @@ -72,6 +85,14 @@ public class CommerceSubscriptionsServiceUnitTest {
private ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
@Mock
private ImplicitPriceDropSubscriptionsManager mImplicitSubscriptionsManager;
@Mock
private Profile mProfile;
@Mock
private PrefService mPrefService;
@Mock
private IdentityServicesProvider mIdentityServicesProvider;
@Mock
private UserPrefs.Natives mUserPrefsJni;
@Captor
private ArgumentCaptor<IdentityManager.Observer> mIdentityManagerObserverCaptor;
@Captor
Expand Down Expand Up @@ -109,6 +130,12 @@ public void setUp() {
mMockNotificationManager.setNotificationsEnabled(false);
PriceDropNotificationManager.setNotificationManagerForTesting(mMockNotificationManager);

mJniMocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJni);
Profile.setLastUsedProfileForTesting(mProfile);
when(mUserPrefsJni.get(mProfile)).thenReturn(mPrefService);
IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
when(mIdentityServicesProvider.getIdentityManager(mProfile)).thenReturn(mIdentityManager);

mService = new CommerceSubscriptionsService(mSubscriptionsManager, mIdentityManager);
verify(mIdentityManager, times(1)).addObserver(mIdentityManagerObserverCaptor.capture());
mService.setImplicitSubscriptionsManagerForTesting(mImplicitSubscriptionsManager);
Expand Down Expand Up @@ -227,6 +254,55 @@ public void testOnResume_TooFrequent() {
verify(mImplicitSubscriptionsManager, times(0)).initializeSubscriptions();
}

@Test
@SmallTest
public void testRecordAccountWaaStatus_SignOut() {
when(mIdentityManager.hasPrimaryAccount(anyInt())).thenReturn(false);

setupTestOnResume();
assertThat(RecordHistogram.getHistogramTotalCountForTesting(
CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM),
equalTo(1));
assertThat(RecordHistogram.getHistogramValueCountForTesting(
CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM,
AccountWaaStatus.SIGN_OUT),
equalTo(1));
}

@Test
@SmallTest
public void testRecordAccountWaaStatus_SignInWaaDisabled() {
when(mIdentityManager.hasPrimaryAccount(anyInt())).thenReturn(true);
when(mPrefService.getBoolean(Pref.WEB_AND_APP_ACTIVITY_ENABLED_FOR_SHOPPING))
.thenReturn(false);

setupTestOnResume();
assertThat(RecordHistogram.getHistogramTotalCountForTesting(
CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM),
equalTo(1));
assertThat(RecordHistogram.getHistogramValueCountForTesting(
CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM,
AccountWaaStatus.SIGN_IN_WAA_DISABLED),
equalTo(1));
}

@Test
@SmallTest
public void testRecordAccountWaaStatus_SignInWaaEnabled() {
when(mIdentityManager.hasPrimaryAccount(anyInt())).thenReturn(true);
when(mPrefService.getBoolean(Pref.WEB_AND_APP_ACTIVITY_ENABLED_FOR_SHOPPING))
.thenReturn(true);

setupTestOnResume();
assertThat(RecordHistogram.getHistogramTotalCountForTesting(
CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM),
equalTo(1));
assertThat(RecordHistogram.getHistogramValueCountForTesting(
CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM,
AccountWaaStatus.SIGN_IN_WAA_ENABLED),
equalTo(1));
}

private void setupTestOnResume() {
mService.initDeferredStartupForActivity(mTabModelSelector, mActivityLifecycleDispatcher);
verify(mActivityLifecycleDispatcher, times(1))
Expand Down
6 changes: 6 additions & 0 deletions tools/metrics/histograms/enums.xml
Expand Up @@ -1222,6 +1222,12 @@ Unknown properties are collapsed to zero. -->
<int value="8" label="With signed in, no match"/>
</enum>

<enum name="AccountWaaStatusForCommerce">
<int value="0" label="Account signed out"/>
<int value="1" label="Account signed in, WAA disabled"/>
<int value="2" label="Account signed in, WAA enabled"/>
</enum>

<enum name="AccuracyTipInteraction">
<int value="0" label="kNoAction"/>
<int value="1" label="kLearnMore"/>
Expand Down
12 changes: 12 additions & 0 deletions tools/metrics/histograms/metadata/commerce/histograms.xml
Expand Up @@ -310,6 +310,18 @@ https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histogra
<token key="TabUsageStatus" variants="TabUsageStatuses"/>
</histogram>

<histogram name="Commerce.SignIn.AccountWaaStatus"
enum="AccountWaaStatusForCommerce" expires_after="2022-10-01">
<owner>zhiyuancai@chromium.org</owner>
<owner>ayman@chromium.org</owner>
<owner>chrome-shopping@google.com</owner>
<summary>
Records the web and app activity status of user account. Emitted for clients
in our experiment when Chrome is foregrounded. Recorded at most once in a
one-day moving window. Implemented for Android.
</summary>
</histogram>

<histogram name="Commerce.Subscriptions.TabEligible" enum="Boolean"
expires_after="2022-10-01">
<owner>zhiyuancai@chromium.org</owner>
Expand Down

0 comments on commit dffd177

Please sign in to comment.