Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7e2115dc authored by Susi Kharraz-Post's avatar Susi Kharraz-Post
Browse files

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
parent 67d3d8ba
Loading
Loading
Loading
Loading
+23 −6
Original line number Diff line number Diff line
@@ -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));
            }
        }

+16 −4
Original line number Diff line number Diff line
@@ -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;
    }
}
+103 −6
Original line number Diff line number Diff line
@@ -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,
@@ -411,7 +422,6 @@ public class ChooserActivityTest {
        waitForIdle();

        onView(withId(R.id.copy_button)).perform(click());

        ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
                Context.CLIPBOARD_SERVICE);
        ClipData clipData = clipboard.getPrimaryClip();
@@ -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);
+9 −0
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
+26 −0
Original line number Diff line number Diff line
@@ -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