Merge "Add sharesheet statsd logging" into rvc-dev am: f9617e33f9

Change-Id: I37b38005b051074cf7dbfa16dcbeec991ae1842b
This commit is contained in:
Susi Kharraz-Post
2020-04-03 13:20:20 +00:00
committed by Automerger Merge Worker
10 changed files with 746 additions and 10 deletions

View File

@@ -124,6 +124,7 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.GridLayoutManager;
import com.android.internal.widget.RecyclerView;
import com.android.internal.widget.ResolverDrawerLayout;
@@ -178,7 +179,7 @@ public class ChooserActivity extends ResolverActivity implements
private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = true;
// TODO(b/123088566) Share these in a better way.
private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share";
public static final String LAUNCH_LOCATION_DIRECT_SHARE = "direct_share";
private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
@@ -194,6 +195,14 @@ public class ChooserActivity extends ResolverActivity implements
public static final int TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER = 2;
public static final int TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE = 3;
public static final int SELECTION_TYPE_SERVICE = 1;
public static final int SELECTION_TYPE_APP = 2;
public static final int SELECTION_TYPE_STANDARD = 3;
public static final int SELECTION_TYPE_COPY = 4;
// statsd logger wrapper
protected ChooserActivityLogger mChooserActivityLogger;
private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true;
@IntDef(flag = false, prefix = { "TARGET_TYPE_" }, value = {
@@ -270,9 +279,9 @@ public class ChooserActivity extends ResolverActivity implements
// Starting at 1 since 0 is considered "undefined" for some of the database transformations
// of tron logs.
private static final int CONTENT_PREVIEW_IMAGE = 1;
private static final int CONTENT_PREVIEW_FILE = 2;
private static final int CONTENT_PREVIEW_TEXT = 3;
protected static final int CONTENT_PREVIEW_IMAGE = 1;
protected static final int CONTENT_PREVIEW_FILE = 2;
protected static final int CONTENT_PREVIEW_TEXT = 3;
protected MetricsLogger mMetricsLogger;
private ContentPreviewCoordinator mPreviewCoord;
@@ -500,6 +509,9 @@ public class ChooserActivity extends ResolverActivity implements
case CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT:
mMinTimeoutPassed = true;
if (!mServiceConnections.isEmpty()) {
getChooserActivityLogger().logSharesheetDirectLoadTimeout();
}
unbindRemainingServices();
maybeStopServiceRequestTimer();
break;
@@ -533,6 +545,7 @@ public class ChooserActivity extends ResolverActivity implements
logDirectShareTargetReceived(
MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER);
sendVoiceChoicesIfNeeded();
getChooserActivityLogger().logSharesheetDirectLoadComplete();
break;
default:
@@ -544,6 +557,7 @@ public class ChooserActivity extends ResolverActivity implements
@Override
protected void onCreate(Bundle savedInstanceState) {
final long intentReceivedTime = System.currentTimeMillis();
getChooserActivityLogger().logSharesheetTriggered();
// This is the only place this value is being set. Effectively final.
mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable();
@@ -707,6 +721,8 @@ public class ChooserActivity extends ResolverActivity implements
incrementNumSheetExpansions();
mWrittenOnce = true;
}
getChooserActivityLogger()
.logSharesheetExpansionChanged(isCollapsed);
}
});
}
@@ -715,6 +731,16 @@ public class ChooserActivity extends ResolverActivity implements
Log.d(TAG, "System Time Cost is " + systemCost);
}
getChooserActivityLogger().logShareStarted(
FrameworkStatsLog.SHARESHEET_STARTED,
getReferrerPackageName(),
target.getType(),
initialIntents == null ? 0 : initialIntents.length,
mCallerChooserTargets == null ? 0 : mCallerChooserTargets.length,
isWorkProfile(),
findPreferredContentPreview(getTargetIntent(), getContentResolver()),
target.getAction()
);
mDirectShareShortcutInfoCache = new HashMap<>();
}
@@ -969,6 +995,10 @@ public class ChooserActivity extends ResolverActivity implements
LogMaker targetLogMaker = new LogMaker(
MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET).setSubtype(1);
getMetricsLogger().write(targetLogMaker);
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_COPY,
"",
-1);
finish();
}
@@ -1644,18 +1674,33 @@ public class ChooserActivity extends ResolverActivity implements
if (mCallerChooserTargets != null) {
numCallerProvided = mCallerChooserTargets.length;
}
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_SERVICE,
targetInfo.getResolveInfo().activityInfo.processName,
value
);
break;
case ChooserListAdapter.TARGET_CALLER:
case ChooserListAdapter.TARGET_STANDARD:
cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET;
value -= currentListAdapter.getSelectableServiceTargetCount();
numCallerProvided = currentListAdapter.getCallerTargetCount();
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_APP,
targetInfo.getResolveInfo().activityInfo.processName,
value
);
break;
case ChooserListAdapter.TARGET_STANDARD_AZ:
// A-Z targets are unranked standard targets; we use -1 to mark that they
// are from the alphabetical pool.
value = -1;
cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET;
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_STANDARD,
targetInfo.getResolveInfo().activityInfo.processName,
value
);
break;
}
@@ -2131,7 +2176,7 @@ public class ChooserActivity extends ResolverActivity implements
if (appTarget != null) {
directShareAppPredictor.notifyAppTargetEvent(
new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
.setLaunchLocation(LAUNCH_LOCATON_DIRECT_SHARE)
.setLaunchLocation(LAUNCH_LOCATION_DIRECT_SHARE)
.build());
}
}
@@ -2290,6 +2335,13 @@ public class ChooserActivity extends ResolverActivity implements
return mMetricsLogger;
}
protected ChooserActivityLogger getChooserActivityLogger() {
if (mChooserActivityLogger == null) {
mChooserActivityLogger = new ChooserActivityLoggerImpl();
}
return mChooserActivityLogger;
}
public class ChooserListController extends ResolverListController {
public ChooserListController(Context context,
PackageManager pm,
@@ -2601,6 +2653,7 @@ public class ChooserActivity extends ResolverActivity implements
// don't support direct share on low ram devices
if (ActivityManager.isLowRamDeviceStatic()) {
getChooserActivityLogger().logSharesheetAppLoadComplete();
return;
}
@@ -2619,6 +2672,8 @@ public class ChooserActivity extends ResolverActivity implements
queryTargetServices(chooserListAdapter);
}
getChooserActivityLogger().logSharesheetAppLoadComplete();
}
private void setupScrollListener() {
@@ -3777,4 +3832,9 @@ public class ChooserActivity extends ResolverActivity implements
canvas.drawRoundRect(x, y, width, height, mRadius, mRadius, mRoundRectPaint);
}
}
@Override
protected void maybeLogProfileChange() {
getChooserActivityLogger().logShareheetProfileChanged();
}
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright (C) 2020 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.internal.app;
import android.content.Intent;
import android.provider.MediaStore;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.FrameworkStatsLog;
/**
* Interface for writing Sharesheet atoms to statsd log.
* @hide
*/
public interface ChooserActivityLogger {
/** Logs a UiEventReported event for the system sharesheet completing initial start-up. */
void logShareStarted(int eventId, String packageName, String mimeType, int appProvidedDirect,
int appProvidedApp, boolean isWorkprofile, int previewType, String intent);
/** Logs a UiEventReported event for the system sharesheet when the user selects a target. */
void logShareTargetSelected(int targetType, String packageName, int positionPicked);
/** Logs a UiEventReported event for the system sharesheet being triggered by the user. */
default void logSharesheetTriggered() {
log(SharesheetStandardEvent.SHARESHEET_TRIGGERED, getInstanceId());
}
/** Logs a UiEventReported event for the system sharesheet completing loading app targets. */
default void logSharesheetAppLoadComplete() {
log(SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE, getInstanceId());
}
/**
* Logs a UiEventReported event for the system sharesheet completing loading service targets.
*/
default void logSharesheetDirectLoadComplete() {
log(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE, getInstanceId());
}
/**
* Logs a UiEventReported event for the system sharesheet timing out loading service targets.
*/
default void logSharesheetDirectLoadTimeout() {
log(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_TIMEOUT, getInstanceId());
}
/**
* Logs a UiEventReported event for the system sharesheet switching
* between work and main profile.
*/
default void logShareheetProfileChanged() {
log(SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED, getInstanceId());
}
/** Logs a UiEventReported event for the system sharesheet getting expanded or collapsed. */
default void logSharesheetExpansionChanged(boolean isCollapsed) {
log(isCollapsed ? SharesheetStandardEvent.SHARESHEET_COLLAPSED :
SharesheetStandardEvent.SHARESHEET_EXPANDED, getInstanceId());
}
/**
* Logs a UiEventReported event for a given share activity
* @param event
* @param instanceId
*/
void log(UiEventLogger.UiEventEnum event, InstanceId instanceId);
/**
*
* @return
*/
InstanceId getInstanceId();
/**
* 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);
private final int mId;
SharesheetTargetSelectedEvent(int id) {
mId = id;
}
@Override public int getId() {
return mId;
}
public static SharesheetTargetSelectedEvent fromTargetType(int targetType) {
switch(targetType) {
case ChooserActivity.SELECTION_TYPE_SERVICE:
return SHARESHEET_SERVICE_TARGET_SELECTED;
case ChooserActivity.SELECTION_TYPE_APP:
return SHARESHEET_APP_TARGET_SELECTED;
case ChooserActivity.SELECTION_TYPE_STANDARD:
return SHARESHEET_STANDARD_TARGET_SELECTED;
case ChooserActivity.SELECTION_TYPE_COPY:
return SHARESHEET_COPY_TARGET_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);
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.
*/
default int typeFromPreviewInt(int previewType) {
switch(previewType) {
case ChooserActivity.CONTENT_PREVIEW_IMAGE:
return FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_IMAGE;
case ChooserActivity.CONTENT_PREVIEW_FILE:
return FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_FILE;
case ChooserActivity.CONTENT_PREVIEW_TEXT:
default:
return FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_TEXT;
}
}
/**
* Returns the enum used in sharesheet started atom to indicate what intent triggers the
* ChooserActivity.
*/
default int typeFromIntentString(String intent) {
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;
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2020 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.internal.app;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.util.FrameworkStatsLog;
/**
* Standard implementation of ChooserActivityLogger interface.
* @hide
*/
public class ChooserActivityLoggerImpl implements ChooserActivityLogger {
private static final int SHARESHEET_INSTANCE_ID_MAX = (1 << 13);
private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
// A small per-notification ID, used for statsd logging.
private InstanceId mInstanceId;
private static InstanceIdSequence sInstanceIdSequence;
@Override
public void logShareStarted(int eventId, String packageName, String mimeType,
int appProvidedDirect, int appProvidedApp, boolean isWorkprofile, int previewType,
String intent) {
FrameworkStatsLog.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));
}
@Override
public void logShareTargetSelected(int targetType, String packageName, int positionPicked) {
FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED,
/* event_id = 1 */ SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(),
/* package_name = 2 */ packageName,
/* instance_id = 3 */ getInstanceId().getId(),
/* position_picked = 4 */ positionPicked);
}
@Override
public void log(UiEventLogger.UiEventEnum event, InstanceId instanceId) {
mUiEventLogger.logWithInstanceId(
event,
0,
null,
instanceId);
}
@Override
public InstanceId getInstanceId() {
if (mInstanceId == null) {
if (sInstanceIdSequence == null) {
sInstanceIdSequence = new InstanceIdSequence(SHARESHEET_INSTANCE_ID_MAX);
}
mInstanceId = sInstanceIdSequence.newInstanceId();
}
return mInstanceId;
}
}

