diff options
| author | 2022-12-12 16:26:16 -0500 | |
|---|---|---|
| committer | 2022-12-15 16:03:11 +0000 | |
| commit | 9ebd81953ce60236381a068a0b7c52c8164bbfea (patch) | |
| tree | dc6daacf40273cef8dc782f582ce6ccd73436edc /java/src/com | |
| parent | 92160d6e9fb1caa1142599ee252f53b796298be5 (diff) | |
Extract remainining ChooserActivity logging
This especially covers other kinds of logging in ChooserActivity that
hadn't previously been a responsibility of ChooserActivityLogger (e.g.
because they instead went directly through MetricsLogger).
There's a few reasons to make these changes, in rough priority order:
1. Tests can make assertions about the high-level logging methods we
expect to call; the implementation of those methods (in terms of
low-level event streams) is unit-tested for the logger itself.
2. We can encapsulate all the types of loggers (UiEventLogger,
MetricsLogger, etc.) in a single object, which is easier to inject
as a dependency as we continue decomposing ChooserActivity
responsibilities into other components.
3. High-level logger APIs reduce the clutter that clients previously
spent building up the low-level event data that's now abstracted
away.
Test: atest IntentResolverUnitTests
Bug: 202167050
Change-Id: Ib3197bca12ec5ea3c925946c0f6d37a6be19d8fa
Diffstat (limited to 'java/src/com')
| -rw-r--r-- | java/src/com/android/intentresolver/ChooserActivity.java | 178 | ||||
| -rw-r--r-- | java/src/com/android/intentresolver/ChooserActivityLogger.java | 145 |
2 files changed, 181 insertions, 142 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index 4682ec50..6d5304d9 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -58,7 +58,6 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.drawable.Drawable; -import android.metrics.LogMaker; import android.net.Uri; import android.os.Bundle; import android.os.Environment; @@ -74,7 +73,6 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.service.chooser.ChooserTarget; import android.text.TextUtils; -import android.util.HashedStringCache; import android.util.Log; import android.util.Size; import android.util.Slog; @@ -114,7 +112,6 @@ import com.android.intentresolver.widget.ResolverDrawerLayout; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.content.PackageMonitor; -import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FrameworkStatsLog; @@ -186,13 +183,6 @@ public class ChooserActivity extends ResolverActivity implements public static final int TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER = 2; public static final int TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE = 3; - public static final int SELECTION_TYPE_SERVICE = 1; - public static final int SELECTION_TYPE_APP = 2; - public static final int SELECTION_TYPE_STANDARD = 3; - public static final int SELECTION_TYPE_COPY = 4; - public static final int SELECTION_TYPE_NEARBY = 5; - public static final int SELECTION_TYPE_EDIT = 6; - private static final int SCROLL_STATUS_IDLE = 0; private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1; private static final int SCROLL_STATUS_SCROLLING_HORIZONTAL = 2; @@ -251,8 +241,6 @@ public class ChooserActivity extends ResolverActivity implements private SharedPreferences mPinnedSharedPrefs; private static final String PINNED_SHARED_PREFS_NAME = "chooser_pin_settings"; - protected MetricsLogger mMetricsLogger; - private final ExecutorService mBackgroundThreadPoolExecutor = Executors.newFixedThreadPool(5); @Nullable @@ -346,13 +334,8 @@ public class ChooserActivity extends ResolverActivity implements mChooserShownTime = System.currentTimeMillis(); final long systemCost = mChooserShownTime - intentReceivedTime; - - getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN) - .setSubtype(isWorkProfile() ? MetricsEvent.MANAGED_PROFILE : - MetricsEvent.PARENT_PROFILE) - .addTaggedData( - MetricsEvent.FIELD_SHARESHEET_MIMETYPE, mChooserRequest.getTargetType()) - .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost)); + getChooserActivityLogger().logChooserActivityShown( + isWorkProfile(), mChooserRequest.getTargetType(), systemCost); if (mResolverDrawerLayout != null) { mResolverDrawerLayout.addOnLayoutChangeListener(this::handleLayoutChange); @@ -593,7 +576,9 @@ public class ChooserActivity extends ResolverActivity implements if (shouldShowStickyContentPreview() || mChooserMultiProfilePagerAdapter .getCurrentRootAdapter().getSystemRowCount() != 0) { - logActionShareWithPreview(); + getChooserActivityLogger().logActionShareWithPreview( + ChooserContentPreviewUi.findPreferredContentPreview( + getTargetIntent(), getContentResolver(), this::isImageType)); } return postRebuildListInternal(rebuildCompleted); } @@ -682,15 +667,7 @@ public class ChooserActivity extends ResolverActivity implements Context.CLIPBOARD_SERVICE); clipboardManager.setPrimaryClipAsPackage(clipData, getReferrerPackageName()); - // Log share completion via copy - LogMaker targetLogMaker = new LogMaker( - MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET).setSubtype(1); - getMetricsLogger().write(targetLogMaker); - getChooserActivityLogger().logShareTargetSelected( - SELECTION_TYPE_COPY, - "", - -1, - false); + getChooserActivityLogger().logActionSelected(ChooserActivityLogger.SELECTION_TYPE_COPY); setResult(RESULT_OK); finish(); @@ -954,12 +931,8 @@ public class ChooserActivity extends ResolverActivity implements ti.getDisplayIconHolder().getDisplayIcon(), ti.getDisplayLabel(), (View unused) -> { - // Log share completion via nearby - getChooserActivityLogger().logShareTargetSelected( - SELECTION_TYPE_NEARBY, - "", - -1, - false); + getChooserActivityLogger().logActionSelected( + ChooserActivityLogger.SELECTION_TYPE_NEARBY); // Action bar is user-independent, always start as primary safelyStartActivityAsUser(ti, getPersonalProfileUserHandle()); finish(); @@ -978,11 +951,8 @@ public class ChooserActivity extends ResolverActivity implements ti.getDisplayLabel(), (View unused) -> { // Log share completion via edit - getChooserActivityLogger().logShareTargetSelected( - SELECTION_TYPE_EDIT, - "", - -1, - false); + getChooserActivityLogger().logActionSelected( + ChooserActivityLogger.SELECTION_TYPE_EDIT); View firstImgView = getFirstVisibleImgPreviewView(); // Action bar is user-independent, always start as primary if (firstImgView == null) { @@ -1032,14 +1002,6 @@ public class ChooserActivity extends ResolverActivity implements return mimeType != null && mimeType.startsWith("image/"); } - private void logContentPreviewWarning(Uri uri) { - // The ContentResolver already logs the exception. Log something more informative. - Log.w(TAG, "Could not load (" + uri.toString() + ") thumbnail/name for preview. If " - + "desired, consider using Intent#createChooser to launch the ChooserActivity, " - + "and set your Intent's clipData and flags in accordance with that method's " - + "documentation"); - } - private int getNumSheetExpansions() { return getPreferences(Context.MODE_PRIVATE).getInt(PREF_NUM_SHEET_EXPANSIONS, 0); } @@ -1249,78 +1211,51 @@ public class ChooserActivity extends ResolverActivity implements super.startSelected(which, always, filtered); if (currentListAdapter.getCount() > 0) { - // Log the index of which type of target the user picked. - // Lower values mean the ranking was better. - int cat = 0; - int value = which; - int directTargetAlsoRanked = -1; - int numCallerProvided = 0; - HashedStringCache.HashResult directTargetHashed = null; switch (currentListAdapter.getPositionTargetType(which)) { case ChooserListAdapter.TARGET_SERVICE: - cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET; - directTargetHashed = targetInfo.getHashedTargetIdForMetrics(this); - directTargetAlsoRanked = getRankedPosition(targetInfo); - - numCallerProvided = mChooserRequest.getCallerChooserTargets().size(); getChooserActivityLogger().logShareTargetSelected( - SELECTION_TYPE_SERVICE, + ChooserActivityLogger.SELECTION_TYPE_SERVICE, targetInfo.getResolveInfo().activityInfo.processName, - value, - targetInfo.isPinned() + which, + /* directTargetAlsoRanked= */ getRankedPosition(targetInfo), + mChooserRequest.getCallerChooserTargets().size(), + targetInfo.getHashedTargetIdForMetrics(this), + targetInfo.isPinned(), + mIsSuccessfullySelected, + selectionCost ); - break; + return; case ChooserListAdapter.TARGET_CALLER: case ChooserListAdapter.TARGET_STANDARD: - cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET; - value -= currentListAdapter.getSurfacedTargetInfo().size(); - numCallerProvided = currentListAdapter.getCallerTargetCount(); getChooserActivityLogger().logShareTargetSelected( - SELECTION_TYPE_APP, + ChooserActivityLogger.SELECTION_TYPE_APP, targetInfo.getResolveInfo().activityInfo.processName, - value, - targetInfo.isPinned() + (which - currentListAdapter.getSurfacedTargetInfo().size()), + /* directTargetAlsoRanked= */ -1, + currentListAdapter.getCallerTargetCount(), + /* directTargetHashed= */ null, + targetInfo.isPinned(), + mIsSuccessfullySelected, + selectionCost ); - break; + return; case ChooserListAdapter.TARGET_STANDARD_AZ: - // A-Z targets are unranked standard targets; we use -1 to mark that they - // are from the alphabetical pool. - value = -1; - cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET; + // A-Z targets are unranked standard targets; we use a value of -1 to mark that + // they are from the alphabetical pool. + // TODO: why do we log a different selection type if the -1 value already + // designates the same condition? getChooserActivityLogger().logShareTargetSelected( - SELECTION_TYPE_STANDARD, + ChooserActivityLogger.SELECTION_TYPE_STANDARD, targetInfo.getResolveInfo().activityInfo.processName, - value, - false + /* value= */ -1, + /* directTargetAlsoRanked= */ -1, + /* numCallerProvided= */ 0, + /* directTargetHashed= */ null, + /* isPinned= */ false, + mIsSuccessfullySelected, + selectionCost ); - break; - } - - if (cat != 0) { - LogMaker targetLogMaker = new LogMaker(cat).setSubtype(value); - if (directTargetHashed != null) { - targetLogMaker.addTaggedData( - MetricsEvent.FIELD_HASHED_TARGET_NAME, directTargetHashed.hashedString); - targetLogMaker.addTaggedData( - MetricsEvent.FIELD_HASHED_TARGET_SALT_GEN, - directTargetHashed.saltGeneration); - targetLogMaker.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, - directTargetAlsoRanked); - } - targetLogMaker.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, - numCallerProvided); - getMetricsLogger().write(targetLogMaker); - } - - if (mIsSuccessfullySelected) { - if (DEBUG) { - Log.d(TAG, "User Selection Time Cost is " + selectionCost); - Log.d(TAG, "position of selected app/service/caller is " + - Integer.toString(value)); - } - MetricsLogger.histogram(null, "user_selection_cost_for_smart_sharing", - (int) selectionCost); - MetricsLogger.histogram(null, "app_position_for_smart_sharing", value); + return; } } } @@ -1396,15 +1331,14 @@ public class ChooserActivity extends ResolverActivity implements } } - private void logDirectShareTargetReceived(int logCategory, UserHandle forUser) { + private void logDirectShareTargetReceived(UserHandle forUser) { ProfileRecord profileRecord = getProfileRecord(forUser); if (profileRecord == null) { return; } - - final int apiLatency = - (int) (SystemClock.elapsedRealtime() - profileRecord.loadingStartTime); - getMetricsLogger().write(new LogMaker(logCategory).setSubtype(apiLatency)); + getChooserActivityLogger().logDirectShareTargetReceived( + MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER, + (int) (SystemClock.elapsedRealtime() - profileRecord.loadingStartTime)); } void updateModelAndChooserCounts(TargetInfo info) { @@ -1546,13 +1480,6 @@ public class ChooserActivity extends ResolverActivity implements } } - protected MetricsLogger getMetricsLogger() { - if (mMetricsLogger == null) { - mMetricsLogger = new MetricsLogger(); - } - return mMetricsLogger; - } - protected ChooserActivityLogger getChooserActivityLogger() { if (mChooserActivityLogger == null) { mChooserActivityLogger = new ChooserActivityLogger(); @@ -1736,7 +1663,7 @@ public class ChooserActivity extends ResolverActivity implements try { return getContentResolver().loadThumbnail(uri, size, null); } catch (IOException | NullPointerException | SecurityException ex) { - logContentPreviewWarning(uri); + getChooserActivityLogger().logContentPreviewWarning(uri); } return null; } @@ -1996,10 +1923,7 @@ public class ChooserActivity extends ResolverActivity implements adapter.completeServiceTargetLoading(); } - logDirectShareTargetReceived( - MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER, - userHandle); - + logDirectShareTargetReceived(userHandle); sendVoiceChoicesIfNeeded(); getChooserActivityLogger().logSharesheetDirectLoadComplete(); } @@ -2130,14 +2054,6 @@ public class ChooserActivity extends ResolverActivity implements contentPreviewContainer.setVisibility(View.GONE); } - private void logActionShareWithPreview() { - Intent targetIntent = getTargetIntent(); - int previewType = ChooserContentPreviewUi.findPreferredContentPreview( - targetIntent, getContentResolver(), this::isImageType); - getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW) - .setSubtype(previewType)); - } - private void startFinishAnimation() { View rootView = findRootView(); if (rootView != null) { diff --git a/java/src/com/android/intentresolver/ChooserActivityLogger.java b/java/src/com/android/intentresolver/ChooserActivityLogger.java index 811d5f3e..9109bf93 100644 --- a/java/src/com/android/intentresolver/ChooserActivityLogger.java +++ b/java/src/com/android/intentresolver/ChooserActivityLogger.java @@ -16,15 +16,22 @@ package com.android.intentresolver; +import android.annotation.Nullable; import android.content.Intent; +import android.metrics.LogMaker; +import android.net.Uri; import android.provider.MediaStore; +import android.util.HashedStringCache; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; import com.android.internal.logging.InstanceIdSequence; +import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FrameworkStatsLog; /** @@ -32,6 +39,16 @@ import com.android.internal.util.FrameworkStatsLog; * @hide */ public class ChooserActivityLogger { + private static final String TAG = "ChooserActivity"; + private static final boolean DEBUG = true; + + public static final int SELECTION_TYPE_SERVICE = 1; + public static final int SELECTION_TYPE_APP = 2; + public static final int SELECTION_TYPE_STANDARD = 3; + public static final int SELECTION_TYPE_COPY = 4; + public static final int SELECTION_TYPE_NEARBY = 5; + public static final int SELECTION_TYPE_EDIT = 6; + /** * This shim is provided only for testing. In production, clients will only ever use a * {@link DefaultFrameworkStatsLogger}. @@ -70,15 +87,30 @@ public class ChooserActivityLogger { private final UiEventLogger mUiEventLogger; private final FrameworkStatsLogger mFrameworkStatsLogger; + private final MetricsLogger mMetricsLogger; public ChooserActivityLogger() { - this(new UiEventLoggerImpl(), new DefaultFrameworkStatsLogger()); + this(new UiEventLoggerImpl(), new DefaultFrameworkStatsLogger(), new MetricsLogger()); } @VisibleForTesting - ChooserActivityLogger(UiEventLogger uiEventLogger, FrameworkStatsLogger frameworkLogger) { + ChooserActivityLogger( + UiEventLogger uiEventLogger, + FrameworkStatsLogger frameworkLogger, + MetricsLogger metricsLogger) { mUiEventLogger = uiEventLogger; mFrameworkStatsLogger = frameworkLogger; + mMetricsLogger = metricsLogger; + } + + /** Records metrics for the start time of the {@link ChooserActivity}. */ + public void logChooserActivityShown( + boolean isWorkProfile, String targetMimeType, long systemCost) { + mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN) + .setSubtype( + isWorkProfile ? MetricsEvent.MANAGED_PROFILE : MetricsEvent.PARENT_PROFILE) + .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, targetMimeType) + .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost)); } /** Logs a UiEventReported event for the system sharesheet completing initial start-up. */ @@ -97,15 +129,92 @@ public class ChooserActivityLogger { /* intentType = 9 */ typeFromIntentString(intent)); } - /** Logs a UiEventReported event for the system sharesheet when the user selects a target. */ - public void logShareTargetSelected(int targetType, String packageName, int positionPicked, - boolean isPinned) { + /** + * Logs a UiEventReported event for the system sharesheet when the user selects a target. + * TODO: document parameters and/or consider breaking up by targetType so we don't have to + * support an overly-generic signature. + */ + public void logShareTargetSelected( + int targetType, + String packageName, + int positionPicked, + int directTargetAlsoRanked, + int numCallerProvided, + @Nullable HashedStringCache.HashResult directTargetHashed, + boolean isPinned, + boolean successfullySelected, + long selectionCost) { mFrameworkStatsLogger.write(FrameworkStatsLog.RANKING_SELECTED, /* event_id = 1 */ SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), /* package_name = 2 */ packageName, /* instance_id = 3 */ getInstanceId().getId(), /* position_picked = 4 */ positionPicked, /* is_pinned = 5 */ isPinned); + + int category = getTargetSelectionCategory(targetType); + if (category != 0) { + LogMaker targetLogMaker = new LogMaker(category).setSubtype(positionPicked); + if (directTargetHashed != null) { + targetLogMaker.addTaggedData( + MetricsEvent.FIELD_HASHED_TARGET_NAME, directTargetHashed.hashedString); + targetLogMaker.addTaggedData( + MetricsEvent.FIELD_HASHED_TARGET_SALT_GEN, + directTargetHashed.saltGeneration); + targetLogMaker.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, + directTargetAlsoRanked); + } + targetLogMaker.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, numCallerProvided); + mMetricsLogger.write(targetLogMaker); + } + + if (successfullySelected) { + if (DEBUG) { + Log.d(TAG, "User Selection Time Cost is " + selectionCost); + Log.d(TAG, "position of selected app/service/caller is " + positionPicked); + } + MetricsLogger.histogram( + null, "user_selection_cost_for_smart_sharing", (int) selectionCost); + MetricsLogger.histogram(null, "app_position_for_smart_sharing", positionPicked); + } + } + + /** Log when direct share targets were received. */ + public void logDirectShareTargetReceived(int category, int latency) { + mMetricsLogger.write(new LogMaker(category).setSubtype(latency)); + } + + /** + * Log when we display a preview UI of the specified {@code previewType} as part of our + * Sharesheet session. + */ + public void logActionShareWithPreview(int previewType) { + mMetricsLogger.write( + new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW).setSubtype(previewType)); + } + + /** Log when the user selects an action button with the specified {@code targetType}. */ + public void logActionSelected(int targetType) { + if (targetType == SELECTION_TYPE_COPY) { + LogMaker targetLogMaker = new LogMaker( + MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET).setSubtype(1); + mMetricsLogger.write(targetLogMaker); + } + mFrameworkStatsLogger.write(FrameworkStatsLog.RANKING_SELECTED, + /* event_id = 1 */ SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), + /* package_name = 2 */ "", + /* instance_id = 3 */ getInstanceId().getId(), + /* position_picked = 4 */ -1, + /* is_pinned = 5 */ false); + } + + /** Log a warning that we couldn't display the content preview from the supplied {@code uri}. */ + public void logContentPreviewWarning(Uri uri) { + // The ContentResolver already logs the exception. Log something more informative. + Log.w(TAG, "Could not load (" + uri.toString() + ") thumbnail/name for preview. If " + + "desired, consider using Intent#createChooser to launch the ChooserActivity, " + + "and set your Intent's clipData and flags in accordance with that method's " + + "documentation"); + } /** Logs a UiEventReported event for the system sharesheet being triggered by the user. */ @@ -231,17 +340,17 @@ public class ChooserActivityLogger { public static SharesheetTargetSelectedEvent fromTargetType(int targetType) { switch(targetType) { - case ChooserActivity.SELECTION_TYPE_SERVICE: + case SELECTION_TYPE_SERVICE: return SHARESHEET_SERVICE_TARGET_SELECTED; - case ChooserActivity.SELECTION_TYPE_APP: + case SELECTION_TYPE_APP: return SHARESHEET_APP_TARGET_SELECTED; - case ChooserActivity.SELECTION_TYPE_STANDARD: + case SELECTION_TYPE_STANDARD: return SHARESHEET_STANDARD_TARGET_SELECTED; - case ChooserActivity.SELECTION_TYPE_COPY: + case SELECTION_TYPE_COPY: return SHARESHEET_COPY_TARGET_SELECTED; - case ChooserActivity.SELECTION_TYPE_NEARBY: + case SELECTION_TYPE_NEARBY: return SHARESHEET_NEARBY_TARGET_SELECTED; - case ChooserActivity.SELECTION_TYPE_EDIT: + case SELECTION_TYPE_EDIT: return SHARESHEET_EDIT_TARGET_SELECTED; default: return INVALID; @@ -328,6 +437,20 @@ public class ChooserActivityLogger { } } + @VisibleForTesting + static int getTargetSelectionCategory(int targetType) { + switch (targetType) { + case SELECTION_TYPE_SERVICE: + return MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET; + case SELECTION_TYPE_APP: + return MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET; + case SELECTION_TYPE_STANDARD: + return MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET; + default: + return 0; + } + } + private static class DefaultFrameworkStatsLogger implements FrameworkStatsLogger { @Override public void write( |