diff options
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 |