Adding logging for sharesheets

The idea is to measure new feature success and some logs to track are
missing.

Additionally, as part of the project, it became clear that phone
orientation is currently not logged. While this is something we want to
know in the context of sharesheets, this will also be a valueable
metric for other applications.

Bug: 122511750
Test: Added some tests, but wasn't able to test addition in
ResolverDrawerLayout and for the direct share target logging.

Change-Id: I0a6bc6f94a318ea3cf59bf8233ec33a2ddda80ce
This commit is contained in:
Susi Kharraz-Post
2019-02-01 16:51:22 -05:00
parent 67d3d8ba22
commit 7e2115dc08
5 changed files with 177 additions and 16 deletions

View File

@@ -54,6 +54,7 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -186,9 +187,12 @@ public class ChooserActivity extends ResolverActivity {
private @interface ContentPreviewType {
}
private static final int CONTENT_PREVIEW_IMAGE = 0;
private static final int CONTENT_PREVIEW_FILE = 1;
private static final int CONTENT_PREVIEW_TEXT = 2;
// Starting at 1 since 0 is considered "undefined" for some of the database transformations
// of tron logs.
private static final int CONTENT_PREVIEW_IMAGE = 1;
private static final int CONTENT_PREVIEW_FILE = 2;
private static final int CONTENT_PREVIEW_TEXT = 3;
protected MetricsLogger mMetricsLogger;
private final Handler mChooserHandler = new Handler() {
@Override
@@ -413,11 +417,12 @@ public class ChooserActivity extends ResolverActivity {
}
});
MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN);
mChooserShownTime = System.currentTimeMillis();
final long systemCost = mChooserShownTime - intentReceivedTime;
MetricsLogger.histogram(null, "system_cost_for_smart_sharing", (int) systemCost);
getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)
.addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
.addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) {
final IntentFilter filter = getTargetIntentFilter();
@@ -470,6 +475,9 @@ public class ChooserActivity extends ResolverActivity {
}
int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)
.setSubtype(previewType));
displayContentPreview(previewType, targetIntent);
}
@@ -1180,6 +1188,13 @@ public class ChooserActivity extends ResolverActivity {
}
}
protected MetricsLogger getMetricsLogger() {
if (mMetricsLogger == null) {
mMetricsLogger = new MetricsLogger();
}
return mMetricsLogger;
}
public class ChooserListController extends ResolverListController {
public ChooserListController(Context context,
PackageManager pm,
@@ -1726,6 +1741,8 @@ public class ChooserActivity extends ResolverActivity {
if (show != mShowServiceTargets) {
mShowServiceTargets = show;
notifyDataSetChanged();
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN_DIRECT_TARGET));
}
}

View File

