Merge "Converting StatsLogManager to kotlin" into main

This commit is contained in:
Treehugger Robot
2025-05-28 18:29:53 -07:00
committed by Android (Google) Code Review
6 changed files with 1149 additions and 1772 deletions
@@ -1,950 +0,0 @@
/*
* Copyright (C) 2018 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.quickstep.logging;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static androidx.core.util.Preconditions.checkNotNull;
import static androidx.core.util.Preconditions.checkState;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.ALL_APPS_CONTAINER;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_0;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_180;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_270;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_90;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__LANDSCAPE;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__PORTRAIT;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__SEASCAPE;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import android.util.StatsEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.slice.SliceItem;
import com.android.internal.jank.Cuj;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.Attribute;
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logger.LauncherAtom.FolderContainer.ParentContainerCase;
import com.android.launcher3.logger.LauncherAtom.FolderIcon;
import com.android.launcher3.logger.LauncherAtom.FromState;
import com.android.launcher3.logger.LauncherAtom.LauncherAttributes;
import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer;
import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer.SearchAttributes;
import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.CollectionInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LogConfig;
import com.android.launcher3.views.ActivityContext;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.SysUiStatsLog;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Inject;
/**
* This class calls StatsLog compile time generated methods.
*
* To see if the logs are properly sent to statsd, execute following command.
* <ul>
* $ wwdebug (to turn on the logcat printout)
* $ wwlogcat (see logcat with grep filter on)
* $ statsd_testdrive (see how ww is writing the proto to statsd buffer)
* </ul>
*/
public class StatsLogCompatManager extends StatsLogManager {
private static final String TAG = "StatsLog";
private static final String LATENCY_TAG = "StatsLatencyLog";
private static final String IMPRESSION_TAG = "StatsImpressionLog";
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
private static final boolean DEBUG = !Utilities.isRunningInTestHarness();
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
// from nano to lite, bake constant to prevent robo test failure.
private static final int DEFAULT_PAGE_INDEX = -2;
private static final int FOLDER_HIERARCHY_OFFSET = 100;
private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
private static final int EXTENDED_CONTAINERS_HIERARCHY_OFFSET = 300;
private static final int ALL_APPS_HIERARCHY_OFFSET = 400;
/**
* Flags for converting SearchAttribute to integer value.
*/
private static final int SEARCH_ATTRIBUTES_CORRECTED_QUERY = 1 << 0;
private static final int SEARCH_ATTRIBUTES_DIRECT_MATCH = 1 << 1;
private static final int SEARCH_ATTRIBUTES_ENTRY_STATE_ALL_APPS = 1 << 2;
private static final int SEARCH_ATTRIBUTES_ENTRY_STATE_QSB = 1 << 3;
private static final int SEARCH_ATTRIBUTES_ENTRY_STATE_OVERVIEW = 1 << 4;
private static final int SEARCH_ATTRIBUTES_ENTRY_STATE_TASKBAR = 1 << 5;
public static final CopyOnWriteArrayList<StatsLogConsumer> LOGS_CONSUMER =
new CopyOnWriteArrayList<>();
private StatsLogCompatManager(Context context) {
super(context);
}
/**
* This class is purely used to support dagger bindings to be overridden in launcher variants.
* Very similar to {@link dagger.assisted.AssistedFactory}. But
* {@link dagger.assisted.AssistedFactory} cannot be overridden and this makes dagger binding
* difficult.
*/
public static class StatsLogCompatManagerFactory extends StatsLogManagerFactory {
@Inject
StatsLogCompatManagerFactory() {
super();
}
public StatsLogManager create(Context context) {
return new StatsLogCompatManager(context);
}
}
@Override
protected StatsLogger createLogger() {
return new StatsCompatLogger(mContext, mActivityContext);
}
@Override
protected StatsLatencyLogger createLatencyLogger() {
return new StatsCompatLatencyLogger();
}
@Override
protected StatsImpressionLogger createImpressionLogger() {
return new StatsCompatImpressionLogger();
}
/**
* Synchronously writes an itemInfo to stats log
*/
@WorkerThread
public static void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
if (IS_VERBOSE) {
Log.d(TAG, String.format("\nwriteSnapshot(%d):\n%s", instanceId.getId(), info));
}
if (Utilities.isRunningInTestHarness()) {
return;
}
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
LAUNCHER_WORKSPACE_SNAPSHOT.getId() /* event_id */,
info.getItemCase().getNumber() /* target_id */,
instanceId.getId() /* instance_id */,
0 /* uid */,
getPackageName(info) /* package_name */,
getComponentName(info) /* component_name */,
getGridX(info, false) /* grid_x */,
getGridY(info, false) /* grid_y */,
getPageId(info) /* page_id */,
getGridX(info, true) /* grid_x_parent */,
getGridY(info, true) /* grid_y_parent */,
getParentPageId(info) /* page_id_parent */,
getHierarchy(info) /* hierarchy */,
info.getIsWork() /* is_work_profile */,
0 /* origin */,
getCardinality(info) /* cardinality */,
info.getWidget().getSpanX(),
info.getWidget().getSpanY(),
getFeatures(info),
getAttributes(info) /* attributes */
);
}
private static byte[] getAttributes(LauncherAtom.ItemInfo itemInfo) {
LauncherAttributes.Builder responseBuilder = LauncherAttributes.newBuilder();
itemInfo.getItemAttributesList().stream().map(Attribute::getNumber).forEach(
responseBuilder::addItemAttributes);
return responseBuilder.build().toByteArray();
}
/**
* Builds {@link StatsEvent} from {@link LauncherAtom.ItemInfo}. Used for pulled atom callback
* implementation.
*/
public static StatsEvent buildStatsEvent(LauncherAtom.ItemInfo info,
@Nullable InstanceId instanceId) {
return SysUiStatsLog.buildStatsEvent(
SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT, // atom ID,
LAUNCHER_WORKSPACE_SNAPSHOT.getId(), // event_id = 1;
info.getItemCase().getNumber(), // item_id = 2;
instanceId == null ? 0 : instanceId.getId(), //instance_id = 3;
0, //uid = 4 [(is_uid) = true];
getPackageName(info), // package_name = 5;
getComponentName(info), // component_name = 6;
getGridX(info, false), //grid_x = 7 [default = -1];
getGridY(info, false), //grid_y = 8 [default = -1];
getPageId(info), // page_id = 9 [default = -2];
getGridX(info, true), //grid_x_parent = 10 [default = -1];
getGridY(info, true), //grid_y_parent = 11 [default = -1];
getParentPageId(info), //page_id_parent = 12 [default = -2];
getHierarchy(info), // container_id = 13;
info.getIsWork(), // is_work_profile = 14;
0, // attribute_id = 15;
getCardinality(info), // cardinality = 16;
info.getWidget().getSpanX(), // span_x = 17 [default = 1];
info.getWidget().getSpanY(), // span_y = 18 [default = 1];
getAttributes(info) /* attributes = 19 [(log_mode) = MODE_BYTES] */,
info.getIsKidsMode() /* is_kids_mode = 20 */
);
}
/**
* Helps to construct and write statsd compatible log message.
*/
private static class StatsCompatLogger implements StatsLogger {
private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
static {
DEFAULT_ITEM_INFO.itemType = ITEM_TYPE_NON_ACTIONABLE;
}
private final Context mContext;
private final Optional<ActivityContext> mActivityContext;
private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
private OptionalInt mRank = OptionalInt.empty();
private Optional<ContainerInfo> mContainerInfo = Optional.empty();
private int mSrcState = LAUNCHER_STATE_UNSPECIFIED;
private int mDstState = LAUNCHER_STATE_UNSPECIFIED;
private Optional<FromState> mFromState = Optional.empty();
private Optional<ToState> mToState = Optional.empty();
private Optional<String> mEditText = Optional.empty();
private SliceItem mSliceItem;
private LauncherAtom.Slice mSlice;
private Optional<Integer> mCardinality = Optional.empty();
private int mInputType = SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__UNKNOWN;
private Optional<Integer> mFeatures = Optional.empty();
private Optional<String> mPackageName = Optional.empty();
/**
* Indicates the current rotation of the display. Uses {@link android.view.Surface values.}
*/
private final int mDisplayRotation;
StatsCompatLogger(Context context, ActivityContext activityContext) {
mContext = context;
mActivityContext = Optional.ofNullable(activityContext);
mDisplayRotation = DisplayController.INSTANCE.get(mContext).getInfo().rotation;
}
@Override
public StatsLogger withItemInfo(ItemInfo itemInfo) {
if (mContainerInfo.isPresent()) {
throw new IllegalArgumentException(
"ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
}
this.mItemInfo = itemInfo;
return this;
}
@Override
public StatsLogger withInstanceId(InstanceId instanceId) {
this.mInstanceId = instanceId;
return this;
}
@Override
public StatsLogger withRank(int rank) {
this.mRank = OptionalInt.of(rank);
return this;
}
@Override
public StatsLogger withSrcState(int srcState) {
this.mSrcState = srcState;
return this;
}
@Override
public StatsLogger withDstState(int dstState) {
this.mDstState = dstState;
return this;
}
@Override
public StatsLogger withContainerInfo(ContainerInfo containerInfo) {
checkState(mItemInfo == DEFAULT_ITEM_INFO,
"ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
this.mContainerInfo = Optional.of(containerInfo);
return this;
}
@Override
public StatsLogger withFromState(FromState fromState) {
this.mFromState = Optional.of(fromState);
return this;
}
@Override
public StatsLogger withToState(ToState toState) {
this.mToState = Optional.of(toState);
return this;
}
@Override
public StatsLogger withEditText(String editText) {
this.mEditText = Optional.of(editText);
return this;
}
@Override
public StatsLogger withSliceItem(@NonNull SliceItem sliceItem) {
checkState(mItemInfo == DEFAULT_ITEM_INFO && mSlice == null,
"ItemInfo, Slice and SliceItem are mutual exclusive; cannot set more than one"
+ " of them.");
this.mSliceItem = checkNotNull(sliceItem, "expected valid sliceItem but received null");
return this;
}
@Override
public StatsLogger withSlice(LauncherAtom.Slice slice) {
checkState(mItemInfo == DEFAULT_ITEM_INFO && mSliceItem == null,
"ItemInfo, Slice and SliceItem are mutual exclusive; cannot set more than one"
+ " of them.");
checkNotNull(slice, "expected valid slice but received null");
checkNotNull(slice.getUri(), "expected valid slice uri but received null");
this.mSlice = slice;
return this;
}
@Override
public StatsLogger withCardinality(int cardinality) {
this.mCardinality = Optional.of(cardinality);
return this;
}
@Override
public StatsLogger withInputType(int inputType) {
this.mInputType = inputType;
return this;
}
@Override
public StatsLogger withFeatures(int feature) {
this.mFeatures = Optional.of(feature);
return this;
}
@Override
public StatsLogger withPackageName(@Nullable String packageName) {
mPackageName = Optional.ofNullable(packageName);
return this;
}
@Override
public void log(EventEnum event) {
if (DEBUG) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
event.getId() + "";
Log.d(TAG, name);
}
if (mSlice == null && mSliceItem != null) {
mSlice = LauncherAtom.Slice.newBuilder().setUri(
mSliceItem.getSlice().getUri().toString()).build();
}
if (mSlice != null) {
Executors.MODEL_EXECUTOR.execute(
() -> {
LauncherAtom.ItemInfo.Builder itemInfoBuilder =
LauncherAtom.ItemInfo.newBuilder().setSlice(mSlice);
mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
write(event, applyOverwrites(itemInfoBuilder.build()));
});
return;
}
if (mItemInfo == null) {
return;
}
// Item is inside a collection, fetch collection info in a BG thread
// and then write to StatsLog.
if (mItemInfo.container < 0) {
LauncherAppState.INSTANCE.get(mContext).getModel().enqueueModelUpdateTask(
(taskController, dataModel, apps) -> write(event, applyOverwrites(
mItemInfo.buildProto(
(CollectionInfo) dataModel.itemsIdMap
.get(mItemInfo.container),
mContext))));
}
}
@Override
public void sendToInteractionJankMonitor(EventEnum event, View view) {
if (!(event instanceof LauncherEvent)) {
return;
}
switch ((LauncherEvent) event) {
case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN:
InteractionJankMonitorWrapper.begin(
view,
Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL);
break;
case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END:
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL);
break;
case LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN:
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_LOCK);
break;
case LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END:
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_LOCK);
break;
case LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN:
InteractionJankMonitorWrapper.begin(
view,
Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK);
break;
case LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END:
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK);
break;
case LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_BEGIN:
InteractionJankMonitorWrapper.begin(
view,
Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND);
break;
case LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_END:
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND);
break;
case LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_BEGIN:
InteractionJankMonitorWrapper.begin(
view,
Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK);
break;
case LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_END:
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK);
break;
default:
break;
}
}
private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) {
LauncherAtom.ItemInfo.Builder itemInfoBuilder = atomInfo.toBuilder();
mRank.ifPresent(itemInfoBuilder::setRank);
mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
mActivityContext.ifPresent(activityContext ->
activityContext.applyOverwritesToLogItem(itemInfoBuilder));
if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) {
FolderIcon.Builder folderIconBuilder = itemInfoBuilder
.getFolderIcon()
.toBuilder();
mFromState.ifPresent(folderIconBuilder::setFromLabelState);
mToState.ifPresent(folderIconBuilder::setToLabelState);
mEditText.ifPresent(folderIconBuilder::setLabelInfo);
itemInfoBuilder.setFolderIcon(folderIconBuilder);
}
return itemInfoBuilder.build();
}
@WorkerThread
private void write(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
InstanceId instanceId = mInstanceId;
int srcState = mSrcState;
int dstState = mDstState;
int inputType = mInputType;
String packageName = mPackageName.orElseGet(() -> getPackageName(atomInfo));
if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
event.getId() + "";
StringBuilder logStringBuilder = new StringBuilder("\n");
if (instanceId != DEFAULT_INSTANCE_ID) {
logStringBuilder.append(String.format("InstanceId:%s ", instanceId));
}
logStringBuilder.append(name);
if (srcState != LAUNCHER_STATE_UNSPECIFIED
|| dstState != LAUNCHER_STATE_UNSPECIFIED) {
logStringBuilder.append(
String.format("(State:%s->%s)", getStateString(srcState),
getStateString(dstState)));
}
if (atomInfo.hasContainerInfo()) {
logStringBuilder.append("\n").append(atomInfo);
}
if (!TextUtils.isEmpty(packageName)) {
logStringBuilder.append(String.format("\nPackage name: %s", packageName));
}
Log.d(TAG, logStringBuilder.toString());
}
for (StatsLogConsumer consumer : LOGS_CONSUMER) {
consumer.consume(event, atomInfo);
}
// TODO: remove this when b/231648228 is fixed.
if (Utilities.isRunningInTestHarness()) {
return;
}
int cardinality = mCardinality.orElseGet(() -> getCardinality(atomInfo));
int features = mFeatures.orElseGet(() -> getFeatures(atomInfo));
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_EVENT,
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
srcState,
dstState,
null /* launcher extensions, deprecated */,
false /* quickstep_enabled, deprecated */,
event.getId() /* event_id */,
atomInfo.getItemCase().getNumber() /* target_id */,
instanceId.getId() /* instance_id TODO */,
0 /* uid TODO */,
packageName /* package_name */,
getComponentName(atomInfo) /* component_name */,
getGridX(atomInfo, false) /* grid_x */,
getGridY(atomInfo, false) /* grid_y */,
getPageId(atomInfo) /* page_id */,
getGridX(atomInfo, true) /* grid_x_parent */,
getGridY(atomInfo, true) /* grid_y_parent */,
getParentPageId(atomInfo) /* page_id_parent */,
getHierarchy(atomInfo) /* hierarchy */,
false /* is_work_profile, deprecated */,
atomInfo.getRank() /* rank */,
atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
cardinality /* cardinality */,
features /* features */,
getSearchAttributes(atomInfo) /* searchAttributes */,
getAttributes(atomInfo) /* attributes */,
inputType /* input_type */,
atomInfo.getUserType() /* user_type */,
getDisplayRotation() /* display_rotation */,
getRecentsOrientationHandler(atomInfo) /* recents_orientation_handler */);
}
private int getDisplayRotation() {
return switch (mDisplayRotation) {
case ROTATION_90 -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_90;
case ROTATION_180 -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_180;
case ROTATION_270 -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_270;
default -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_0;
};
}
private int getRecentsOrientationHandler(LauncherAtom.ItemInfo itemInfo) {
var orientationHandler =
itemInfo.getContainerInfo().getTaskSwitcherContainer().getOrientationHandler();
return switch (orientationHandler) {
case PORTRAIT -> LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__PORTRAIT;
case LANDSCAPE -> LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__LANDSCAPE;
case SEASCAPE -> LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__SEASCAPE;
};
}
}
/**
* Helps to construct and log statsd compatible latency events.
*/
private static class StatsCompatLatencyLogger implements StatsLatencyLogger {
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
private LatencyType mType = LatencyType.UNKNOWN;
private int mPackageId = 0;
private long mLatencyInMillis;
private int mQueryLength = -1;
private int mSubEventType = 0;
private int mCardinality = -1;
@Override
public StatsLatencyLogger withInstanceId(InstanceId instanceId) {
this.mInstanceId = instanceId;
return this;
}
@Override
public StatsLatencyLogger withType(LatencyType type) {
this.mType = type;
return this;
}
@Override
public StatsLatencyLogger withPackageId(int packageId) {
this.mPackageId = packageId;
return this;
}
@Override
public StatsLatencyLogger withLatency(long latencyInMillis) {
this.mLatencyInMillis = latencyInMillis;
return this;
}
@Override
public StatsLatencyLogger withQueryLength(int queryLength) {
this.mQueryLength = queryLength;
return this;
}
@Override
public StatsLatencyLogger withSubEventType(int type) {
this.mSubEventType = type;
return this;
}
@Override
public StatsLatencyLogger withCardinality(int cardinality) {
this.mCardinality = cardinality;
return this;
}
@Override
public void log(EventEnum event) {
if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
event.getId() + "";
StringBuilder logStringBuilder = new StringBuilder("\n");
logStringBuilder.append(String.format("InstanceId:%s ", mInstanceId));
logStringBuilder.append(String.format("%s=%sms", name, mLatencyInMillis));
Log.d(LATENCY_TAG, logStringBuilder.toString());
}
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_LATENCY,
event.getId(), // event_id
mInstanceId.getId(), // instance_id
mPackageId, // package_id
mLatencyInMillis, // latency_in_millis
mType.getId(), //type
mQueryLength, // query_length
mSubEventType, // sub_event_type
mCardinality // cardinality
);
}
}
/**
* Helps to construct and log statsd compatible impression events.
*/
private static class StatsCompatImpressionLogger implements StatsImpressionLogger {
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
private State mLauncherState = State.UNKNOWN;
private int mQueryLength = -1;
// Fields used for Impression Logging V2.
private int mResultType;
private boolean mAboveKeyboard = false;
private int mUid;
private int mResultSource;
@Override
public StatsImpressionLogger withInstanceId(InstanceId instanceId) {
this.mInstanceId = instanceId;
return this;
}
@Override
public StatsImpressionLogger withState(State state) {
this.mLauncherState = state;
return this;
}
@Override
public StatsImpressionLogger withQueryLength(int queryLength) {
this.mQueryLength = queryLength;
return this;
}
@Override
public StatsImpressionLogger withResultType(int resultType) {
mResultType = resultType;
return this;
}
@Override
public StatsImpressionLogger withAboveKeyboard(boolean aboveKeyboard) {
mAboveKeyboard = aboveKeyboard;
return this;
}
@Override
public StatsImpressionLogger withUid(int uid) {
mUid = uid;
return this;
}
@Override
public StatsImpressionLogger withResultSource(int resultSource) {
mResultSource = resultSource;
return this;
}
@Override
public void log(EventEnum event) {
if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
event.getId() + "";
StringBuilder logStringBuilder = new StringBuilder("\n");
logStringBuilder.append(String.format("InstanceId:%s ", mInstanceId));
logStringBuilder.append(String.format("ImpressionEvent:%s ", name));
logStringBuilder.append(String.format("\n\tLauncherState = %s ", mLauncherState));
logStringBuilder.append(String.format("\tQueryLength = %s ", mQueryLength));
logStringBuilder.append(String.format(
"\n\t ResultType = %s is_above_keyboard = %s"
+ " uid = %s result_source = %s",
mResultType,
mAboveKeyboard, mUid, mResultSource));
Log.d(IMPRESSION_TAG, logStringBuilder.toString());
}
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_IMPRESSION_EVENT_V2,
event.getId(), // event_id
mInstanceId.getId(), // instance_id
mLauncherState.getLauncherState(), // state
mQueryLength, // query_length
mResultType, //result type
mAboveKeyboard, // above keyboard
mUid, // uid
mResultSource // result source
);
}
}
private static int getCardinality(LauncherAtom.ItemInfo info) {
if (Utilities.isRunningInTestHarness()) {
return 0;
}
switch (info.getContainerInfo().getContainerCase()) {
case PREDICTED_HOTSEAT_CONTAINER:
return info.getContainerInfo().getPredictedHotseatContainer().getCardinality();
case TASK_BAR_CONTAINER:
return info.getContainerInfo().getTaskBarContainer().getCardinality();
case SEARCH_RESULT_CONTAINER:
return info.getContainerInfo().getSearchResultContainer().getQueryLength();
case EXTENDED_CONTAINERS:
ExtendedContainers extendedCont = info.getContainerInfo().getExtendedContainers();
if (extendedCont.getContainerCase() == DEVICE_SEARCH_RESULT_CONTAINER) {
DeviceSearchResultContainer deviceSearchResultCont = extendedCont
.getDeviceSearchResultContainer();
return deviceSearchResultCont.hasQueryLength() ? deviceSearchResultCont
.getQueryLength() : -1;
}
default:
return switch (info.getItemCase()) {
case FOLDER_ICON -> info.getFolderIcon().getCardinality();
case TASK_VIEW -> info.getTaskView().getCardinality();
default -> 0;
};
}
}
private static String getPackageName(LauncherAtom.ItemInfo info) {
return switch (info.getItemCase()) {
case APPLICATION -> info.getApplication().getPackageName();
case SHORTCUT -> info.getShortcut().getShortcutName();
case WIDGET -> info.getWidget().getPackageName();
case TASK -> info.getTask().getPackageName();
case SEARCH_ACTION_ITEM -> info.getSearchActionItem().getPackageName();
default -> null;
};
}
private static String getComponentName(LauncherAtom.ItemInfo info) {
return switch (info.getItemCase()) {
case APPLICATION -> info.getApplication().getComponentName();
case SHORTCUT -> info.getShortcut().getShortcutName();
case WIDGET -> info.getWidget().getComponentName();
case TASK -> info.getTask().getComponentName();
case TASK_VIEW -> info.getTaskView().getComponentName();
case SEARCH_ACTION_ITEM -> info.getSearchActionItem().getTitle();
case SLICE -> info.getSlice().getUri();
default -> null;
};
}
private static int getGridX(LauncherAtom.ItemInfo info, boolean parent) {
ContainerInfo containerInfo = info.getContainerInfo();
if (containerInfo.getContainerCase() == FOLDER) {
if (parent) {
return containerInfo.getFolder().getWorkspace().getGridX();
} else {
return containerInfo.getFolder().getGridX();
}
} else if (containerInfo.getContainerCase() == EXTENDED_CONTAINERS) {
return containerInfo.getExtendedContainers()
.getDeviceSearchResultContainer().getGridX();
} else {
return containerInfo.getWorkspace().getGridX();
}
}
private static int getGridY(LauncherAtom.ItemInfo info, boolean parent) {
if (info.getContainerInfo().getContainerCase() == FOLDER) {
if (parent) {
return info.getContainerInfo().getFolder().getWorkspace().getGridY();
} else {
return info.getContainerInfo().getFolder().getGridY();
}
} else {
return info.getContainerInfo().getWorkspace().getGridY();
}
}
private static int getPageId(LauncherAtom.ItemInfo info) {
return switch (info.getItemCase()) {
case TASK -> info.getTask().getIndex();
case TASK_VIEW -> info.getTaskView().getIndex();
default -> getPageIdFromContainerInfo(info.getContainerInfo());
};
}
private static int getPageIdFromContainerInfo(LauncherAtom.ContainerInfo containerInfo) {
return switch (containerInfo.getContainerCase()) {
case FOLDER -> containerInfo.getFolder().getPageIndex();
case HOTSEAT -> containerInfo.getHotseat().getIndex();
case PREDICTED_HOTSEAT_CONTAINER ->
containerInfo.getPredictedHotseatContainer().getIndex();
case TASK_BAR_CONTAINER -> containerInfo.getTaskBarContainer().getIndex();
default -> containerInfo.getWorkspace().getPageIndex();
};
}
private static int getParentPageId(LauncherAtom.ItemInfo info) {
return switch (info.getContainerInfo().getContainerCase()) {
case FOLDER -> {
if (info.getContainerInfo().getFolder().getParentContainerCase()
== ParentContainerCase.HOTSEAT) {
yield info.getContainerInfo().getFolder().getHotseat().getIndex();
}
yield info.getContainerInfo().getFolder().getWorkspace().getPageIndex();
}
case SEARCH_RESULT_CONTAINER ->
info.getContainerInfo().getSearchResultContainer().getWorkspace()
.getPageIndex();
default -> info.getContainerInfo().getWorkspace().getPageIndex();
};
}
private static int getHierarchy(LauncherAtom.ItemInfo info) {
if (Utilities.isRunningInTestHarness()) {
return 0;
}
if (info.getContainerInfo().getContainerCase() == FOLDER) {
return info.getContainerInfo().getFolder().getParentContainerCase().getNumber()
+ FOLDER_HIERARCHY_OFFSET;
} else if (info.getContainerInfo().getContainerCase() == SEARCH_RESULT_CONTAINER) {
return info.getContainerInfo().getSearchResultContainer().getParentContainerCase()
.getNumber() + SEARCH_RESULT_HIERARCHY_OFFSET;
} else if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS) {
return info.getContainerInfo().getExtendedContainers().getContainerCase().getNumber()
+ EXTENDED_CONTAINERS_HIERARCHY_OFFSET;
} else if (info.getContainerInfo().getContainerCase() == ALL_APPS_CONTAINER) {
return info.getContainerInfo().getAllAppsContainer().getParentContainerCase()
.getNumber() + ALL_APPS_HIERARCHY_OFFSET;
} else {
return info.getContainerInfo().getContainerCase().getNumber();
}
}
private static String getStateString(int state) {
return switch (state) {
case LAUNCHER_UICHANGED__DST_STATE__BACKGROUND -> "BACKGROUND";
case LAUNCHER_UICHANGED__DST_STATE__HOME -> "HOME";
case LAUNCHER_UICHANGED__DST_STATE__OVERVIEW -> "OVERVIEW";
case LAUNCHER_UICHANGED__DST_STATE__ALLAPPS -> "ALLAPPS";
default -> "INVALID";
};
}
private static int getFeatures(LauncherAtom.ItemInfo info) {
return switch (info.getItemCase()) {
case WIDGET -> info.getWidget().getWidgetFeatures();
case TASK_VIEW -> info.getTaskView().getType();
default -> 0;
};
}
private static int getSearchAttributes(LauncherAtom.ItemInfo info) {
if (Utilities.isRunningInTestHarness()) {
return 0;
}
ContainerInfo containerInfo = info.getContainerInfo();
if (containerInfo.getContainerCase() == EXTENDED_CONTAINERS
&& containerInfo.getExtendedContainers().getContainerCase()
== DEVICE_SEARCH_RESULT_CONTAINER
&& containerInfo.getExtendedContainers()
.getDeviceSearchResultContainer().hasSearchAttributes()
) {
return searchAttributesToInt(containerInfo.getExtendedContainers()
.getDeviceSearchResultContainer().getSearchAttributes());
}
return 0;
}
private static int searchAttributesToInt(SearchAttributes searchAttributes) {
int response = 0;
if (searchAttributes.getCorrectedQuery()) {
response = response | SEARCH_ATTRIBUTES_CORRECTED_QUERY;
}
if (searchAttributes.getDirectMatch()) {
response = response | SEARCH_ATTRIBUTES_DIRECT_MATCH;
}
if (searchAttributes.getEntryState() == SearchAttributes.EntryState.ALL_APPS) {
response = response | SEARCH_ATTRIBUTES_ENTRY_STATE_ALL_APPS;
} else if (searchAttributes.getEntryState() == SearchAttributes.EntryState.QSB) {
response = response | SEARCH_ATTRIBUTES_ENTRY_STATE_QSB;
} else if (searchAttributes.getEntryState() == SearchAttributes.EntryState.OVERVIEW) {
response = response | SEARCH_ATTRIBUTES_ENTRY_STATE_OVERVIEW;
} else if (searchAttributes.getEntryState() == SearchAttributes.EntryState.TASKBAR) {
response = response | SEARCH_ATTRIBUTES_ENTRY_STATE_TASKBAR;
}
return response;
}
/**
* Interface to get stats log while it is dispatched to the system
*/
public interface StatsLogConsumer {
@WorkerThread
void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo);
}
}
@@ -0,0 +1,798 @@
/*
* Copyright (C) 2018 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.quickstep.logging
import android.content.Context
import android.util.Log
import android.util.StatsEvent
import android.view.Surface
import android.view.View
import androidx.annotation.WorkerThread
import androidx.slice.SliceItem
import com.android.internal.jank.Cuj
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.Utilities
import com.android.launcher3.logger.LauncherAtom
import com.android.launcher3.logger.LauncherAtom.ContainerInfo
import com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.ALL_APPS_CONTAINER
import com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS
import com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER
import com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.HOTSEAT
import com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.PREDICTED_HOTSEAT_CONTAINER
import com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER
import com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.TASK_BAR_CONTAINER
import com.android.launcher3.logger.LauncherAtom.FolderContainer.ParentContainerCase
import com.android.launcher3.logger.LauncherAtom.FromState
import com.android.launcher3.logger.LauncherAtom.ItemInfo.ItemCase.APPLICATION
import com.android.launcher3.logger.LauncherAtom.ItemInfo.ItemCase.FOLDER_ICON
import com.android.launcher3.logger.LauncherAtom.ItemInfo.ItemCase.SEARCH_ACTION_ITEM
import com.android.launcher3.logger.LauncherAtom.ItemInfo.ItemCase.SHORTCUT
import com.android.launcher3.logger.LauncherAtom.ItemInfo.ItemCase.SLICE
import com.android.launcher3.logger.LauncherAtom.ItemInfo.ItemCase.TASK
import com.android.launcher3.logger.LauncherAtom.ItemInfo.ItemCase.TASK_VIEW
import com.android.launcher3.logger.LauncherAtom.ItemInfo.ItemCase.WIDGET
import com.android.launcher3.logger.LauncherAtom.LauncherAttributes
import com.android.launcher3.logger.LauncherAtom.Slice
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer.OrientationHandler.LANDSCAPE
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer.OrientationHandler.PORTRAIT
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer.OrientationHandler.SEASCAPE
import com.android.launcher3.logger.LauncherAtom.ToState
import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer.SearchAttributes
import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer.SearchAttributes.EntryState.ALL_APPS
import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer.SearchAttributes.EntryState.OVERVIEW
import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer.SearchAttributes.EntryState.QSB
import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer.SearchAttributes.EntryState.TASKBAR
import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER
import com.android.launcher3.logging.InstanceId
import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_BEGIN
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_END
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_BEGIN
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_END
import com.android.launcher3.logging.StatsLogManager.StatsImpressionLogger.State
import com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType
import com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.UNKNOWN
import com.android.launcher3.model.data.CollectionInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.Executors
import com.android.launcher3.util.LogConfig
import com.android.launcher3.views.ActivityContext
import com.android.systemui.shared.system.InteractionJankMonitorWrapper
import com.android.systemui.shared.system.SysUiStatsLog
import java.util.concurrent.CopyOnWriteArrayList
import javax.inject.Inject
/**
* This class calls StatsLog compile time generated methods.
*
* To see if the logs are properly sent to statsd, execute following command.
*
* $ wwdebug (to turn on the logcat printout) $ wwlogcat (see logcat with grep filter on) $
* statsd_testdrive (see how ww is writing the proto to statsd buffer)
*/
class StatsLogCompatManager private constructor(context: Context) : StatsLogManager(context) {
/**
* This class is purely used to support dagger bindings to be overridden in launcher variants.
* Very similar to [dagger.assisted.AssistedFactory]. But [dagger.assisted.AssistedFactory]
* cannot be overridden and this makes dagger binding difficult.
*/
class StatsLogCompatManagerFactory @Inject internal constructor() : StatsLogManagerFactory() {
override fun create(context: Context): StatsLogManager {
return StatsLogCompatManager(context)
}
}
override fun createLogger(): StatsLogger {
return StatsCompatLogger(mContext, mActivityContext)
}
override fun createLatencyLogger(): StatsLatencyLogger {
return StatsCompatLatencyLogger()
}
override fun createImpressionLogger(): StatsImpressionLogger {
return StatsCompatImpressionLogger()
}
/** Helps to construct and write statsd compatible log message. */
private class StatsCompatLogger(
private val context: Context,
private val activityContext: ActivityContext?,
) : StatsLogger {
private var mItemInfo: ItemInfo? = DEFAULT_ITEM_INFO
private var mInstanceId: InstanceId? = DEFAULT_INSTANCE_ID
private var mRank: Int? = null
private var mContainerInfo: ContainerInfo? = null
private var mSrcState = LAUNCHER_STATE_UNSPECIFIED
private var mDstState = LAUNCHER_STATE_UNSPECIFIED
private var mFromState: FromState? = null
private var mToState: ToState? = null
private var mEditText: String? = null
private var mSliceItem: SliceItem? = null
private var mSlice: Slice? = null
private var mCardinality: Int? = null
private var mInputType = SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__UNKNOWN
private var mFeatures: Int? = null
private var mPackageName: String? = null
/** Indicates the current rotation of the display. Uses [values.][android.view.Surface] */
private val mDisplayRotation = DisplayController.INSTANCE[context].info.rotation
override fun withItemInfo(itemInfo: ItemInfo?) = apply {
require(mContainerInfo == null) {
"ItemInfo and ContainerInfo are mutual exclusive; cannot log both."
}
mItemInfo = itemInfo
}
override fun withInstanceId(instanceId: InstanceId?) = apply { mInstanceId = instanceId }
override fun withRank(rank: Int) = apply { mRank = rank }
override fun withSrcState(srcState: Int) = apply { mSrcState = srcState }
override fun withDstState(dstState: Int) = apply { mDstState = dstState }
override fun withContainerInfo(containerInfo: ContainerInfo?) = apply {
require(mItemInfo === DEFAULT_ITEM_INFO) {
"ItemInfo and ContainerInfo are mutual exclusive; cannot log both."
}
this.mContainerInfo = containerInfo
}
override fun withFromState(fromState: FromState?) = apply { this.mFromState = fromState }
override fun withToState(toState: ToState?) = apply { this.mToState = toState }
override fun withEditText(editText: String?) = apply { this.mEditText = editText }
override fun withSliceItem(sliceItem: SliceItem) = apply {
require(mItemInfo === DEFAULT_ITEM_INFO && mSlice == null) {
"ItemInfo, Slice and SliceItem are mutual exclusive; cannot set more than one of them."
}
this.mSliceItem = sliceItem
}
override fun withSlice(slice: Slice) = apply {
require(mItemInfo === DEFAULT_ITEM_INFO && mSliceItem == null) {
"ItemInfo, Slice and SliceItem are mutual exclusive; cannot set more than one of them."
}
this.mSlice = slice
}
override fun withCardinality(cardinality: Int) = apply { this.mCardinality = cardinality }
override fun withInputType(inputType: Int) = apply { this.mInputType = inputType }
override fun withFeatures(feature: Int) = apply { this.mFeatures = feature }
override fun withPackageName(packageName: String?) = apply { mPackageName = packageName }
override fun log(event: EventEnum) {
if (DEBUG) {
val name = if (event is Enum<*>) event.name else event.id.toString() + ""
Log.d(TAG, name)
}
if (mSlice == null && mSliceItem != null)
mSlice = Slice.newBuilder().setUri(mSliceItem!!.slice!!.uri.toString()).build()
if (mSlice != null) {
Executors.MODEL_EXECUTOR.execute {
val itemInfoBuilder = LauncherAtom.ItemInfo.newBuilder().setSlice(mSlice)
mContainerInfo?.let { itemInfoBuilder.setContainerInfo(it) }
write(event, applyOverwrites(itemInfoBuilder.build()))
}
return
}
val info = mItemInfo ?: return
// If the item is inside a collection, fetch collection info in a BG thread
// and then write to StatsLog.
LauncherAppState.INSTANCE[context].model.enqueueModelUpdateTask { _, dataModel, _ ->
write(
event,
applyOverwrites(
info.buildProto(
dataModel.itemsIdMap[info.container] as CollectionInfo?,
context,
)
),
)
}
}
override fun sendToInteractionJankMonitor(event: EventEnum?, view: View?) {
if (event !is LauncherEvent) return
when (event) {
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL)
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END ->
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL)
LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_LOCK)
LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END ->
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_LOCK)
LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK)
LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END ->
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK)
LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_BEGIN ->
InteractionJankMonitorWrapper.begin(
view,
Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND,
)
LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_END ->
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND)
LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_BEGIN ->
InteractionJankMonitorWrapper.begin(
view,
Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK,
)
LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_END ->
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK)
else -> {}
}
}
fun applyOverwrites(atomInfo: LauncherAtom.ItemInfo): LauncherAtom.ItemInfo =
atomInfo
.toBuilder()
.apply {
mRank?.let { setRank(it) }
mContainerInfo?.let { setContainerInfo(it) }
activityContext?.applyOverwritesToLogItem(this)
if (mFromState != null || mToState != null || mEditText != null) {
val folderIconBuilder = folderIcon.toBuilder()
mFromState?.let { folderIconBuilder.setFromLabelState(it) }
mToState?.let { folderIconBuilder.setToLabelState(it) }
mEditText?.let { folderIconBuilder.setLabelInfo(it) }
setFolderIcon(folderIconBuilder)
}
}
.build()
@WorkerThread
fun write(event: EventEnum, atomInfo: LauncherAtom.ItemInfo) {
val instanceId = mInstanceId
val srcState = mSrcState
val dstState = mDstState
val inputType = mInputType
val packageName = mPackageName ?: getPackageName(atomInfo)
if (IS_VERBOSE) {
val name =
if (event is Enum<*>) (event as Enum<*>).name else event.id.toString() + ""
val logStringBuilder = StringBuilder("\n")
if (instanceId !== DEFAULT_INSTANCE_ID)
logStringBuilder.append("InstanceId:$instanceId")
logStringBuilder.append(name)
if (
srcState != LAUNCHER_STATE_UNSPECIFIED || dstState != LAUNCHER_STATE_UNSPECIFIED
) {
logStringBuilder.append(
"(State:${getStateString(srcState)}->${getStateString(dstState)})"
)
}
if (atomInfo.hasContainerInfo()) logStringBuilder.append("\n$atomInfo")
packageName?.let { logStringBuilder.append("\nPackage name: $it") }
Log.d(TAG, logStringBuilder.toString())
}
for (consumer in LOGS_CONSUMER) {
consumer.consume(event, atomInfo)
}
// TODO: remove this when b/231648228 is fixed.
if (Utilities.isRunningInTestHarness()) {
return
}
val cardinality = mCardinality ?: getCardinality(atomInfo)
val features = mFeatures ?: getFeatures(atomInfo)
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_EVENT,
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION, /* deprecated */
srcState,
dstState,
null, /* launcher extensions, deprecated */
false, /* quickstep_enabled, deprecated */
event.id, /* event_id */
atomInfo.itemCase.number, /* target_id */
instanceId!!.id, /* instance_id TODO */
0, /* uid TODO */
packageName, /* package_name */
getComponentName(atomInfo), /* component_name */
getGridX(atomInfo, false), /* grid_x */
getGridY(atomInfo, false), /* grid_y */
getPageId(atomInfo), /* page_id */
getGridX(atomInfo, true), /* grid_x_parent */
getGridY(atomInfo, true), /* grid_y_parent */
getParentPageId(atomInfo), /* page_id_parent */
getHierarchy(atomInfo), /* hierarchy */
false, /* is_work_profile, deprecated */
atomInfo.rank, /* rank */
atomInfo.folderIcon.fromLabelState.number, /* fromState */
atomInfo.folderIcon.toLabelState.number, /* toState */
atomInfo.folderIcon.labelInfo, /* edittext */
cardinality, /* cardinality */
features, /* features */
getSearchAttributes(atomInfo), /* searchAttributes */
getAttributes(atomInfo), /* attributes */
inputType, /* input_type */
atomInfo.userType, /* user_type */
displayRotation, /* display_rotation */
getRecentsOrientationHandler(atomInfo), /* recents_orientation_handler */
)
}
val displayRotation: Int
get() =
when (mDisplayRotation) {
Surface.ROTATION_90 ->
SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_90
Surface.ROTATION_180 ->
SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_180
Surface.ROTATION_270 ->
SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_270
else -> SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_0
}
fun getRecentsOrientationHandler(itemInfo: LauncherAtom.ItemInfo): Int {
val orientationHandler = itemInfo.containerInfo.taskSwitcherContainer.orientationHandler
return when (orientationHandler) {
PORTRAIT -> SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__PORTRAIT
LANDSCAPE ->
SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__LANDSCAPE
SEASCAPE -> SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__SEASCAPE
}
}
companion object {
private val DEFAULT_ITEM_INFO = ItemInfo()
init {
DEFAULT_ITEM_INFO.itemType = Favorites.ITEM_TYPE_NON_ACTIONABLE
}
}
}
/** Helps to construct and log statsd compatible latency events. */
private class StatsCompatLatencyLogger : StatsLatencyLogger {
private var mInstanceId: InstanceId? = DEFAULT_INSTANCE_ID
private var mType: LatencyType? = UNKNOWN
private var mPackageId = 0
private var mLatencyInMillis: Long = 0
private var mQueryLength = -1
private var mSubEventType = 0
private var mCardinality = -1
override fun withInstanceId(instanceId: InstanceId?) = apply {
this.mInstanceId = instanceId
}
override fun withType(type: LatencyType?) = apply { this.mType = type }
override fun withPackageId(packageId: Int) = apply { this.mPackageId = packageId }
override fun withLatency(latencyInMillis: Long) = apply {
this.mLatencyInMillis = latencyInMillis
}
override fun withQueryLength(queryLength: Int) = apply { this.mQueryLength = queryLength }
override fun withSubEventType(type: Int) = apply { this.mSubEventType = type }
override fun withCardinality(cardinality: Int) = apply { this.mCardinality = cardinality }
override fun log(event: EventEnum) {
if (IS_VERBOSE) {
val name =
if (event is Enum<*>) (event as Enum<*>).name else event.id.toString() + ""
Log.d(LATENCY_TAG, "InstanceId=$mInstanceId $name=${mLatencyInMillis}ms")
}
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_LATENCY,
event.id, // event_id
mInstanceId!!.id, // instance_id
mPackageId, // package_id
mLatencyInMillis, // latency_in_millis
mType!!.id, // type
mQueryLength, // query_length
mSubEventType, // sub_event_type
mCardinality, // cardinality
)
}
}
/** Helps to construct and log statsd compatible impression events. */
private class StatsCompatImpressionLogger : StatsImpressionLogger {
private var mInstanceId: InstanceId? = DEFAULT_INSTANCE_ID
private var mLauncherState: State? = State.UNKNOWN
private var mQueryLength = -1
// Fields used for Impression Logging V2.
private var mResultType = 0
private var mAboveKeyboard = false
private var mUid = 0
private var mResultSource = 0
override fun withInstanceId(instanceId: InstanceId?) = apply {
this.mInstanceId = instanceId
}
override fun withState(state: State?) = apply { this.mLauncherState = state }
override fun withQueryLength(queryLength: Int) = apply { this.mQueryLength = queryLength }
override fun withResultType(resultType: Int) = apply { mResultType = resultType }
override fun withAboveKeyboard(aboveKeyboard: Boolean) = apply {
mAboveKeyboard = aboveKeyboard
}
override fun withUid(uid: Int) = apply { mUid = uid }
override fun withResultSource(resultSource: Int) = apply { mResultSource = resultSource }
override fun log(event: EventEnum) {
if (IS_VERBOSE) {
val name =
if (event is Enum<*>) (event as Enum<*>).name else event.id.toString() + ""
Log.d(
IMPRESSION_TAG,
""""
|InstanceId:$mInstanceId
|ImpressionEvent:$name
| LauncherState = $mLauncherState
| QueryLength = $mQueryLength
| ResultType=$mResultType is_above_keyboard=$mAboveKeyboard mUid=$mUid
| result_source=$mResultSource
|"""
.trimMargin(),
)
}
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_IMPRESSION_EVENT_V2,
event.id, // event_id
mInstanceId!!.id, // instance_id
mLauncherState!!.launcherState, // state
mQueryLength, // query_length
mResultType, // result type
mAboveKeyboard, // above keyboard
mUid, // uid
mResultSource, // result source
)
}
}
/** Interface to get stats log while it is dispatched to the system */
interface StatsLogConsumer {
@WorkerThread fun consume(event: EventEnum?, atomInfo: LauncherAtom.ItemInfo?)
}
companion object {
private const val TAG = "StatsLog"
private const val LATENCY_TAG = "StatsLatencyLog"
private const val IMPRESSION_TAG = "StatsImpressionLog"
private val IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG)
private val DEBUG = !Utilities.isRunningInTestHarness()
private val DEFAULT_INSTANCE_ID: InstanceId = InstanceId.fakeInstanceId(0)
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto
// migrates
// from nano to lite, bake constant to prevent robo test failure.
private const val DEFAULT_PAGE_INDEX = -2
private const val FOLDER_HIERARCHY_OFFSET = 100
private const val SEARCH_RESULT_HIERARCHY_OFFSET = 200
private const val EXTENDED_CONTAINERS_HIERARCHY_OFFSET = 300
private const val ALL_APPS_HIERARCHY_OFFSET = 400
/** Flags for converting SearchAttribute to integer value. */
private const val SEARCH_ATTRIBUTES_CORRECTED_QUERY = 1 shl 0
private const val SEARCH_ATTRIBUTES_DIRECT_MATCH = 1 shl 1
private const val SEARCH_ATTRIBUTES_ENTRY_STATE_ALL_APPS = 1 shl 2
private const val SEARCH_ATTRIBUTES_ENTRY_STATE_QSB = 1 shl 3
private const val SEARCH_ATTRIBUTES_ENTRY_STATE_OVERVIEW = 1 shl 4
private const val SEARCH_ATTRIBUTES_ENTRY_STATE_TASKBAR = 1 shl 5
@JvmField val LOGS_CONSUMER: CopyOnWriteArrayList<StatsLogConsumer> = CopyOnWriteArrayList()
/** Synchronously writes an itemInfo to stats log */
@WorkerThread
@JvmStatic
fun writeSnapshot(info: LauncherAtom.ItemInfo, instanceId: InstanceId) {
if (IS_VERBOSE) {
Log.d(TAG, String.format("\nwriteSnapshot(%d):\n%s", instanceId.id, info))
}
if (Utilities.isRunningInTestHarness()) {
return
}
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_SNAPSHOT,
LAUNCHER_WORKSPACE_SNAPSHOT.id, /* event_id */
info.itemCase.number, /* target_id */
instanceId.id, /* instance_id */
0, /* uid */
getPackageName(info), /* package_name */
getComponentName(info), /* component_name */
getGridX(info, false), /* grid_x */
getGridY(info, false), /* grid_y */
getPageId(info), /* page_id */
getGridX(info, true), /* grid_x_parent */
getGridY(info, true), /* grid_y_parent */
getParentPageId(info), /* page_id_parent */
getHierarchy(info), /* hierarchy */
info.isWork, /* is_work_profile */
0, /* origin */
getCardinality(info), /* cardinality */
info.widget.spanX,
info.widget.spanY,
getFeatures(info),
getAttributes(info), /* attributes */
)
}
private fun getAttributes(itemInfo: LauncherAtom.ItemInfo): ByteArray =
LauncherAttributes.newBuilder()
.apply { itemInfo.itemAttributesList.forEach { addItemAttributes(it.number) } }
.build()
.toByteArray()
/**
* Builds [StatsEvent] from [LauncherAtom.ItemInfo]. Used for pulled atom callback
* implementation.
*/
@JvmStatic
fun buildStatsEvent(info: LauncherAtom.ItemInfo, instanceId: InstanceId?): StatsEvent {
return SysUiStatsLog.buildStatsEvent(
SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT, // atom ID,
LAUNCHER_WORKSPACE_SNAPSHOT.id, // event_id = 1;
info.itemCase.number, // item_id = 2;
instanceId?.id ?: 0, // instance_id = 3;
0, // uid = 4 [(is_uid) = true];
getPackageName(info), // package_name = 5;
getComponentName(info), // component_name = 6;
getGridX(info, false), // grid_x = 7 [default = -1];
getGridY(info, false), // grid_y = 8 [default = -1];
getPageId(info), // page_id = 9 [default = -2];
getGridX(info, true), // grid_x_parent = 10 [default = -1];
getGridY(info, true), // grid_y_parent = 11 [default = -1];
getParentPageId(info), // page_id_parent = 12 [default = -2];
getHierarchy(info), // container_id = 13;
info.isWork, // is_work_profile = 14;
0, // attribute_id = 15;
getCardinality(info), // cardinality = 16;
info.widget.spanX, // span_x = 17 [default = 1];
info.widget.spanY, // span_y = 18 [default = 1];
getAttributes(info), /* attributes = 19 [(log_mode) = MODE_BYTES] */
info.isKidsMode, /* is_kids_mode = 20 */
)
}
private fun getCardinality(info: LauncherAtom.ItemInfo): Int {
if (Utilities.isRunningInTestHarness()) return 0
when (info.containerInfo.containerCase) {
PREDICTED_HOTSEAT_CONTAINER ->
return info.containerInfo.predictedHotseatContainer.cardinality
TASK_BAR_CONTAINER -> return info.containerInfo.taskBarContainer.cardinality
SEARCH_RESULT_CONTAINER ->
return info.containerInfo.searchResultContainer.queryLength
EXTENDED_CONTAINERS -> {
val extendedCont = info.containerInfo.extendedContainers
if (extendedCont.containerCase == DEVICE_SEARCH_RESULT_CONTAINER) {
val deviceSearchResultCont = extendedCont.deviceSearchResultContainer
return if (deviceSearchResultCont.hasQueryLength())
deviceSearchResultCont.queryLength
else -1
}
return when (info.itemCase) {
FOLDER_ICON -> info.folderIcon.cardinality
TASK_VIEW -> info.taskView.cardinality
else -> 0
}
}
else ->
return when (info.itemCase) {
FOLDER_ICON -> info.folderIcon.cardinality
TASK_VIEW -> info.taskView.cardinality
else -> 0
}
}
}
private fun getPackageName(info: LauncherAtom.ItemInfo): String? =
when (info.itemCase) {
APPLICATION -> info.application.packageName
SHORTCUT -> info.shortcut.shortcutName
WIDGET -> info.widget.packageName
TASK -> info.task.packageName
SEARCH_ACTION_ITEM -> info.searchActionItem.packageName
else -> null
}
private fun getComponentName(info: LauncherAtom.ItemInfo): String? =
when (info.itemCase) {
APPLICATION -> info.application.componentName
SHORTCUT -> info.shortcut.shortcutName
WIDGET -> info.widget.componentName
TASK -> info.task.componentName
TASK_VIEW -> info.taskView.componentName
SEARCH_ACTION_ITEM -> info.searchActionItem.title
SLICE -> info.slice.uri
else -> null
}
private fun getGridX(info: LauncherAtom.ItemInfo, parent: Boolean): Int {
val containerInfo = info.containerInfo
return if (containerInfo.containerCase == FOLDER) {
if (parent) {
containerInfo.folder.workspace.gridX
} else {
containerInfo.folder.gridX
}
} else if (containerInfo.containerCase == EXTENDED_CONTAINERS) {
containerInfo.extendedContainers.deviceSearchResultContainer.gridX
} else {
containerInfo.workspace.gridX
}
}
private fun getGridY(info: LauncherAtom.ItemInfo, parent: Boolean): Int =
if (info.containerInfo.containerCase == FOLDER) {
if (parent) {
info.containerInfo.folder.workspace.gridY
} else {
info.containerInfo.folder.gridY
}
} else {
info.containerInfo.workspace.gridY
}
private fun getPageId(info: LauncherAtom.ItemInfo): Int =
when (info.itemCase) {
TASK -> info.task.index
TASK_VIEW -> info.taskView.index
else -> getPageIdFromContainerInfo(info.containerInfo)
}
private fun getPageIdFromContainerInfo(containerInfo: ContainerInfo): Int =
when (containerInfo.containerCase) {
FOLDER -> containerInfo.folder.pageIndex
HOTSEAT -> containerInfo.hotseat.index
PREDICTED_HOTSEAT_CONTAINER -> containerInfo.predictedHotseatContainer.index
TASK_BAR_CONTAINER -> containerInfo.taskBarContainer.index
else -> containerInfo.workspace.pageIndex
}
private fun getParentPageId(info: LauncherAtom.ItemInfo): Int =
info.containerInfo.run {
when {
containerCase == FOLDER &&
folder.parentContainerCase == ParentContainerCase.HOTSEAT ->
folder.hotseat.index
containerCase == FOLDER -> folder.workspace.pageIndex
containerCase == SEARCH_RESULT_CONTAINER ->
searchResultContainer.workspace.pageIndex
else -> workspace.pageIndex
}
}
private fun getHierarchy(info: LauncherAtom.ItemInfo): Int {
if (Utilities.isRunningInTestHarness()) return 0
return info.containerInfo.run {
when (containerCase) {
FOLDER -> folder.parentContainerCase.number + FOLDER_HIERARCHY_OFFSET
SEARCH_RESULT_CONTAINER ->
searchResultContainer.parentContainerCase.number +
SEARCH_RESULT_HIERARCHY_OFFSET
EXTENDED_CONTAINERS ->
extendedContainers.containerCase.number +
EXTENDED_CONTAINERS_HIERARCHY_OFFSET
ALL_APPS_CONTAINER ->
allAppsContainer.parentContainerCase.number + ALL_APPS_HIERARCHY_OFFSET
else -> containerCase.number
}
}
}
private fun getStateString(state: Int): String =
when (state) {
SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND -> "BACKGROUND"
SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME -> "HOME"
SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW -> "OVERVIEW"
SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS -> "ALLAPPS"
else -> "INVALID"
}
private fun getFeatures(info: LauncherAtom.ItemInfo): Int =
when (info.itemCase) {
WIDGET -> info.widget.widgetFeatures
TASK_VIEW -> info.taskView.type
else -> 0
}
private fun getSearchAttributes(info: LauncherAtom.ItemInfo): Int {
if (Utilities.isRunningInTestHarness()) return 0
val containerInfo = info.containerInfo
if (
containerInfo.containerCase == EXTENDED_CONTAINERS &&
(containerInfo.extendedContainers.containerCase ==
DEVICE_SEARCH_RESULT_CONTAINER) &&
containerInfo.extendedContainers.deviceSearchResultContainer
.hasSearchAttributes()
) {
return searchAttributesToInt(
containerInfo.extendedContainers.deviceSearchResultContainer.searchAttributes
)
}
return 0
}
private fun searchAttributesToInt(searchAttributes: SearchAttributes): Int {
var response = 0
if (searchAttributes.correctedQuery)
response = response or SEARCH_ATTRIBUTES_CORRECTED_QUERY
if (searchAttributes.directMatch) response = response or SEARCH_ATTRIBUTES_DIRECT_MATCH
response =
response or
when (searchAttributes.entryState) {
ALL_APPS -> SEARCH_ATTRIBUTES_ENTRY_STATE_ALL_APPS
QSB -> SEARCH_ATTRIBUTES_ENTRY_STATE_QSB
OVERVIEW -> SEARCH_ATTRIBUTES_ENTRY_STATE_OVERVIEW
TASKBAR -> SEARCH_ATTRIBUTES_ENTRY_STATE_TASKBAR
else -> 0
}
return response
}
}
}
@@ -40,20 +40,25 @@ import com.android.launcher3.util.SettingsCache
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.capture
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class SettingsChangeLoggerTest {
@get:Rule val mockito = MockitoJUnit.rule()
private val mContext: Context = ApplicationProvider.getApplicationContext()
private val mInstanceId = InstanceId.fakeInstanceId(1)
@@ -64,7 +69,8 @@ class SettingsChangeLoggerTest {
@Mock private lateinit var mStatsLogManager: StatsLogManager
@Mock private lateinit var mMockLogger: StatsLogManager.StatsLogger
@Mock(answer = Answers.RETURNS_SELF)
private lateinit var mMockLogger: StatsLogManager.StatsLogger
@Mock private lateinit var mTracker: DaggerSingletonTracker
private var displayController: DisplayController = DisplayController.INSTANCE.get(mContext)
private var settingsCache: SettingsCache = SettingsCache.INSTANCE.get(mContext)
@@ -79,10 +85,8 @@ class SettingsChangeLoggerTest {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(mStatsLogFactory.create(mContext)).doReturn(mStatsLogManager)
whenever(mStatsLogManager.logger()).doReturn(mMockLogger)
whenever(mStatsLogManager.logger().withInstanceId(any())).doReturn(mMockLogger)
mDefaultThemedIcons = themeManager.isMonoThemeEnabled
mDefaultAllowRotation = LauncherPrefs.get(mContext).get(ALLOW_ROTATION)
// To match the default value of THEMED_ICONS
@@ -128,7 +132,7 @@ class SettingsChangeLoggerTest {
fun logSnapshot_defaultValue() {
mSystemUnderTest.logSnapshot(mInstanceId)
verify(mMockLogger, atLeastOnce()).log(mEventCaptor.capture())
verify(mMockLogger, atLeastOnce()).log(capture(mEventCaptor))
val capturedEvents = mEventCaptor.allValues
assertThat(capturedEvents.isNotEmpty()).isTrue()
verifyDefaultEvent(capturedEvents)
@@ -144,7 +148,7 @@ class SettingsChangeLoggerTest {
SettingsChangeLogger(mContext, mTracker, displayController, settingsCache, mStatsLogFactory)
.logSnapshot(mInstanceId)
verify(mMockLogger, atLeastOnce()).log(mEventCaptor.capture())
verify(mMockLogger, atLeastOnce()).log(capture(mEventCaptor))
val capturedEvents = mEventCaptor.allValues
assertThat(capturedEvents.isNotEmpty()).isTrue()
verifyDefaultEvent(capturedEvents)
@@ -27,7 +27,7 @@ import com.android.launcher3.LauncherConstants.TraceEvents.SINGLE_TRACE_COOKIE
import com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent
import com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION
import com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC
import com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LAUNCHER_LATENCY_PACKAGE_ID
import com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.Companion.LAUNCHER_LATENCY_PACKAGE_ID
import com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType
import com.android.launcher3.util.Executors
import com.android.launcher3.util.LockedUserState
@@ -25,7 +25,7 @@ import com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCH
import com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION
import com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC
import com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger
import com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LAUNCHER_LATENCY_PACKAGE_ID
import com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.Companion.LAUNCHER_LATENCY_PACKAGE_ID
import com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.LauncherMultivalentJUnit