View File

@@ -1578,6 +1578,7 @@ public class ResolverActivity extends Activity implements
viewPager.setCurrentItem(1);
}
setupViewVisibilities();
maybeLogProfileChange();
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS)
.setInt(viewPager.getCurrentItem())
@@ -1998,4 +1999,6 @@ public class ResolverActivity extends Activity implements
}
}
}
protected void maybeLogProfileChange() {}
}

View File

@@ -85,7 +85,7 @@ public class UiEventLoggerFake implements UiEventLogger {
}
@Override
public void logWithInstanceId(UiEventLogger.UiEventEnum event, int uid, String packageName,
public void logWithInstanceId(UiEventEnum event, int uid, String packageName,
InstanceId instance) {
final int eventId = event.getId();
if (eventId > 0) {

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2020 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.internal.app;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.FrameworkStatsLog;
import java.util.ArrayList;
import java.util.List;
public class ChooserActivityLoggerFake implements ChooserActivityLogger {
static class CallRecord {
// shared fields between all logs
public int atomId;
public String packageName;
public InstanceId instanceId;
// generic log field
public UiEventLogger.UiEventEnum event;
// share started fields
public String mimeType;
public int appProvidedDirect;
public int appProvidedApp;
public boolean isWorkprofile;
public int previewType;
public String intent;
// share completed fields
public int targetType;
public int positionPicked;
CallRecord(int atomId, UiEventLogger.UiEventEnum eventId,
String packageName, InstanceId instanceId) {
this.atomId = atomId;
this.packageName = packageName;
this.instanceId = instanceId;
this.event = eventId;
}
CallRecord(int atomId, String packageName, InstanceId instanceId, String mimeType,
int appProvidedDirect, int appProvidedApp, boolean isWorkprofile, int previewType,
String intent) {
this.atomId = atomId;
this.packageName = packageName;
this.instanceId = instanceId;
this.mimeType = mimeType;
this.appProvidedDirect = appProvidedDirect;
this.appProvidedApp = appProvidedApp;
this.isWorkprofile = isWorkprofile;
this.previewType = previewType;
this.intent = intent;
}
CallRecord(int atomId, String packageName, InstanceId instanceId, int targetType,
int positionPicked) {
this.atomId = atomId;
this.packageName = packageName;
this.instanceId = instanceId;
this.targetType = targetType;
this.positionPicked = positionPicked;
}
}
private List<CallRecord> mCalls = new ArrayList<>();
public int numCalls() {
return mCalls.size();
}
List<CallRecord> getCalls() {
return mCalls;
}
CallRecord get(int index) {
return mCalls.get(index);
}
UiEventLogger.UiEventEnum event(int index) {
return mCalls.get(index).event;
}
@Override
public void logShareStarted(int eventId, String packageName, String mimeType,
int appProvidedDirect, int appProvidedApp, boolean isWorkprofile, int previewType,
String intent) {
mCalls.add(new CallRecord(FrameworkStatsLog.SHARESHEET_STARTED, packageName,
getInstanceId(), mimeType, appProvidedDirect, appProvidedApp, isWorkprofile,
previewType, intent));
}
@Override
public void logShareTargetSelected(int targetType, String packageName, int positionPicked) {
mCalls.add(new CallRecord(FrameworkStatsLog.RANKING_SELECTED, packageName, getInstanceId(),
SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), positionPicked));
}
@Override
public void log(UiEventLogger.UiEventEnum event, InstanceId instanceId) {
mCalls.add(new CallRecord(FrameworkStatsLog.UI_EVENT_REPORTED,
event, "", instanceId));
}
@Override
public InstanceId getInstanceId() {
return InstanceId.fakeInstanceId(-1);
}
}

View File

@@ -77,6 +77,7 @@ import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
import org.junit.Before;
import org.junit.Ignore;
@@ -1430,6 +1431,251 @@ public class ChooserActivityTest {
.check(matches(isDisplayed()));
}
@Test
public void testAppTargetLogging() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
assertThat(activity.getAdapter().getCount(), is(2));
onView(withId(R.id.profile_button)).check(doesNotExist());
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
return true;
};
ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
onView(withText(toChoose.activityInfo.name))
.perform(click());
waitForIdle();
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
assertThat(logger.numCalls(), is(6));
// first one should be SHARESHEET_TRIGGERED uievent
assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(0).event.getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
// second one should be SHARESHEET_STARTED event
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
// third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(2).event.getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
// fourth and fifth are just artifacts of test set-up
// sixth one should be ranking atom with SHARESHEET_APP_TARGET_SELECTED event
assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
assertThat(logger.get(5).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_APP_TARGET_SELECTED.getId()));
}
@Test
public void testDirectTargetLogging() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
// Create direct share target
List<ChooserTarget> serviceTargets = createDirectShareTargets(1,
resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
// Start activity
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
// Insert the direct share target
Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
directShareToShortcutInfos.put(serviceTargets.get(0), null);
InstrumentationRegistry.getInstrumentation().runOnMainSync(
() -> activity.getAdapter().addServiceResults(
activity.createTestDisplayResolveInfo(sendIntent,
ri,
"testLabel",
"testInfo",
sendIntent,
/* resolveInfoPresentationGetter */ null),
serviceTargets,
TARGET_TYPE_CHOOSER_TARGET,
directShareToShortcutInfos,
null)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
// TODO: restructure the tests b/129870719
Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
activity.getAdapter().getCount(), is(3));
assertThat("Chooser should have exactly one selectable direct target",
activity.getAdapter().getSelectableServiceTargetCount(), is(1));
assertThat("The resolver info must match the resolver info used to create the target",
activity.getAdapter().getItem(0).getResolveInfo(), is(ri));
// Click on the direct target
String name = serviceTargets.get(0).getTitle().toString();
onView(withText(name))
.perform(click());
waitForIdle();
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
assertThat(logger.numCalls(), is(6));
// first one should be SHARESHEET_TRIGGERED uievent
assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(0).event.getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
// second one should be SHARESHEET_STARTED event
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
// third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(2).event.getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
// fourth and fifth are just artifacts of test set-up
// sixth one should be ranking atom with SHARESHEET_COPY_TARGET_SELECTED event
assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
assertThat(logger.get(5).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()));
}
@Test
public void testCopyTextToClipboardLogging() throws Exception {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(R.id.chooser_copy_button)).check(matches(isDisplayed()));
onView(withId(R.id.chooser_copy_button)).perform(click());
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
assertThat(logger.numCalls(), is(6));
// first one should be SHARESHEET_TRIGGERED uievent
assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(0).event.getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
// second one should be SHARESHEET_STARTED event
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
// third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(2).event.getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
// fourth and fifth are just artifacts of test set-up
// sixth one should be ranking atom with SHARESHEET_COPY_TARGET_SELECTED event
assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
assertThat(logger.get(5).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()));
}
@Test
public void testSwitchProfileLogging() throws InterruptedException {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
int workProfileTargets = 4;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
final ChooserWrapperActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
onView(withText(R.string.resolver_work_tab)).perform(click());
waitForIdle();
onView(withText(R.string.resolver_personal_tab)).perform(click());
waitForIdle();
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
assertThat(logger.numCalls(), is(8));
// first one should be SHARESHEET_TRIGGERED uievent
assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(0).event.getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
// second one should be SHARESHEET_STARTED event
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("TestType"));
assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
// third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(2).event.getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
// fourth one is artifact of test setup
// fifth one is switch to work profile
assertThat(logger.get(4).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(4).event.getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED.getId()));
// sixth one should be SHARESHEET_APP_LOAD_COMPLETE uievent
assertThat(logger.get(5).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(5).event.getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
// seventh one is artifact of test setup
// eigth one is switch to work profile
assertThat(logger.get(7).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
assertThat(logger.get(7).event.getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED.getId()));
}
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);

