From 4fe01c7ca647194636c592949516677e68fc4a80 Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Wed, 13 Sep 2023 11:10:10 -0400 Subject: Extract an interface for EventLog, rename implementation This is a purely automated refactor with no functional changes. Test: atest IntentResolverUnitTests Bug: 300157408 Flag: EXEMPT Change-Id: I7440a6bb1035999f202e799402f09bcb1d657ecc --- .../intentresolver/ChooserActionFactory.java | 26 +- .../android/intentresolver/ChooserActivity.java | 3 +- .../android/intentresolver/logging/EventLog.java | 539 --------------------- .../com/android/intentresolver/logging/EventLog.kt | 74 +++ .../intentresolver/logging/EventLogImpl.java | 514 ++++++++++++++++++++ .../intentresolver/logging/FrameworkStatsLogger.kt | 50 ++ .../model/AbstractResolverComparator.java | 4 +- .../AppPredictionServiceResolverComparator.java | 2 +- .../ResolverRankerServiceResolverComparator.java | 13 +- .../intentresolver/ChooserActionFactoryTest.kt | 3 +- .../ChooserActivityOverrideData.java | 6 +- .../intentresolver/ChooserListAdapterTest.kt | 4 +- .../intentresolver/ChooserWrapperActivity.java | 4 +- .../android/intentresolver/IChooserWrapper.java | 4 +- .../UnbundledChooserActivityTest.java | 11 +- .../intentresolver/logging/EventLogImplTest.java | 421 ++++++++++++++++ .../intentresolver/logging/EventLogTest.java | 422 ---------------- 17 files changed, 1101 insertions(+), 999 deletions(-) delete mode 100644 java/src/com/android/intentresolver/logging/EventLog.java create mode 100644 java/src/com/android/intentresolver/logging/EventLog.kt create mode 100644 java/src/com/android/intentresolver/logging/EventLogImpl.java create mode 100644 java/src/com/android/intentresolver/logging/FrameworkStatsLogger.kt create mode 100644 java/tests/src/com/android/intentresolver/logging/EventLogImplTest.java delete mode 100644 java/tests/src/com/android/intentresolver/logging/EventLogTest.java (limited to 'java') diff --git a/java/src/com/android/intentresolver/ChooserActionFactory.java b/java/src/com/android/intentresolver/ChooserActionFactory.java index a54e8c62..2c97c0b1 100644 --- a/java/src/com/android/intentresolver/ChooserActionFactory.java +++ b/java/src/com/android/intentresolver/ChooserActionFactory.java @@ -98,7 +98,7 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio private final @Nullable ChooserAction mModifyShareAction; private final Consumer mExcludeSharedTextAction; private final Consumer mFinishCallback; - private final EventLog mLogger; + private final EventLog mLog; /** * @param context @@ -117,7 +117,7 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio Context context, ChooserRequestParameters chooserRequest, ChooserIntegratedDeviceComponents integratedDeviceComponents, - EventLog logger, + EventLog log, Consumer onUpdateSharedTextIsExcluded, Callable firstVisibleImageQuery, ActionActivityStarter activityStarter, @@ -129,7 +129,7 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio chooserRequest.getTargetIntent(), chooserRequest.getReferrerPackageName(), finishCallback, - logger), + log), makeEditButtonRunnable( getEditSharingTarget( context, @@ -137,11 +137,11 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio integratedDeviceComponents), firstVisibleImageQuery, activityStarter, - logger), + log), chooserRequest.getChooserActions(), chooserRequest.getModifyShareAction(), onUpdateSharedTextIsExcluded, - logger, + log, finishCallback); } @@ -153,7 +153,7 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio List customActions, @Nullable ChooserAction modifyShareAction, Consumer onUpdateSharedTextIsExcluded, - EventLog logger, + EventLog log, Consumer finishCallback) { mContext = context; mCopyButtonRunnable = copyButtonRunnable; @@ -161,7 +161,7 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio mCustomActions = ImmutableList.copyOf(customActions); mModifyShareAction = modifyShareAction; mExcludeSharedTextAction = onUpdateSharedTextIsExcluded; - mLogger = logger; + mLog = log; mFinishCallback = finishCallback; } @@ -188,7 +188,7 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio mCustomActions.get(i), mFinishCallback, () -> { - mLogger.logCustomActionSelected(position); + mLog.logCustomActionSelected(position); } ); if (actionRow != null) { @@ -209,7 +209,7 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio mModifyShareAction, mFinishCallback, () -> { - mLogger.logActionSelected(EventLog.SELECTION_TYPE_MODIFY_SHARE); + mLog.logActionSelected(EventLog.SELECTION_TYPE_MODIFY_SHARE); }); } @@ -233,7 +233,7 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio Intent targetIntent, String referrerPackageName, Consumer finishCallback, - EventLog logger) { + EventLog log) { final ClipData clipData; try { clipData = extractTextToCopy(targetIntent); @@ -249,7 +249,7 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio Context.CLIPBOARD_SERVICE); clipboardManager.setPrimaryClipAsPackage(clipData, referrerPackageName); - logger.logActionSelected(EventLog.SELECTION_TYPE_COPY); + log.logActionSelected(EventLog.SELECTION_TYPE_COPY); finishCallback.accept(Activity.RESULT_OK); }; } @@ -328,10 +328,10 @@ public final class ChooserActionFactory implements ChooserContentPreviewUi.Actio TargetInfo editSharingTarget, Callable firstVisibleImageQuery, ActionActivityStarter activityStarter, - EventLog logger) { + EventLog log) { return () -> { // Log share completion via edit. - logger.logActionSelected(EventLog.SELECTION_TYPE_EDIT); + log.logActionSelected(EventLog.SELECTION_TYPE_EDIT); View firstImageView = null; try { diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index ebe6f04b..0a4f9f46 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -85,6 +85,7 @@ import com.android.intentresolver.grid.ChooserGridAdapter; import com.android.intentresolver.icons.DefaultTargetDataLoader; import com.android.intentresolver.icons.TargetDataLoader; import com.android.intentresolver.logging.EventLog; +import com.android.intentresolver.logging.EventLogImpl; import com.android.intentresolver.measurements.Tracer; import com.android.intentresolver.model.AbstractResolverComparator; import com.android.intentresolver.model.AppPredictionServiceResolverComparator; @@ -1111,7 +1112,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements protected EventLog getEventLog() { if (mEventLog == null) { - mEventLog = new EventLog(); + mEventLog = new EventLogImpl(); } return mEventLog; } diff --git a/java/src/com/android/intentresolver/logging/EventLog.java b/java/src/com/android/intentresolver/logging/EventLog.java deleted file mode 100644 index b30e825b..00000000 --- a/java/src/com/android/intentresolver/logging/EventLog.java +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.intentresolver.logging; - -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.intentresolver.ChooserActivity; -import com.android.intentresolver.contentpreview.ContentPreviewType; -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; - -/** - * Helper for writing Sharesheet atoms to statsd log. - * @hide - */ -public class EventLog { - 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; - public static final int SELECTION_TYPE_MODIFY_SHARE = 7; - public static final int SELECTION_TYPE_CUSTOM_ACTION = 8; - - /** - * This shim is provided only for testing. In production, clients will only ever use a - * {@link DefaultFrameworkStatsLogger}. - */ - @VisibleForTesting - interface FrameworkStatsLogger { - /** Overload to use for logging {@code FrameworkStatsLog.SHARESHEET_STARTED}. */ - void write( - int frameworkEventId, - int appEventId, - String packageName, - int instanceId, - String mimeType, - int numAppProvidedDirectTargets, - int numAppProvidedAppTargets, - boolean isWorkProfile, - int previewType, - int intentType, - int numCustomActions, - boolean modifyShareActionProvided); - - /** Overload to use for logging {@code FrameworkStatsLog.RANKING_SELECTED}. */ - void write( - int frameworkEventId, - int appEventId, - String packageName, - int instanceId, - int positionPicked, - boolean isPinned); - } - - private static final int SHARESHEET_INSTANCE_ID_MAX = (1 << 13); - - // A small per-notification ID, used for statsd logging. - // TODO: consider precomputing and storing as final. - private static InstanceIdSequence sInstanceIdSequence; - private InstanceId mInstanceId; - - private final UiEventLogger mUiEventLogger; - private final FrameworkStatsLogger mFrameworkStatsLogger; - private final MetricsLogger mMetricsLogger; - - public EventLog() { - this(new UiEventLoggerImpl(), new DefaultFrameworkStatsLogger(), new MetricsLogger()); - } - - @VisibleForTesting - EventLog( - 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. */ - public void logShareStarted( - String packageName, - String mimeType, - int appProvidedDirect, - int appProvidedApp, - boolean isWorkprofile, - int previewType, - String intent, - int customActionCount, - boolean modifyShareActionProvided) { - mFrameworkStatsLogger.write(FrameworkStatsLog.SHARESHEET_STARTED, - /* event_id = 1 */ SharesheetStartedEvent.SHARE_STARTED.getId(), - /* package_name = 2 */ packageName, - /* instance_id = 3 */ getInstanceId().getId(), - /* mime_type = 4 */ mimeType, - /* num_app_provided_direct_targets = 5 */ appProvidedDirect, - /* num_app_provided_app_targets = 6 */ appProvidedApp, - /* is_workprofile = 7 */ isWorkprofile, - /* previewType = 8 */ typeFromPreviewInt(previewType), - /* intentType = 9 */ typeFromIntentString(intent), - /* num_provided_custom_actions = 10 */ customActionCount, - /* modify_share_action_provided = 11 */ modifyShareActionProvided); - } - - /** - * Log that a custom action has been tapped by the user. - * - * @param positionPicked index of the custom action within the list of custom actions. - */ - public void logCustomActionSelected(int positionPicked) { - mFrameworkStatsLogger.write(FrameworkStatsLog.RANKING_SELECTED, - /* event_id = 1 */ - SharesheetTargetSelectedEvent.SHARESHEET_CUSTOM_ACTION_SELECTED.getId(), - /* package_name = 2 */ null, - /* instance_id = 3 */ getInstanceId().getId(), - /* position_picked = 4 */ positionPicked, - /* is_pinned = 5 */ false); - } - - /** - * 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. */ - public void logSharesheetTriggered() { - log(SharesheetStandardEvent.SHARESHEET_TRIGGERED, getInstanceId()); - } - - /** Logs a UiEventReported event for the system sharesheet completing loading app targets. */ - public void logSharesheetAppLoadComplete() { - log(SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE, getInstanceId()); - } - - /** - * Logs a UiEventReported event for the system sharesheet completing loading service targets. - */ - public void logSharesheetDirectLoadComplete() { - log(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE, getInstanceId()); - } - - /** - * Logs a UiEventReported event for the system sharesheet timing out loading service targets. - */ - public void logSharesheetDirectLoadTimeout() { - log(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_TIMEOUT, getInstanceId()); - } - - /** - * Logs a UiEventReported event for the system sharesheet switching - * between work and main profile. - */ - public void logSharesheetProfileChanged() { - log(SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED, getInstanceId()); - } - - /** Logs a UiEventReported event for the system sharesheet getting expanded or collapsed. */ - public void logSharesheetExpansionChanged(boolean isCollapsed) { - log(isCollapsed ? SharesheetStandardEvent.SHARESHEET_COLLAPSED : - SharesheetStandardEvent.SHARESHEET_EXPANDED, getInstanceId()); - } - - /** - * Logs a UiEventReported event for the system sharesheet app share ranking timing out. - */ - public void logSharesheetAppShareRankingTimeout() { - log(SharesheetStandardEvent.SHARESHEET_APP_SHARE_RANKING_TIMEOUT, getInstanceId()); - } - - /** - * Logs a UiEventReported event for the system sharesheet when direct share row is empty. - */ - public void logSharesheetEmptyDirectShareRow() { - log(SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW, getInstanceId()); - } - - /** - * Logs a UiEventReported event for a given share activity - * @param event - * @param instanceId - */ - private void log(UiEventLogger.UiEventEnum event, InstanceId instanceId) { - mUiEventLogger.logWithInstanceId( - event, - 0, - null, - instanceId); - } - - /** - * @return A unique {@link InstanceId} to join across events recorded by this logger instance. - */ - private InstanceId getInstanceId() { - if (mInstanceId == null) { - if (sInstanceIdSequence == null) { - sInstanceIdSequence = new InstanceIdSequence(SHARESHEET_INSTANCE_ID_MAX); - } - mInstanceId = sInstanceIdSequence.newInstanceId(); - } - return mInstanceId; - } - - /** - * The UiEvent enums that this class can log. - */ - enum SharesheetStartedEvent implements UiEventLogger.UiEventEnum { - @UiEvent(doc = "Basic system Sharesheet has started and is visible.") - SHARE_STARTED(228); - - private final int mId; - SharesheetStartedEvent(int id) { - mId = id; - } - @Override - public int getId() { - return mId; - } - } - - /** - * The UiEvent enums that this class can log. - */ - enum SharesheetTargetSelectedEvent implements UiEventLogger.UiEventEnum { - INVALID(0), - @UiEvent(doc = "User selected a service target.") - SHARESHEET_SERVICE_TARGET_SELECTED(232), - @UiEvent(doc = "User selected an app target.") - SHARESHEET_APP_TARGET_SELECTED(233), - @UiEvent(doc = "User selected a standard target.") - SHARESHEET_STANDARD_TARGET_SELECTED(234), - @UiEvent(doc = "User selected the copy target.") - SHARESHEET_COPY_TARGET_SELECTED(235), - @UiEvent(doc = "User selected the nearby target.") - SHARESHEET_NEARBY_TARGET_SELECTED(626), - @UiEvent(doc = "User selected the edit target.") - SHARESHEET_EDIT_TARGET_SELECTED(669), - @UiEvent(doc = "User selected the modify share target.") - SHARESHEET_MODIFY_SHARE_SELECTED(1316), - @UiEvent(doc = "User selected a custom action.") - SHARESHEET_CUSTOM_ACTION_SELECTED(1317); - - private final int mId; - SharesheetTargetSelectedEvent(int id) { - mId = id; - } - @Override public int getId() { - return mId; - } - - public static SharesheetTargetSelectedEvent fromTargetType(int targetType) { - switch(targetType) { - case SELECTION_TYPE_SERVICE: - return SHARESHEET_SERVICE_TARGET_SELECTED; - case SELECTION_TYPE_APP: - return SHARESHEET_APP_TARGET_SELECTED; - case SELECTION_TYPE_STANDARD: - return SHARESHEET_STANDARD_TARGET_SELECTED; - case SELECTION_TYPE_COPY: - return SHARESHEET_COPY_TARGET_SELECTED; - case SELECTION_TYPE_NEARBY: - return SHARESHEET_NEARBY_TARGET_SELECTED; - case SELECTION_TYPE_EDIT: - return SHARESHEET_EDIT_TARGET_SELECTED; - case SELECTION_TYPE_MODIFY_SHARE: - return SHARESHEET_MODIFY_SHARE_SELECTED; - case SELECTION_TYPE_CUSTOM_ACTION: - return SHARESHEET_CUSTOM_ACTION_SELECTED; - default: - return INVALID; - } - } - } - - /** - * The UiEvent enums that this class can log. - */ - enum SharesheetStandardEvent implements UiEventLogger.UiEventEnum { - INVALID(0), - @UiEvent(doc = "User clicked share.") - SHARESHEET_TRIGGERED(227), - @UiEvent(doc = "User changed from work to personal profile or vice versa.") - SHARESHEET_PROFILE_CHANGED(229), - @UiEvent(doc = "User expanded target list.") - SHARESHEET_EXPANDED(230), - @UiEvent(doc = "User collapsed target list.") - SHARESHEET_COLLAPSED(231), - @UiEvent(doc = "Sharesheet app targets is fully populated.") - SHARESHEET_APP_LOAD_COMPLETE(322), - @UiEvent(doc = "Sharesheet direct targets is fully populated.") - SHARESHEET_DIRECT_LOAD_COMPLETE(323), - @UiEvent(doc = "Sharesheet direct targets timed out.") - SHARESHEET_DIRECT_LOAD_TIMEOUT(324), - @UiEvent(doc = "Sharesheet app share ranking timed out.") - SHARESHEET_APP_SHARE_RANKING_TIMEOUT(831), - @UiEvent(doc = "Sharesheet empty direct share row.") - SHARESHEET_EMPTY_DIRECT_SHARE_ROW(828); - - private final int mId; - SharesheetStandardEvent(int id) { - mId = id; - } - @Override public int getId() { - return mId; - } - } - - /** - * Returns the enum used in sharesheet started atom to indicate what preview type was used. - */ - private static int typeFromPreviewInt(int previewType) { - switch(previewType) { - case ContentPreviewType.CONTENT_PREVIEW_IMAGE: - return FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_IMAGE; - case ContentPreviewType.CONTENT_PREVIEW_FILE: - return FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_FILE; - case ContentPreviewType.CONTENT_PREVIEW_TEXT: - default: - return FrameworkStatsLog - .SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_TYPE_UNKNOWN; - } - } - - /** - * Returns the enum used in sharesheet started atom to indicate what intent triggers the - * ChooserActivity. - */ - private static int typeFromIntentString(String intent) { - if (intent == null) { - return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_DEFAULT; - } - switch (intent) { - case Intent.ACTION_VIEW: - return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_VIEW; - case Intent.ACTION_EDIT: - return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_EDIT; - case Intent.ACTION_SEND: - return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SEND; - case Intent.ACTION_SENDTO: - return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO; - case Intent.ACTION_SEND_MULTIPLE: - return FrameworkStatsLog - .SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SEND_MULTIPLE; - case MediaStore.ACTION_IMAGE_CAPTURE: - return FrameworkStatsLog - .SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_IMAGE_CAPTURE; - case Intent.ACTION_MAIN: - return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_MAIN; - default: - return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_DEFAULT; - } - } - - @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( - int frameworkEventId, - int appEventId, - String packageName, - int instanceId, - String mimeType, - int numAppProvidedDirectTargets, - int numAppProvidedAppTargets, - boolean isWorkProfile, - int previewType, - int intentType, - int numCustomActions, - boolean modifyShareActionProvided) { - FrameworkStatsLog.write( - frameworkEventId, - /* event_id = 1 */ appEventId, - /* package_name = 2 */ packageName, - /* instance_id = 3 */ instanceId, - /* mime_type = 4 */ mimeType, - /* num_app_provided_direct_targets */ numAppProvidedDirectTargets, - /* num_app_provided_app_targets */ numAppProvidedAppTargets, - /* is_workprofile */ isWorkProfile, - /* previewType = 8 */ previewType, - /* intentType = 9 */ intentType, - /* num_provided_custom_actions = 10 */ numCustomActions, - /* modify_share_action_provided = 11 */ modifyShareActionProvided); - } - - @Override - public void write( - int frameworkEventId, - int appEventId, - String packageName, - int instanceId, - int positionPicked, - boolean isPinned) { - FrameworkStatsLog.write( - frameworkEventId, - /* event_id = 1 */ appEventId, - /* package_name = 2 */ packageName, - /* instance_id = 3 */ instanceId, - /* position_picked = 4 */ positionPicked, - /* is_pinned = 5 */ isPinned); - } - } -} diff --git a/java/src/com/android/intentresolver/logging/EventLog.kt b/java/src/com/android/intentresolver/logging/EventLog.kt new file mode 100644 index 00000000..476bd4bf --- /dev/null +++ b/java/src/com/android/intentresolver/logging/EventLog.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.intentresolver.logging + +import android.net.Uri +import android.util.HashedStringCache + +/** Logs notable events during ShareSheet usage. */ +interface EventLog { + + companion object { + const val SELECTION_TYPE_SERVICE = 1 + const val SELECTION_TYPE_APP = 2 + const val SELECTION_TYPE_STANDARD = 3 + const val SELECTION_TYPE_COPY = 4 + const val SELECTION_TYPE_NEARBY = 5 + const val SELECTION_TYPE_EDIT = 6 + const val SELECTION_TYPE_MODIFY_SHARE = 7 + const val SELECTION_TYPE_CUSTOM_ACTION = 8 + } + + fun logChooserActivityShown(isWorkProfile: Boolean, targetMimeType: String?, systemCost: Long) + + fun logShareStarted( + packageName: String?, + mimeType: String?, + appProvidedDirect: Int, + appProvidedApp: Int, + isWorkprofile: Boolean, + previewType: Int, + intent: String?, + customActionCount: Int, + modifyShareActionProvided: Boolean + ) + + fun logCustomActionSelected(positionPicked: Int) + fun logShareTargetSelected( + targetType: Int, + packageName: String?, + positionPicked: Int, + directTargetAlsoRanked: Int, + numCallerProvided: Int, + directTargetHashed: HashedStringCache.HashResult?, + isPinned: Boolean, + successfullySelected: Boolean, + selectionCost: Long + ) + + fun logDirectShareTargetReceived(category: Int, latency: Int) + fun logActionShareWithPreview(previewType: Int) + fun logActionSelected(targetType: Int) + fun logContentPreviewWarning(uri: Uri?) + fun logSharesheetTriggered() + fun logSharesheetAppLoadComplete() + fun logSharesheetDirectLoadComplete() + fun logSharesheetDirectLoadTimeout() + fun logSharesheetProfileChanged() + fun logSharesheetExpansionChanged(isCollapsed: Boolean) + fun logSharesheetAppShareRankingTimeout() + fun logSharesheetEmptyDirectShareRow() +} diff --git a/java/src/com/android/intentresolver/logging/EventLogImpl.java b/java/src/com/android/intentresolver/logging/EventLogImpl.java new file mode 100644 index 00000000..33e617b1 --- /dev/null +++ b/java/src/com/android/intentresolver/logging/EventLogImpl.java @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver.logging; + +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.intentresolver.ChooserActivity; +import com.android.intentresolver.contentpreview.ContentPreviewType; +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; + +/** + * Helper for writing Sharesheet atoms to statsd log. + */ +public class EventLogImpl implements EventLog { + private static final String TAG = "ChooserActivity"; + private static final boolean DEBUG = true; + + private static final int SHARESHEET_INSTANCE_ID_MAX = (1 << 13); + + // A small per-notification ID, used for statsd logging. + // TODO: consider precomputing and storing as final. + private static InstanceIdSequence sInstanceIdSequence; + private InstanceId mInstanceId; + + private final UiEventLogger mUiEventLogger; + private final FrameworkStatsLogger mFrameworkStatsLogger; + private final MetricsLogger mMetricsLogger; + + public EventLogImpl() { + this(new UiEventLoggerImpl(), new DefaultFrameworkStatsLogger(), new MetricsLogger()); + } + + @VisibleForTesting + EventLogImpl( + UiEventLogger uiEventLogger, + FrameworkStatsLogger frameworkLogger, + MetricsLogger metricsLogger) { + mUiEventLogger = uiEventLogger; + mFrameworkStatsLogger = frameworkLogger; + mMetricsLogger = metricsLogger; + } + + /** Records metrics for the start time of the {@link ChooserActivity}. */ + @Override + 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. */ + @Override + public void logShareStarted( + String packageName, + String mimeType, + int appProvidedDirect, + int appProvidedApp, + boolean isWorkprofile, + int previewType, + String intent, + int customActionCount, + boolean modifyShareActionProvided) { + mFrameworkStatsLogger.write(FrameworkStatsLog.SHARESHEET_STARTED, + /* event_id = 1 */ SharesheetStartedEvent.SHARE_STARTED.getId(), + /* package_name = 2 */ packageName, + /* instance_id = 3 */ getInstanceId().getId(), + /* mime_type = 4 */ mimeType, + /* num_app_provided_direct_targets = 5 */ appProvidedDirect, + /* num_app_provided_app_targets = 6 */ appProvidedApp, + /* is_workprofile = 7 */ isWorkprofile, + /* previewType = 8 */ typeFromPreviewInt(previewType), + /* intentType = 9 */ typeFromIntentString(intent), + /* num_provided_custom_actions = 10 */ customActionCount, + /* modify_share_action_provided = 11 */ modifyShareActionProvided); + } + + /** + * Log that a custom action has been tapped by the user. + * + * @param positionPicked index of the custom action within the list of custom actions. + */ + @Override + public void logCustomActionSelected(int positionPicked) { + mFrameworkStatsLogger.write(FrameworkStatsLog.RANKING_SELECTED, + /* event_id = 1 */ + SharesheetTargetSelectedEvent.SHARESHEET_CUSTOM_ACTION_SELECTED.getId(), + /* package_name = 2 */ null, + /* instance_id = 3 */ getInstanceId().getId(), + /* position_picked = 4 */ positionPicked, + /* is_pinned = 5 */ false); + } + + /** + * 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. + */ + @Override + 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. */ + @Override + 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. + */ + @Override + 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}. */ + @Override + 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}. */ + @Override + 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. */ + @Override + public void logSharesheetTriggered() { + log(SharesheetStandardEvent.SHARESHEET_TRIGGERED, getInstanceId()); + } + + /** Logs a UiEventReported event for the system sharesheet completing loading app targets. */ + @Override + public void logSharesheetAppLoadComplete() { + log(SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE, getInstanceId()); + } + + /** + * Logs a UiEventReported event for the system sharesheet completing loading service targets. + */ + @Override + public void logSharesheetDirectLoadComplete() { + log(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE, getInstanceId()); + } + + /** + * Logs a UiEventReported event for the system sharesheet timing out loading service targets. + */ + @Override + public void logSharesheetDirectLoadTimeout() { + log(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_TIMEOUT, getInstanceId()); + } + + /** + * Logs a UiEventReported event for the system sharesheet switching + * between work and main profile. + */ + @Override + public void logSharesheetProfileChanged() { + log(SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED, getInstanceId()); + } + + /** Logs a UiEventReported event for the system sharesheet getting expanded or collapsed. */ + @Override + public void logSharesheetExpansionChanged(boolean isCollapsed) { + log(isCollapsed ? SharesheetStandardEvent.SHARESHEET_COLLAPSED : + SharesheetStandardEvent.SHARESHEET_EXPANDED, getInstanceId()); + } + + /** + * Logs a UiEventReported event for the system sharesheet app share ranking timing out. + */ + @Override + public void logSharesheetAppShareRankingTimeout() { + log(SharesheetStandardEvent.SHARESHEET_APP_SHARE_RANKING_TIMEOUT, getInstanceId()); + } + + /** + * Logs a UiEventReported event for the system sharesheet when direct share row is empty. + */ + @Override + public void logSharesheetEmptyDirectShareRow() { + log(SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW, getInstanceId()); + } + + /** + * Logs a UiEventReported event for a given share activity + * @param event + * @param instanceId + */ + private void log(UiEventLogger.UiEventEnum event, InstanceId instanceId) { + mUiEventLogger.logWithInstanceId( + event, + 0, + null, + instanceId); + } + + /** + * @return A unique {@link InstanceId} to join across events recorded by this logger instance. + */ + private InstanceId getInstanceId() { + if (mInstanceId == null) { + if (sInstanceIdSequence == null) { + sInstanceIdSequence = new InstanceIdSequence(SHARESHEET_INSTANCE_ID_MAX); + } + mInstanceId = sInstanceIdSequence.newInstanceId(); + } + return mInstanceId; + } + + /** + * The UiEvent enums that this class can log. + */ + enum SharesheetStartedEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "Basic system Sharesheet has started and is visible.") + SHARE_STARTED(228); + + private final int mId; + SharesheetStartedEvent(int id) { + mId = id; + } + @Override + public int getId() { + return mId; + } + } + + /** + * The UiEvent enums that this class can log. + */ + enum SharesheetTargetSelectedEvent implements UiEventLogger.UiEventEnum { + INVALID(0), + @UiEvent(doc = "User selected a service target.") + SHARESHEET_SERVICE_TARGET_SELECTED(232), + @UiEvent(doc = "User selected an app target.") + SHARESHEET_APP_TARGET_SELECTED(233), + @UiEvent(doc = "User selected a standard target.") + SHARESHEET_STANDARD_TARGET_SELECTED(234), + @UiEvent(doc = "User selected the copy target.") + SHARESHEET_COPY_TARGET_SELECTED(235), + @UiEvent(doc = "User selected the nearby target.") + SHARESHEET_NEARBY_TARGET_SELECTED(626), + @UiEvent(doc = "User selected the edit target.") + SHARESHEET_EDIT_TARGET_SELECTED(669), + @UiEvent(doc = "User selected the modify share target.") + SHARESHEET_MODIFY_SHARE_SELECTED(1316), + @UiEvent(doc = "User selected a custom action.") + SHARESHEET_CUSTOM_ACTION_SELECTED(1317); + + private final int mId; + SharesheetTargetSelectedEvent(int id) { + mId = id; + } + @Override public int getId() { + return mId; + } + + public static SharesheetTargetSelectedEvent fromTargetType(int targetType) { + switch(targetType) { + case SELECTION_TYPE_SERVICE: + return SHARESHEET_SERVICE_TARGET_SELECTED; + case SELECTION_TYPE_APP: + return SHARESHEET_APP_TARGET_SELECTED; + case SELECTION_TYPE_STANDARD: + return SHARESHEET_STANDARD_TARGET_SELECTED; + case SELECTION_TYPE_COPY: + return SHARESHEET_COPY_TARGET_SELECTED; + case SELECTION_TYPE_NEARBY: + return SHARESHEET_NEARBY_TARGET_SELECTED; + case SELECTION_TYPE_EDIT: + return SHARESHEET_EDIT_TARGET_SELECTED; + case SELECTION_TYPE_MODIFY_SHARE: + return SHARESHEET_MODIFY_SHARE_SELECTED; + case SELECTION_TYPE_CUSTOM_ACTION: + return SHARESHEET_CUSTOM_ACTION_SELECTED; + default: + return INVALID; + } + } + } + + /** + * The UiEvent enums that this class can log. + */ + enum SharesheetStandardEvent implements UiEventLogger.UiEventEnum { + INVALID(0), + @UiEvent(doc = "User clicked share.") + SHARESHEET_TRIGGERED(227), + @UiEvent(doc = "User changed from work to personal profile or vice versa.") + SHARESHEET_PROFILE_CHANGED(229), + @UiEvent(doc = "User expanded target list.") + SHARESHEET_EXPANDED(230), + @UiEvent(doc = "User collapsed target list.") + SHARESHEET_COLLAPSED(231), + @UiEvent(doc = "Sharesheet app targets is fully populated.") + SHARESHEET_APP_LOAD_COMPLETE(322), + @UiEvent(doc = "Sharesheet direct targets is fully populated.") + SHARESHEET_DIRECT_LOAD_COMPLETE(323), + @UiEvent(doc = "Sharesheet direct targets timed out.") + SHARESHEET_DIRECT_LOAD_TIMEOUT(324), + @UiEvent(doc = "Sharesheet app share ranking timed out.") + SHARESHEET_APP_SHARE_RANKING_TIMEOUT(831), + @UiEvent(doc = "Sharesheet empty direct share row.") + SHARESHEET_EMPTY_DIRECT_SHARE_ROW(828); + + private final int mId; + SharesheetStandardEvent(int id) { + mId = id; + } + @Override public int getId() { + return mId; + } + } + + /** + * Returns the enum used in sharesheet started atom to indicate what preview type was used. + */ + private static int typeFromPreviewInt(int previewType) { + switch(previewType) { + case ContentPreviewType.CONTENT_PREVIEW_IMAGE: + return FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_IMAGE; + case ContentPreviewType.CONTENT_PREVIEW_FILE: + return FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_FILE; + case ContentPreviewType.CONTENT_PREVIEW_TEXT: + default: + return FrameworkStatsLog + .SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_TYPE_UNKNOWN; + } + } + + /** + * Returns the enum used in sharesheet started atom to indicate what intent triggers the + * ChooserActivity. + */ + private static int typeFromIntentString(String intent) { + if (intent == null) { + return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_DEFAULT; + } + switch (intent) { + case Intent.ACTION_VIEW: + return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_VIEW; + case Intent.ACTION_EDIT: + return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_EDIT; + case Intent.ACTION_SEND: + return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SEND; + case Intent.ACTION_SENDTO: + return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO; + case Intent.ACTION_SEND_MULTIPLE: + return FrameworkStatsLog + .SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SEND_MULTIPLE; + case MediaStore.ACTION_IMAGE_CAPTURE: + return FrameworkStatsLog + .SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_IMAGE_CAPTURE; + case Intent.ACTION_MAIN: + return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_MAIN; + default: + return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_DEFAULT; + } + } + + @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( + int frameworkEventId, + int appEventId, + String packageName, + int instanceId, + String mimeType, + int numAppProvidedDirectTargets, + int numAppProvidedAppTargets, + boolean isWorkProfile, + int previewType, + int intentType, + int numCustomActions, + boolean modifyShareActionProvided) { + FrameworkStatsLog.write( + frameworkEventId, + /* event_id = 1 */ appEventId, + /* package_name = 2 */ packageName, + /* instance_id = 3 */ instanceId, + /* mime_type = 4 */ mimeType, + /* num_app_provided_direct_targets */ numAppProvidedDirectTargets, + /* num_app_provided_app_targets */ numAppProvidedAppTargets, + /* is_workprofile */ isWorkProfile, + /* previewType = 8 */ previewType, + /* intentType = 9 */ intentType, + /* num_provided_custom_actions = 10 */ numCustomActions, + /* modify_share_action_provided = 11 */ modifyShareActionProvided); + } + + @Override + public void write( + int frameworkEventId, + int appEventId, + String packageName, + int instanceId, + int positionPicked, + boolean isPinned) { + FrameworkStatsLog.write( + frameworkEventId, + /* event_id = 1 */ appEventId, + /* package_name = 2 */ packageName, + /* instance_id = 3 */ instanceId, + /* position_picked = 4 */ positionPicked, + /* is_pinned = 5 */ isPinned); + } + } +} diff --git a/java/src/com/android/intentresolver/logging/FrameworkStatsLogger.kt b/java/src/com/android/intentresolver/logging/FrameworkStatsLogger.kt new file mode 100644 index 00000000..e0682b9e --- /dev/null +++ b/java/src/com/android/intentresolver/logging/FrameworkStatsLogger.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.intentresolver.logging + +import com.android.internal.util.FrameworkStatsLog + +/** A documenting annotation for FrameworkStatsLog methods and their associated UiEvents. */ +internal annotation class ForUiEvent(vararg val uiEventId: Int) + +/** Isolates the specific method signatures to use for each of the logged UiEvents. */ +internal interface FrameworkStatsLogger { + @ForUiEvent(FrameworkStatsLog.SHARESHEET_STARTED) + fun write( + frameworkEventId: Int, + appEventId: Int, + packageName: String?, + instanceId: Int, + mimeType: String?, + numAppProvidedDirectTargets: Int, + numAppProvidedAppTargets: Int, + isWorkProfile: Boolean, + previewType: Int, + intentType: Int, + numCustomActions: Int, + modifyShareActionProvided: Boolean + ) + + @ForUiEvent(FrameworkStatsLog.RANKING_SELECTED) + fun write( + frameworkEventId: Int, + appEventId: Int, + packageName: String?, + instanceId: Int, + positionPicked: Int, + isPinned: Boolean + ) +} diff --git a/java/src/com/android/intentresolver/model/AbstractResolverComparator.java b/java/src/com/android/intentresolver/model/AbstractResolverComparator.java index ff2d6a0f..932d8590 100644 --- a/java/src/com/android/intentresolver/model/AbstractResolverComparator.java +++ b/java/src/com/android/intentresolver/model/AbstractResolverComparator.java @@ -30,10 +30,11 @@ import android.os.Message; import android.os.UserHandle; import android.util.Log; -import com.android.intentresolver.logging.EventLog; import com.android.intentresolver.ResolvedComponentInfo; import com.android.intentresolver.ResolverActivity; +import com.android.intentresolver.ResolverListController; import com.android.intentresolver.chooser.TargetInfo; +import com.android.intentresolver.logging.EventLog; import java.text.Collator; import java.util.ArrayList; @@ -75,6 +76,7 @@ public abstract class AbstractResolverComparator implements Comparator targetUserSpaceList, - @Nullable ComponentName promoteToFirst) { + String referrerPackage, Runnable afterCompute, EventLog eventLog, + List targetUserSpaceList, @Nullable ComponentName promoteToFirst) { super(launchedFromContext, intent, targetUserSpaceList, promoteToFirst); mCollator = Collator.getInstance( launchedFromContext.getResources().getConfiguration().locale); diff --git a/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt b/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt index 2d1ac4e4..e2b987c2 100644 --- a/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt +++ b/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt @@ -28,12 +28,13 @@ import android.graphics.drawable.Icon import android.service.chooser.ChooserAction import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry -import com.android.intentresolver.logging.EventLog +import com.android.intentresolver.logging.EventLogImpl import com.google.common.collect.ImmutableList import com.google.common.truth.Truth.assertThat import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import java.util.function.Consumer +import com.android.intentresolver.logging.EventLog import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue diff --git a/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java b/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java index 5b938aa1..b5c14ff1 100644 --- a/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java +++ b/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java @@ -29,7 +29,7 @@ import android.os.UserHandle; import com.android.intentresolver.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker; import com.android.intentresolver.chooser.TargetInfo; import com.android.intentresolver.contentpreview.ImageLoader; -import com.android.intentresolver.logging.EventLog; +import com.android.intentresolver.logging.EventLogImpl; import com.android.intentresolver.shortcuts.ShortcutLoader; import java.util.function.Consumer; @@ -64,7 +64,7 @@ public class ChooserActivityOverrideData { public Cursor resolverCursor; public boolean resolverForceException; public ImageLoader imageLoader; - public EventLog mEventLog; + public EventLogImpl mEventLog; public int alternateProfileSetting; public Resources resources; public UserHandle workProfileUserHandle; @@ -86,7 +86,7 @@ public class ChooserActivityOverrideData { resolverForceException = false; resolverListController = mock(ChooserActivity.ChooserListController.class); workResolverListController = mock(ChooserActivity.ChooserListController.class); - mEventLog = mock(EventLog.class); + mEventLog = mock(EventLogImpl.class); alternateProfileSetting = 0; resources = null; workProfileUserHandle = null; diff --git a/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt b/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt index 9b5e2d1c..87e58954 100644 --- a/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt +++ b/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt @@ -32,7 +32,7 @@ import com.android.intentresolver.chooser.DisplayResolveInfo import com.android.intentresolver.chooser.SelectableTargetInfo import com.android.intentresolver.chooser.TargetInfo import com.android.intentresolver.icons.TargetDataLoader -import com.android.intentresolver.logging.EventLog +import com.android.intentresolver.logging.EventLogImpl import com.android.internal.R import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -54,7 +54,7 @@ class ChooserListAdapterTest { private val resolverListController = mock() private val appLabel = "App" private val targetLabel = "Target" - private val mEventLog = mock() + private val mEventLog = mock() private val mTargetDataLoader = mock() private val testSubject by lazy { diff --git a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java index 578b9557..6dbf9b3f 100644 --- a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java +++ b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java @@ -39,7 +39,7 @@ import com.android.intentresolver.chooser.DisplayResolveInfo; import com.android.intentresolver.chooser.TargetInfo; import com.android.intentresolver.grid.ChooserGridAdapter; import com.android.intentresolver.icons.TargetDataLoader; -import com.android.intentresolver.logging.EventLog; +import com.android.intentresolver.logging.EventLogImpl; import com.android.intentresolver.shortcuts.ShortcutLoader; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -204,7 +204,7 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW } @Override - public EventLog getEventLog() { + public EventLogImpl getEventLog() { return sOverrides.mEventLog; } diff --git a/java/tests/src/com/android/intentresolver/IChooserWrapper.java b/java/tests/src/com/android/intentresolver/IChooserWrapper.java index 3326d7f2..54e4da9e 100644 --- a/java/tests/src/com/android/intentresolver/IChooserWrapper.java +++ b/java/tests/src/com/android/intentresolver/IChooserWrapper.java @@ -23,7 +23,7 @@ import android.content.pm.ResolveInfo; import android.os.UserHandle; import com.android.intentresolver.chooser.DisplayResolveInfo; -import com.android.intentresolver.logging.EventLog; +import com.android.intentresolver.logging.EventLogImpl; import java.util.concurrent.Executor; @@ -42,6 +42,6 @@ public interface IChooserWrapper { CharSequence pLabel, CharSequence pInfo, Intent replacementIntent, @Nullable TargetPresentationGetter resolveInfoPresentationGetter); UserHandle getCurrentUserHandle(); - EventLog getEventLog(); + EventLogImpl getEventLog(); Executor getMainExecutor(); } diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java index 59357843..2838f00b 100644 --- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java +++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java @@ -113,6 +113,7 @@ import androidx.test.rule.ActivityTestRule; import com.android.intentresolver.chooser.DisplayResolveInfo; import com.android.intentresolver.contentpreview.ImageLoader; import com.android.intentresolver.logging.EventLog; +import com.android.intentresolver.logging.EventLogImpl; import com.android.intentresolver.shortcuts.ShortcutLoader; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -845,7 +846,7 @@ public class UnbundledChooserActivityTest { onView(withId(R.id.copy)).perform(click()); EventLog logger = activity.getEventLog(); - verify(logger, times(1)).logActionSelected(eq(EventLog.SELECTION_TYPE_COPY)); + verify(logger, times(1)).logActionSelected(eq(EventLogImpl.SELECTION_TYPE_COPY)); } @Test @@ -1466,7 +1467,7 @@ public class UnbundledChooserActivityTest { ArgumentCaptor hashCaptor = ArgumentCaptor.forClass(HashedStringCache.HashResult.class); verify(activity.getEventLog(), times(1)).logShareTargetSelected( - eq(EventLog.SELECTION_TYPE_SERVICE), + eq(EventLogImpl.SELECTION_TYPE_SERVICE), /* packageName= */ any(), /* positionPicked= */ anyInt(), /* directTargetAlsoRanked= */ eq(-1), @@ -1547,7 +1548,7 @@ public class UnbundledChooserActivityTest { waitForIdle(); verify(activity.getEventLog(), times(1)).logShareTargetSelected( - eq(EventLog.SELECTION_TYPE_SERVICE), + eq(EventLogImpl.SELECTION_TYPE_SERVICE), /* packageName= */ any(), /* positionPicked= */ anyInt(), /* directTargetAlsoRanked= */ eq(0), @@ -1963,7 +1964,7 @@ public class UnbundledChooserActivityTest { EventLog logger = wrapper.getEventLog(); verify(logger, times(1)).logShareTargetSelected( - eq(EventLog.SELECTION_TYPE_SERVICE), + eq(EventLogImpl.SELECTION_TYPE_SERVICE), /* packageName= */ any(), /* positionPicked= */ anyInt(), // The packages sholdn't match for app target and direct target: @@ -2296,7 +2297,7 @@ public class UnbundledChooserActivityTest { EventLog logger = activity.getEventLog(); ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(Integer.class); verify(logger, times(1)).logShareTargetSelected( - eq(EventLog.SELECTION_TYPE_SERVICE), + eq(EventLogImpl.SELECTION_TYPE_SERVICE), /* packageName= */ any(), /* positionPicked= */ anyInt(), /* directTargetAlsoRanked= */ anyInt(), diff --git a/java/tests/src/com/android/intentresolver/logging/EventLogImplTest.java b/java/tests/src/com/android/intentresolver/logging/EventLogImplTest.java new file mode 100644 index 00000000..19177798 --- /dev/null +++ b/java/tests/src/com/android/intentresolver/logging/EventLogImplTest.java @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver.logging; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.AdditionalMatchers.gt; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import android.content.Intent; +import android.metrics.LogMaker; + +import com.android.intentresolver.logging.EventLogImpl.SharesheetStandardEvent; +import com.android.intentresolver.logging.EventLogImpl.SharesheetStartedEvent; +import com.android.intentresolver.logging.EventLogImpl.SharesheetTargetSelectedEvent; +import com.android.intentresolver.contentpreview.ContentPreviewType; +import com.android.internal.logging.InstanceId; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLogger.UiEventEnum; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.util.FrameworkStatsLog; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public final class EventLogImplTest { + @Mock private UiEventLogger mUiEventLog; + @Mock private FrameworkStatsLogger mFrameworkLog; + @Mock private MetricsLogger mMetricsLogger; + + private EventLogImpl mChooserLogger; + + @Before + public void setUp() { + //Mockito.reset(mUiEventLog, mFrameworkLog, mMetricsLogger); + mChooserLogger = new EventLogImpl(mUiEventLog, mFrameworkLog, mMetricsLogger); + } + + @After + public void tearDown() { + verifyNoMoreInteractions(mUiEventLog); + verifyNoMoreInteractions(mFrameworkLog); + verifyNoMoreInteractions(mMetricsLogger); + } + + @Test + public void testLogChooserActivityShown_personalProfile() { + final boolean isWorkProfile = false; + final String mimeType = "application/TestType"; + final long systemCost = 456; + + mChooserLogger.logChooserActivityShown(isWorkProfile, mimeType, systemCost); + + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); + verify(mMetricsLogger).write(eventCaptor.capture()); + LogMaker event = eventCaptor.getValue(); + + assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN); + assertThat(event.getSubtype()).isEqualTo(MetricsEvent.PARENT_PROFILE); + assertThat(event.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE)).isEqualTo(mimeType); + assertThat(event.getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS)) + .isEqualTo(systemCost); + } + + @Test + public void testLogChooserActivityShown_workProfile() { + final boolean isWorkProfile = true; + final String mimeType = "application/TestType"; + final long systemCost = 456; + + mChooserLogger.logChooserActivityShown(isWorkProfile, mimeType, systemCost); + + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); + verify(mMetricsLogger).write(eventCaptor.capture()); + LogMaker event = eventCaptor.getValue(); + + assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN); + assertThat(event.getSubtype()).isEqualTo(MetricsEvent.MANAGED_PROFILE); + assertThat(event.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE)).isEqualTo(mimeType); + assertThat(event.getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS)) + .isEqualTo(systemCost); + } + + @Test + public void testLogShareStarted() { + final String packageName = "com.test.foo"; + final String mimeType = "text/plain"; + final int appProvidedDirectTargets = 123; + final int appProvidedAppTargets = 456; + final boolean workProfile = true; + final int previewType = ContentPreviewType.CONTENT_PREVIEW_FILE; + final String intentAction = Intent.ACTION_SENDTO; + final int numCustomActions = 3; + final boolean modifyShareProvided = true; + + mChooserLogger.logShareStarted( + packageName, + mimeType, + appProvidedDirectTargets, + appProvidedAppTargets, + workProfile, + previewType, + intentAction, + numCustomActions, + modifyShareProvided); + + verify(mFrameworkLog).write( + eq(FrameworkStatsLog.SHARESHEET_STARTED), + eq(SharesheetStartedEvent.SHARE_STARTED.getId()), + eq(packageName), + /* instanceId=*/ gt(0), + eq(mimeType), + eq(appProvidedDirectTargets), + eq(appProvidedAppTargets), + eq(workProfile), + eq(FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_FILE), + eq(FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO), + /* custom actions provided */ eq(numCustomActions), + /* reselection action provided */ eq(modifyShareProvided)); + } + + @Test + public void testLogShareTargetSelected() { + final int targetType = EventLogImpl.SELECTION_TYPE_SERVICE; + final String packageName = "com.test.foo"; + final int positionPicked = 123; + final int directTargetAlsoRanked = -1; + final int callerTargetCount = 0; + final boolean isPinned = true; + final boolean isSuccessfullySelected = true; + final long selectionCost = 456; + + mChooserLogger.logShareTargetSelected( + targetType, + packageName, + positionPicked, + directTargetAlsoRanked, + callerTargetCount, + /* directTargetHashed= */ null, + isPinned, + isSuccessfullySelected, + selectionCost); + + verify(mFrameworkLog).write( + eq(FrameworkStatsLog.RANKING_SELECTED), + eq(SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()), + eq(packageName), + /* instanceId=*/ gt(0), + eq(positionPicked), + eq(isPinned)); + + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); + verify(mMetricsLogger).write(eventCaptor.capture()); + LogMaker event = eventCaptor.getValue(); + assertThat(event.getCategory()).isEqualTo( + MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET); + assertThat(event.getSubtype()).isEqualTo(positionPicked); + } + + @Test + public void testLogActionSelected() { + mChooserLogger.logActionSelected(EventLogImpl.SELECTION_TYPE_COPY); + + verify(mFrameworkLog).write( + eq(FrameworkStatsLog.RANKING_SELECTED), + eq(SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()), + eq(""), + /* instanceId=*/ gt(0), + eq(-1), + eq(false)); + + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); + verify(mMetricsLogger).write(eventCaptor.capture()); + LogMaker event = eventCaptor.getValue(); + assertThat(event.getCategory()).isEqualTo( + MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET); + assertThat(event.getSubtype()).isEqualTo(1); + } + + @Test + public void testLogCustomActionSelected() { + final int position = 4; + mChooserLogger.logCustomActionSelected(position); + + verify(mFrameworkLog).write( + eq(FrameworkStatsLog.RANKING_SELECTED), + eq(SharesheetTargetSelectedEvent.SHARESHEET_CUSTOM_ACTION_SELECTED.getId()), + any(), anyInt(), eq(position), eq(false)); + } + + @Test + public void testLogDirectShareTargetReceived() { + final int category = MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER; + final int latency = 123; + + mChooserLogger.logDirectShareTargetReceived(category, latency); + + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); + verify(mMetricsLogger).write(eventCaptor.capture()); + LogMaker event = eventCaptor.getValue(); + assertThat(event.getCategory()).isEqualTo(category); + assertThat(event.getSubtype()).isEqualTo(latency); + } + + @Test + public void testLogActionShareWithPreview() { + final int previewType = ContentPreviewType.CONTENT_PREVIEW_TEXT; + + mChooserLogger.logActionShareWithPreview(previewType); + + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); + verify(mMetricsLogger).write(eventCaptor.capture()); + LogMaker event = eventCaptor.getValue(); + assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_SHARE_WITH_PREVIEW); + assertThat(event.getSubtype()).isEqualTo(previewType); + } + + @Test + public void testLogSharesheetTriggered() { + mChooserLogger.logSharesheetTriggered(); + verify(mUiEventLog).logWithInstanceId( + eq(SharesheetStandardEvent.SHARESHEET_TRIGGERED), eq(0), isNull(), any()); + } + + @Test + public void testLogSharesheetAppLoadComplete() { + mChooserLogger.logSharesheetAppLoadComplete(); + verify(mUiEventLog).logWithInstanceId( + eq(SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE), eq(0), isNull(), any()); + } + + @Test + public void testLogSharesheetDirectLoadComplete() { + mChooserLogger.logSharesheetDirectLoadComplete(); + verify(mUiEventLog).logWithInstanceId( + eq(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE), + eq(0), + isNull(), + any()); + } + + @Test + public void testLogSharesheetDirectLoadTimeout() { + mChooserLogger.logSharesheetDirectLoadTimeout(); + verify(mUiEventLog).logWithInstanceId( + eq(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_TIMEOUT), eq(0), isNull(), any()); + } + + @Test + public void testLogSharesheetProfileChanged() { + mChooserLogger.logSharesheetProfileChanged(); + verify(mUiEventLog).logWithInstanceId( + eq(SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED), eq(0), isNull(), any()); + } + + @Test + public void testLogSharesheetExpansionChanged_collapsed() { + mChooserLogger.logSharesheetExpansionChanged(/* isCollapsed=*/ true); + verify(mUiEventLog).logWithInstanceId( + eq(SharesheetStandardEvent.SHARESHEET_COLLAPSED), eq(0), isNull(), any()); + } + + @Test + public void testLogSharesheetExpansionChanged_expanded() { + mChooserLogger.logSharesheetExpansionChanged(/* isCollapsed=*/ false); + verify(mUiEventLog).logWithInstanceId( + eq(SharesheetStandardEvent.SHARESHEET_EXPANDED), eq(0), isNull(), any()); + } + + @Test + public void testLogSharesheetAppShareRankingTimeout() { + mChooserLogger.logSharesheetAppShareRankingTimeout(); + verify(mUiEventLog).logWithInstanceId( + eq(SharesheetStandardEvent.SHARESHEET_APP_SHARE_RANKING_TIMEOUT), + eq(0), + isNull(), + any()); + } + + @Test + public void testLogSharesheetEmptyDirectShareRow() { + mChooserLogger.logSharesheetEmptyDirectShareRow(); + verify(mUiEventLog).logWithInstanceId( + eq(SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW), + eq(0), + isNull(), + any()); + } + + @Test + public void testDifferentLoggerInstancesUseDifferentInstanceIds() { + ArgumentCaptor idIntCaptor = ArgumentCaptor.forClass(Integer.class); + EventLogImpl chooserLogger2 = + new EventLogImpl(mUiEventLog, mFrameworkLog, mMetricsLogger); + + final int targetType = EventLogImpl.SELECTION_TYPE_COPY; + final String packageName = "com.test.foo"; + final int positionPicked = 123; + final int directTargetAlsoRanked = -1; + final int callerTargetCount = 0; + final boolean isPinned = true; + final boolean isSuccessfullySelected = true; + final long selectionCost = 456; + + mChooserLogger.logShareTargetSelected( + targetType, + packageName, + positionPicked, + directTargetAlsoRanked, + callerTargetCount, + /* directTargetHashed= */ null, + isPinned, + isSuccessfullySelected, + selectionCost); + + chooserLogger2.logShareTargetSelected( + targetType, + packageName, + positionPicked, + directTargetAlsoRanked, + callerTargetCount, + /* directTargetHashed= */ null, + isPinned, + isSuccessfullySelected, + selectionCost); + + verify(mFrameworkLog, times(2)).write( + anyInt(), anyInt(), anyString(), idIntCaptor.capture(), anyInt(), anyBoolean()); + + int id1 = idIntCaptor.getAllValues().get(0); + int id2 = idIntCaptor.getAllValues().get(1); + + assertThat(id1).isGreaterThan(0); + assertThat(id2).isGreaterThan(0); + assertThat(id1).isNotEqualTo(id2); + } + + @Test + public void testUiAndFrameworkEventsUseSameInstanceIdForSameLoggerInstance() { + ArgumentCaptor idIntCaptor = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor idObjectCaptor = ArgumentCaptor.forClass(InstanceId.class); + + final int targetType = EventLogImpl.SELECTION_TYPE_COPY; + final String packageName = "com.test.foo"; + final int positionPicked = 123; + final int directTargetAlsoRanked = -1; + final int callerTargetCount = 0; + final boolean isPinned = true; + final boolean isSuccessfullySelected = true; + final long selectionCost = 456; + + mChooserLogger.logShareTargetSelected( + targetType, + packageName, + positionPicked, + directTargetAlsoRanked, + callerTargetCount, + /* directTargetHashed= */ null, + isPinned, + isSuccessfullySelected, + selectionCost); + + verify(mFrameworkLog).write( + anyInt(), anyInt(), anyString(), idIntCaptor.capture(), anyInt(), anyBoolean()); + + mChooserLogger.logSharesheetTriggered(); + verify(mUiEventLog).logWithInstanceId( + any(UiEventEnum.class), anyInt(), any(), idObjectCaptor.capture()); + + assertThat(idIntCaptor.getValue()).isGreaterThan(0); + assertThat(idObjectCaptor.getValue().getId()).isEqualTo(idIntCaptor.getValue()); + } + + @Test + public void testTargetSelectionCategories() { + assertThat(EventLogImpl.getTargetSelectionCategory( + EventLogImpl.SELECTION_TYPE_SERVICE)) + .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET); + assertThat(EventLogImpl.getTargetSelectionCategory( + EventLogImpl.SELECTION_TYPE_APP)) + .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET); + assertThat(EventLogImpl.getTargetSelectionCategory( + EventLogImpl.SELECTION_TYPE_STANDARD)) + .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET); + assertThat(EventLogImpl.getTargetSelectionCategory( + EventLogImpl.SELECTION_TYPE_COPY)).isEqualTo(0); + assertThat(EventLogImpl.getTargetSelectionCategory( + EventLogImpl.SELECTION_TYPE_NEARBY)).isEqualTo(0); + assertThat(EventLogImpl.getTargetSelectionCategory( + EventLogImpl.SELECTION_TYPE_EDIT)).isEqualTo(0); + } +} diff --git a/java/tests/src/com/android/intentresolver/logging/EventLogTest.java b/java/tests/src/com/android/intentresolver/logging/EventLogTest.java deleted file mode 100644 index 17452774..00000000 --- a/java/tests/src/com/android/intentresolver/logging/EventLogTest.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.intentresolver.logging; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.AdditionalMatchers.gt; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -import android.content.Intent; -import android.metrics.LogMaker; - -import com.android.intentresolver.logging.EventLog.FrameworkStatsLogger; -import com.android.intentresolver.logging.EventLog.SharesheetStandardEvent; -import com.android.intentresolver.logging.EventLog.SharesheetStartedEvent; -import com.android.intentresolver.logging.EventLog.SharesheetTargetSelectedEvent; -import com.android.intentresolver.contentpreview.ContentPreviewType; -import com.android.internal.logging.InstanceId; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.UiEventLogger.UiEventEnum; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.util.FrameworkStatsLog; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public final class EventLogTest { - @Mock private UiEventLogger mUiEventLog; - @Mock private FrameworkStatsLogger mFrameworkLog; - @Mock private MetricsLogger mMetricsLogger; - - private EventLog mChooserLogger; - - @Before - public void setUp() { - //Mockito.reset(mUiEventLog, mFrameworkLog, mMetricsLogger); - mChooserLogger = new EventLog(mUiEventLog, mFrameworkLog, mMetricsLogger); - } - - @After - public void tearDown() { - verifyNoMoreInteractions(mUiEventLog); - verifyNoMoreInteractions(mFrameworkLog); - verifyNoMoreInteractions(mMetricsLogger); - } - - @Test - public void testLogChooserActivityShown_personalProfile() { - final boolean isWorkProfile = false; - final String mimeType = "application/TestType"; - final long systemCost = 456; - - mChooserLogger.logChooserActivityShown(isWorkProfile, mimeType, systemCost); - - ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); - verify(mMetricsLogger).write(eventCaptor.capture()); - LogMaker event = eventCaptor.getValue(); - - assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN); - assertThat(event.getSubtype()).isEqualTo(MetricsEvent.PARENT_PROFILE); - assertThat(event.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE)).isEqualTo(mimeType); - assertThat(event.getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS)) - .isEqualTo(systemCost); - } - - @Test - public void testLogChooserActivityShown_workProfile() { - final boolean isWorkProfile = true; - final String mimeType = "application/TestType"; - final long systemCost = 456; - - mChooserLogger.logChooserActivityShown(isWorkProfile, mimeType, systemCost); - - ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); - verify(mMetricsLogger).write(eventCaptor.capture()); - LogMaker event = eventCaptor.getValue(); - - assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN); - assertThat(event.getSubtype()).isEqualTo(MetricsEvent.MANAGED_PROFILE); - assertThat(event.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE)).isEqualTo(mimeType); - assertThat(event.getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS)) - .isEqualTo(systemCost); - } - - @Test - public void testLogShareStarted() { - final String packageName = "com.test.foo"; - final String mimeType = "text/plain"; - final int appProvidedDirectTargets = 123; - final int appProvidedAppTargets = 456; - final boolean workProfile = true; - final int previewType = ContentPreviewType.CONTENT_PREVIEW_FILE; - final String intentAction = Intent.ACTION_SENDTO; - final int numCustomActions = 3; - final boolean modifyShareProvided = true; - - mChooserLogger.logShareStarted( - packageName, - mimeType, - appProvidedDirectTargets, - appProvidedAppTargets, - workProfile, - previewType, - intentAction, - numCustomActions, - modifyShareProvided); - - verify(mFrameworkLog).write( - eq(FrameworkStatsLog.SHARESHEET_STARTED), - eq(SharesheetStartedEvent.SHARE_STARTED.getId()), - eq(packageName), - /* instanceId=*/ gt(0), - eq(mimeType), - eq(appProvidedDirectTargets), - eq(appProvidedAppTargets), - eq(workProfile), - eq(FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_FILE), - eq(FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO), - /* custom actions provided */ eq(numCustomActions), - /* reselection action provided */ eq(modifyShareProvided)); - } - - @Test - public void testLogShareTargetSelected() { - final int targetType = EventLog.SELECTION_TYPE_SERVICE; - final String packageName = "com.test.foo"; - final int positionPicked = 123; - final int directTargetAlsoRanked = -1; - final int callerTargetCount = 0; - final boolean isPinned = true; - final boolean isSuccessfullySelected = true; - final long selectionCost = 456; - - mChooserLogger.logShareTargetSelected( - targetType, - packageName, - positionPicked, - directTargetAlsoRanked, - callerTargetCount, - /* directTargetHashed= */ null, - isPinned, - isSuccessfullySelected, - selectionCost); - - verify(mFrameworkLog).write( - eq(FrameworkStatsLog.RANKING_SELECTED), - eq(SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()), - eq(packageName), - /* instanceId=*/ gt(0), - eq(positionPicked), - eq(isPinned)); - - ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); - verify(mMetricsLogger).write(eventCaptor.capture()); - LogMaker event = eventCaptor.getValue(); - assertThat(event.getCategory()).isEqualTo( - MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET); - assertThat(event.getSubtype()).isEqualTo(positionPicked); - } - - @Test - public void testLogActionSelected() { - mChooserLogger.logActionSelected(EventLog.SELECTION_TYPE_COPY); - - verify(mFrameworkLog).write( - eq(FrameworkStatsLog.RANKING_SELECTED), - eq(SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()), - eq(""), - /* instanceId=*/ gt(0), - eq(-1), - eq(false)); - - ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); - verify(mMetricsLogger).write(eventCaptor.capture()); - LogMaker event = eventCaptor.getValue(); - assertThat(event.getCategory()).isEqualTo( - MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET); - assertThat(event.getSubtype()).isEqualTo(1); - } - - @Test - public void testLogCustomActionSelected() { - final int position = 4; - mChooserLogger.logCustomActionSelected(position); - - verify(mFrameworkLog).write( - eq(FrameworkStatsLog.RANKING_SELECTED), - eq(SharesheetTargetSelectedEvent.SHARESHEET_CUSTOM_ACTION_SELECTED.getId()), - any(), anyInt(), eq(position), eq(false)); - } - - @Test - public void testLogDirectShareTargetReceived() { - final int category = MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER; - final int latency = 123; - - mChooserLogger.logDirectShareTargetReceived(category, latency); - - ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); - verify(mMetricsLogger).write(eventCaptor.capture()); - LogMaker event = eventCaptor.getValue(); - assertThat(event.getCategory()).isEqualTo(category); - assertThat(event.getSubtype()).isEqualTo(latency); - } - - @Test - public void testLogActionShareWithPreview() { - final int previewType = ContentPreviewType.CONTENT_PREVIEW_TEXT; - - mChooserLogger.logActionShareWithPreview(previewType); - - ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(LogMaker.class); - verify(mMetricsLogger).write(eventCaptor.capture()); - LogMaker event = eventCaptor.getValue(); - assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_SHARE_WITH_PREVIEW); - assertThat(event.getSubtype()).isEqualTo(previewType); - } - - @Test - public void testLogSharesheetTriggered() { - mChooserLogger.logSharesheetTriggered(); - verify(mUiEventLog).logWithInstanceId( - eq(SharesheetStandardEvent.SHARESHEET_TRIGGERED), eq(0), isNull(), any()); - } - - @Test - public void testLogSharesheetAppLoadComplete() { - mChooserLogger.logSharesheetAppLoadComplete(); - verify(mUiEventLog).logWithInstanceId( - eq(SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE), eq(0), isNull(), any()); - } - - @Test - public void testLogSharesheetDirectLoadComplete() { - mChooserLogger.logSharesheetDirectLoadComplete(); - verify(mUiEventLog).logWithInstanceId( - eq(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE), - eq(0), - isNull(), - any()); - } - - @Test - public void testLogSharesheetDirectLoadTimeout() { - mChooserLogger.logSharesheetDirectLoadTimeout(); - verify(mUiEventLog).logWithInstanceId( - eq(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_TIMEOUT), eq(0), isNull(), any()); - } - - @Test - public void testLogSharesheetProfileChanged() { - mChooserLogger.logSharesheetProfileChanged(); - verify(mUiEventLog).logWithInstanceId( - eq(SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED), eq(0), isNull(), any()); - } - - @Test - public void testLogSharesheetExpansionChanged_collapsed() { - mChooserLogger.logSharesheetExpansionChanged(/* isCollapsed=*/ true); - verify(mUiEventLog).logWithInstanceId( - eq(SharesheetStandardEvent.SHARESHEET_COLLAPSED), eq(0), isNull(), any()); - } - - @Test - public void testLogSharesheetExpansionChanged_expanded() { - mChooserLogger.logSharesheetExpansionChanged(/* isCollapsed=*/ false); - verify(mUiEventLog).logWithInstanceId( - eq(SharesheetStandardEvent.SHARESHEET_EXPANDED), eq(0), isNull(), any()); - } - - @Test - public void testLogSharesheetAppShareRankingTimeout() { - mChooserLogger.logSharesheetAppShareRankingTimeout(); - verify(mUiEventLog).logWithInstanceId( - eq(SharesheetStandardEvent.SHARESHEET_APP_SHARE_RANKING_TIMEOUT), - eq(0), - isNull(), - any()); - } - - @Test - public void testLogSharesheetEmptyDirectShareRow() { - mChooserLogger.logSharesheetEmptyDirectShareRow(); - verify(mUiEventLog).logWithInstanceId( - eq(SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW), - eq(0), - isNull(), - any()); - } - - @Test - public void testDifferentLoggerInstancesUseDifferentInstanceIds() { - ArgumentCaptor idIntCaptor = ArgumentCaptor.forClass(Integer.class); - EventLog chooserLogger2 = - new EventLog(mUiEventLog, mFrameworkLog, mMetricsLogger); - - final int targetType = EventLog.SELECTION_TYPE_COPY; - final String packageName = "com.test.foo"; - final int positionPicked = 123; - final int directTargetAlsoRanked = -1; - final int callerTargetCount = 0; - final boolean isPinned = true; - final boolean isSuccessfullySelected = true; - final long selectionCost = 456; - - mChooserLogger.logShareTargetSelected( - targetType, - packageName, - positionPicked, - directTargetAlsoRanked, - callerTargetCount, - /* directTargetHashed= */ null, - isPinned, - isSuccessfullySelected, - selectionCost); - - chooserLogger2.logShareTargetSelected( - targetType, - packageName, - positionPicked, - directTargetAlsoRanked, - callerTargetCount, - /* directTargetHashed= */ null, - isPinned, - isSuccessfullySelected, - selectionCost); - - verify(mFrameworkLog, times(2)).write( - anyInt(), anyInt(), anyString(), idIntCaptor.capture(), anyInt(), anyBoolean()); - - int id1 = idIntCaptor.getAllValues().get(0); - int id2 = idIntCaptor.getAllValues().get(1); - - assertThat(id1).isGreaterThan(0); - assertThat(id2).isGreaterThan(0); - assertThat(id1).isNotEqualTo(id2); - } - - @Test - public void testUiAndFrameworkEventsUseSameInstanceIdForSameLoggerInstance() { - ArgumentCaptor idIntCaptor = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor idObjectCaptor = ArgumentCaptor.forClass(InstanceId.class); - - final int targetType = EventLog.SELECTION_TYPE_COPY; - final String packageName = "com.test.foo"; - final int positionPicked = 123; - final int directTargetAlsoRanked = -1; - final int callerTargetCount = 0; - final boolean isPinned = true; - final boolean isSuccessfullySelected = true; - final long selectionCost = 456; - - mChooserLogger.logShareTargetSelected( - targetType, - packageName, - positionPicked, - directTargetAlsoRanked, - callerTargetCount, - /* directTargetHashed= */ null, - isPinned, - isSuccessfullySelected, - selectionCost); - - verify(mFrameworkLog).write( - anyInt(), anyInt(), anyString(), idIntCaptor.capture(), anyInt(), anyBoolean()); - - mChooserLogger.logSharesheetTriggered(); - verify(mUiEventLog).logWithInstanceId( - any(UiEventEnum.class), anyInt(), any(), idObjectCaptor.capture()); - - assertThat(idIntCaptor.getValue()).isGreaterThan(0); - assertThat(idObjectCaptor.getValue().getId()).isEqualTo(idIntCaptor.getValue()); - } - - @Test - public void testTargetSelectionCategories() { - assertThat(EventLog.getTargetSelectionCategory( - EventLog.SELECTION_TYPE_SERVICE)) - .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET); - assertThat(EventLog.getTargetSelectionCategory( - EventLog.SELECTION_TYPE_APP)) - .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET); - assertThat(EventLog.getTargetSelectionCategory( - EventLog.SELECTION_TYPE_STANDARD)) - .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET); - assertThat(EventLog.getTargetSelectionCategory( - EventLog.SELECTION_TYPE_COPY)).isEqualTo(0); - assertThat(EventLog.getTargetSelectionCategory( - EventLog.SELECTION_TYPE_NEARBY)).isEqualTo(0); - assertThat(EventLog.getTargetSelectionCategory( - EventLog.SELECTION_TYPE_EDIT)).isEqualTo(0); - } -} -- cgit v1.2.3-59-g8ed1b From a7382f3265d0bb76532b561059e56bafa855542e Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Wed, 30 Aug 2023 14:59:06 -0400 Subject: Switches to HiltTestApplication in tests. This allows testing with @HiltAndroidTest, @TestInstallIn, etc. Usage to follow in a CL series. Bug: 296633726 Bug: 300157408 Flag: EXEMPT Change-Id: I13fbf29005e11ea7375d77d11da4bc1bfa68b666 --- .../android/intentresolver/ChooserActivity.java | 8 ++----- java/tests/Android.bp | 1 + java/tests/AndroidManifest.xml | 2 +- .../intentresolver/ChooserWrapperActivity.java | 2 +- .../com/android/intentresolver/TestApplication.kt | 26 ---------------------- .../UnbundledChooserActivityTest.java | 9 +++++++- .../UnbundledChooserActivityWorkProfileTest.java | 9 +++++++- 7 files changed, 21 insertions(+), 36 deletions(-) delete mode 100644 java/tests/src/com/android/intentresolver/TestApplication.kt (limited to 'java') diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index 0a4f9f46..c27d3bac 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -258,7 +258,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements createProfileRecords( new AppPredictorFactory( - this, // TODO: Review w/team, possible side effects? + this, mChooserRequest.getSharedText(), mChooserRequest.getTargetIntentFilter()), mChooserRequest.getTargetIntentFilter()); @@ -276,10 +276,6 @@ public class ChooserActivity extends Hilt_ChooserActivity implements new DefaultTargetDataLoader(this, getLifecycle(), false), /* safeForwardingMode= */ true); - if (mFeatureFlags.exampleNewSharingMethod()) { - // Sample flag usage - } - mIntegratedDeviceComponents = getIntegratedDeviceComponents(); mRefinementManager = new ViewModelProvider(this).get(ChooserRefinementManager.class); @@ -387,7 +383,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements ShortcutLoader shortcutLoader = ActivityManager.isLowRamDeviceStatic() ? null : createShortcutLoader( - this, // TODO: Review w/team, possible side effects? + this, appPredictor, userHandle, targetIntentFilter, diff --git a/java/tests/Android.bp b/java/tests/Android.bp index e4d24eb5..974b8a47 100644 --- a/java/tests/Android.bp +++ b/java/tests/Android.bp @@ -44,6 +44,7 @@ android_test { "androidx.lifecycle_lifecycle-common-java8", "androidx.lifecycle_lifecycle-extensions", "androidx.lifecycle_lifecycle-runtime-testing", + "hilt_android_testing", "IntentResolver-core", "junit", "kotlinx_coroutines_test", diff --git a/java/tests/AndroidManifest.xml b/java/tests/AndroidManifest.xml index ae6a2205..35dc2ee6 100644 --- a/java/tests/AndroidManifest.xml +++ b/java/tests/AndroidManifest.xml @@ -23,7 +23,7 @@ - + diff --git a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java index 6dbf9b3f..d488e02b 100644 --- a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java +++ b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java @@ -263,7 +263,7 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW @Override public Context createContextAsUser(UserHandle user, int flags) { // return the current context as a work profile doesn't really exist in these tests - return getApplicationContext(); + return this; } @Override diff --git a/java/tests/src/com/android/intentresolver/TestApplication.kt b/java/tests/src/com/android/intentresolver/TestApplication.kt deleted file mode 100644 index b57fd4d9..00000000 --- a/java/tests/src/com/android/intentresolver/TestApplication.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.intentresolver - -import android.content.Context -import android.os.UserHandle - -class TestApplication : MainApplication() { - - // return the current context as a work profile doesn't really exist in these tests - override fun createContextAsUser(user: UserHandle, flags: Int): Context = this -} diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java index 2838f00b..0b3cb20a 100644 --- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java +++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java @@ -146,6 +146,9 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; +import dagger.hilt.android.testing.HiltAndroidRule; +import dagger.hilt.android.testing.HiltAndroidTest; + /** * Instrumentation tests for ChooserActivity. *

@@ -153,6 +156,7 @@ import java.util.function.Function; *

*/ @RunWith(Parameterized.class) +@HiltAndroidTest public class UnbundledChooserActivityTest { private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry @@ -187,7 +191,10 @@ public class UnbundledChooserActivityTest { cleanOverrideData(); } - @Rule + @Rule(order = 0) + public HiltAndroidRule mHiltAndroidRule = new HiltAndroidRule(this); + + @Rule(order = 1) public ActivityTestRule mActivityRule = new ActivityTestRule<>(ChooserWrapperActivity.class, false, false); diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java index 92bccb7d..f0e88564 100644 --- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java +++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java @@ -64,15 +64,22 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import dagger.hilt.android.testing.HiltAndroidRule; +import dagger.hilt.android.testing.HiltAndroidTest; + @DeviceFilter.MediumType @RunWith(Parameterized.class) +@HiltAndroidTest public class UnbundledChooserActivityWorkProfileTest { private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry .getInstrumentation().getTargetContext().getUser(); private static final UserHandle WORK_USER_HANDLE = UserHandle.of(10); - @Rule + @Rule(order = 0) + public HiltAndroidRule mHiltAndroidRule = new HiltAndroidRule(this); + + @Rule(order = 1) public ActivityTestRule mActivityRule = new ActivityTestRule<>(ChooserWrapperActivity.class, false, false); -- cgit v1.2.3-59-g8ed1b