diff options
| author | 2019-02-01 16:51:22 -0500 | |
|---|---|---|
| committer | 2019-02-06 18:26:52 +0000 | |
| commit | 7e2115dc08f6b82c5fe886bede7bbdd6e1f39a1c (patch) | |
| tree | ab9577b8fb913edb8286ef883fccc499d3c8c993 | |
| parent | 67d3d8ba221df6ff0a09888ef2f1145643b612d7 (diff) | |
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
5 files changed, 177 insertions, 16 deletions
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 0879af3df32a..119a015cd5ea 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -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)); } } diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index b7e656bc9278..ee8637d8c773 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -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; + } } diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index b6f56ada445e..3d59835a6719 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -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); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index ec8122fb2e47..f60467bd3df2 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -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); } } } diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index eb8710d6759d..3c910696ba60 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -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 |