View File

@@ -145,6 +145,11 @@ public class ChooserWrapperActivity extends ChooserActivity {
return sOverrides.metricsLogger;
}
@Override
protected ChooserActivityLogger getChooserActivityLogger() {
return sOverrides.chooserActivityLogger;
}
@Override
public Cursor queryResolver(ContentResolver resolver, Uri uri) {
if (sOverrides.resolverCursor != null) {
@@ -205,6 +210,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
public boolean resolverForceException;
public Bitmap previewThumbnail;
public MetricsLogger metricsLogger;
public ChooserActivityLogger chooserActivityLogger;
public int alternateProfileSetting;
public Resources resources;
public UserHandle workProfileUserHandle;
@@ -223,6 +229,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
resolverListController = mock(ResolverListController.class);
workResolverListController = mock(ResolverListController.class);
metricsLogger = mock(MetricsLogger.class);
chooserActivityLogger = new ChooserActivityLoggerFake();
alternateProfileSetting = 0;
resources = null;
workProfileUserHandle = null;

View File

@@ -220,7 +220,7 @@ public class DataManager {
String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
@Event.EventType int eventType = mimeTypeToShareEventType(mimeType);
EventHistoryImpl eventHistory;
if (ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE.equals(event.getLaunchLocation())) {
if (ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE.equals(event.getLaunchLocation())) {
// Direct share event
if (appTarget.getShortcutInfo() == null) {
return;

View File

@@ -299,7 +299,7 @@ public final class DataManagerTest {
.build();
AppTargetEvent appTargetEvent =
new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE)
.build();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
@@ -319,7 +319,7 @@ public final class DataManagerTest {
.build();
AppTargetEvent appTargetEvent =
new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE)
.build();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
@@ -667,7 +667,7 @@ public final class DataManagerTest {
.build();
AppTargetEvent appTargetEvent =
new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE)
.build();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");