@@ -17,15 +17,12 @@
package com.android.internal.widget;
import com.android.internal.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -45,8 +42,13 @@ import android.view.animation.AnimationUtils;
import android.widget.AbsListView;
import android.widget.OverScroller;
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
public class ResolverDrawerLayout extends ViewGroup {
private static final String TAG = "ResolverDrawerLayout";
private MetricsLogger mMetricsLogger;
/**
* Max width of the whole drawer layout
@@ -496,6 +498,9 @@ public class ResolverDrawerLayout extends ViewGroup {
final boolean isCollapsedNew = newPos != 0;
if (isCollapsedOld != isCollapsedNew) {
onCollapsedChanged(isCollapsedNew);
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_SHARESHEET_COLLAPSED_CHANGED)
.setSubtype(isCollapsedNew ? 1 : 0));
}
postInvalidateOnAnimation();
return dy;
@@ -1037,4 +1042,11 @@ public class ResolverDrawerLayout extends ViewGroup {
dispatchOnDismissed();
}
}
private MetricsLogger getMetricsLogger() {
if (mMetricsLogger == null) {
mMetricsLogger = new MetricsLogger();
}
return mMetricsLogger;
}
}

View File

@@ -27,7 +27,9 @@ import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,6 +45,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.metrics.LogMaker;
import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -51,11 +54,14 @@ import androidx.test.rule.ActivityTestRule;
import com.android.internal.R;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.util.ArrayList;
@@ -66,6 +72,11 @@ import java.util.List;
*/
@RunWith(AndroidJUnit4.class)
public class ChooserActivityTest {
private static final int CONTENT_PREVIEW_IMAGE = 1;
private static final int CONTENT_PREVIEW_FILE = 2;
private static final int CONTENT_PREVIEW_TEXT = 3;
@Rule
public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
new ActivityTestRule<>(ChooserWrapperActivity.class, false,
@@ -402,16 +413,15 @@ public class ChooserActivityTest {
createResolvedComponentsForTestWithOtherProfile(1);
when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(R.id.copy_button)).perform(click());
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
Context.CLIPBOARD_SERVICE);
ClipData clipData = clipboard.getPrimaryClip();
@@ -488,8 +498,8 @@ public class ChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(R.id.content_preview_image_1_large)).check(matches(isDisplayed()));
@@ -498,6 +508,93 @@ public class ChooserActivityTest {
onView(withId(R.id.content_preview_image_3_small)).check(matches(isDisplayed()));
}
@Test
public void testOnCreateLogging() {
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
MetricsLogger mockLogger = sOverrides.metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test"));
waitForIdle();
verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture());
assertThat(logMakerCaptor.getAllValues().get(0).getCategory(),
is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN));
assertThat(logMakerCaptor
.getAllValues().get(0)
.getTaggedData(MetricsProto.MetricsEvent.FIELD_TIME_TO_APP_TARGETS),
is(notNullValue()));
assertThat(logMakerCaptor
.getAllValues().get(0)
.getTaggedData(MetricsProto.MetricsEvent.FIELD_SHARESHEET_MIMETYPE),
is("TestType"));
}
@Test
public void testEmptyPreviewLogging() {
Intent sendIntent = createSendTextIntentWithPreview(null, null);
MetricsLogger mockLogger = sOverrides.metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "empty preview logger test"));
waitForIdle();
verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture());
// First invocation is from onCreate
assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
is(CONTENT_PREVIEW_TEXT));
}
@Test
public void testTitlePreviewLogging() {
Intent sendIntent = createSendTextIntentWithPreview("TestTitle", null);
MetricsLogger mockLogger = sOverrides.metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture());
// First invocation is from onCreate
assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
is(CONTENT_PREVIEW_TEXT));
}
@Test
public void testImagePreviewLogging() {
Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ com.android.frameworks.coretests.R.drawable.test320x240);
ArrayList<Uri> uris = new ArrayList<>();
uris.add(uri);
Intent sendIntent = createSendImageIntentWithPreview(uris);
sOverrides.previewThumbnail = createBitmap();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
MetricsLogger mockLogger = sOverrides.metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture());
// First invocation is from onCreate
assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
is(CONTENT_PREVIEW_IMAGE));
assertThat(logMakerCaptor.getAllValues().get(2).getCategory(),
is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
assertThat(logMakerCaptor.getAllValues().get(2).getSubtype(),
is(CONTENT_PREVIEW_IMAGE));
}
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);

View File

@@ -25,6 +25,8 @@ import android.graphics.Bitmap;
import android.net.Uri;
import android.util.Size;
import com.android.internal.logging.MetricsLogger;
import java.util.function.Function;
public class ChooserWrapperActivity extends ChooserActivity {
@@ -94,6 +96,11 @@ public class ChooserWrapperActivity extends ChooserActivity {
return super.isImageType(mimeType);
}
@Override
protected MetricsLogger getMetricsLogger() {
return sOverrides.metricsLogger;
}
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
@@ -106,6 +113,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
public ResolverListController resolverListController;
public Boolean isVoiceInteraction;
public Bitmap previewThumbnail;
public MetricsLogger metricsLogger;
public void reset() {
onSafelyStartCallback = null;
@@ -113,6 +121,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
createPackageManager = null;
previewThumbnail = null;
resolverListController = mock(ResolverListController.class);
metricsLogger = mock(MetricsLogger.class);
}
}
}

View File

@@ -6949,6 +6949,32 @@ message MetricsEvent {
// OS: Q
NOTIFICATION_SMART_REPLY_MODIFIED_BEFORE_SENDING = 1648;
// CATEGORY: ACTION_ACTIVITY_CHOOSER_SHOWN
// Field to add the mimetype for a ChooserActivity
// OS:Q
FIELD_SHARESHEET_MIMETYPE = 1649;
// CATEGORY: ACTION_ACTIVITY_CHOOSER_SHOWN
// Sharesheet direct targets are ready to show.
// OS:Q
ACTION_ACTIVITY_CHOOSER_SHOWN_DIRECT_TARGET = 1650;
// CATEGORY: ACTION_SHARESHEET_SCROLL
// Sharesheet are either expanded, scrolling through them or compacted again.
// OS:Q
// Subtype 1 means collapsed, 0 expanded
ACTION_SHARESHEET_COLLAPSED_CHANGED = 1651;
// ACTION: Share with screenshot extra
// OS: Q
ACTION_SHARE_WITH_PREVIEW = 1652;
// CATEGORY: ACTION_ACTIVITY_CHOOSER_SHOWN
// OS:Q
// The time elapsed from triggering the share to displaying the app targets
// formerly: histogram system_cost_for_smart_sharing
FIELD_TIME_TO_APP_TARGETS = 1653;
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS