• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.quickstep.logging;
18 
19 import static androidx.core.util.Preconditions.checkNotNull;
20 import static androidx.core.util.Preconditions.checkState;
21 
22 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
23 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.ALL_APPS_CONTAINER;
24 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
25 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
26 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
27 import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER;
28 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
29 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
30 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
31 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
32 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
33 
34 import android.content.Context;
35 import android.util.Log;
36 import android.util.StatsEvent;
37 import android.view.View;
38 
39 import androidx.annotation.NonNull;
40 import androidx.annotation.Nullable;
41 import androidx.annotation.WorkerThread;
42 import androidx.slice.SliceItem;
43 
44 import com.android.launcher3.LauncherAppState;
45 import com.android.launcher3.Utilities;
46 import com.android.launcher3.logger.LauncherAtom;
47 import com.android.launcher3.logger.LauncherAtom.Attribute;
48 import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
49 import com.android.launcher3.logger.LauncherAtom.FolderContainer.ParentContainerCase;
50 import com.android.launcher3.logger.LauncherAtom.FolderIcon;
51 import com.android.launcher3.logger.LauncherAtom.FromState;
52 import com.android.launcher3.logger.LauncherAtom.LauncherAttributes;
53 import com.android.launcher3.logger.LauncherAtom.ToState;
54 import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer;
55 import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer.SearchAttributes;
56 import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
57 import com.android.launcher3.logging.InstanceId;
58 import com.android.launcher3.logging.StatsLogManager;
59 import com.android.launcher3.model.AllAppsList;
60 import com.android.launcher3.model.BaseModelUpdateTask;
61 import com.android.launcher3.model.BgDataModel;
62 import com.android.launcher3.model.data.FolderInfo;
63 import com.android.launcher3.model.data.ItemInfo;
64 import com.android.launcher3.util.Executors;
65 import com.android.launcher3.util.LogConfig;
66 import com.android.launcher3.views.ActivityContext;
67 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
68 import com.android.systemui.shared.system.SysUiStatsLog;
69 
70 import java.util.Optional;
71 import java.util.OptionalInt;
72 import java.util.concurrent.CopyOnWriteArrayList;
73 
74 /**
75  * This class calls StatsLog compile time generated methods.
76  *
77  * To see if the logs are properly sent to statsd, execute following command.
78  * <ul>
79  * $ wwdebug (to turn on the logcat printout)
80  * $ wwlogcat (see logcat with grep filter on)
81  * $ statsd_testdrive (see how ww is writing the proto to statsd buffer)
82  * </ul>
83  */
84 public class StatsLogCompatManager extends StatsLogManager {
85 
86     private static final String TAG = "StatsLog";
87     private static final String LATENCY_TAG = "StatsLatencyLog";
88     private static final String IMPRESSION_TAG = "StatsImpressionLog";
89     private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
90     private static final boolean DEBUG = !Utilities.isRunningInTestHarness();
91     private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
92     // LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
93     // from nano to lite, bake constant to prevent robo test failure.
94     private static final int DEFAULT_PAGE_INDEX = -2;
95     private static final int FOLDER_HIERARCHY_OFFSET = 100;
96     private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
97     private static final int EXTENDED_CONTAINERS_HIERARCHY_OFFSET = 300;
98     private static final int ALL_APPS_HIERARCHY_OFFSET = 400;
99 
100     /**
101      * Flags for converting SearchAttribute to integer value.
102      */
103     private static final int SEARCH_ATTRIBUTES_CORRECTED_QUERY = 1 << 0;
104     private static final int SEARCH_ATTRIBUTES_DIRECT_MATCH = 1 << 1;
105     private static final int SEARCH_ATTRIBUTES_ENTRY_STATE_ALL_APPS = 1 << 2;
106     private static final int SEARCH_ATTRIBUTES_ENTRY_STATE_QSB = 1 << 3;
107 
108     public static final CopyOnWriteArrayList<StatsLogConsumer> LOGS_CONSUMER =
109             new CopyOnWriteArrayList<>();
110 
StatsLogCompatManager(Context context)111     public StatsLogCompatManager(Context context) {
112         mContext = context;
113     }
114 
115     @Override
createLogger()116     protected StatsLogger createLogger() {
117         return new StatsCompatLogger(mContext, mActivityContext);
118     }
119 
120     @Override
createLatencyLogger()121     protected StatsLatencyLogger createLatencyLogger() {
122         return new StatsCompatLatencyLogger();
123     }
124 
125     @Override
createImpressionLogger()126     protected StatsImpressionLogger createImpressionLogger() {
127         return new StatsCompatImpressionLogger();
128     }
129 
130     /**
131      * Synchronously writes an itemInfo to stats log
132      */
133     @WorkerThread
writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId)134     public static void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
135         if (IS_VERBOSE) {
136             Log.d(TAG, String.format("\nwriteSnapshot(%d):\n%s", instanceId.getId(), info));
137         }
138         if (!Utilities.ATLEAST_R || Utilities.isRunningInTestHarness()) {
139             return;
140         }
141         SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
142                 LAUNCHER_WORKSPACE_SNAPSHOT.getId() /* event_id */,
143                 info.getItemCase().getNumber()  /* target_id */,
144                 instanceId.getId() /* instance_id */,
145                 0 /* uid */,
146                 getPackageName(info) /* package_name */,
147                 getComponentName(info) /* component_name */,
148                 getGridX(info, false) /* grid_x */,
149                 getGridY(info, false) /* grid_y */,
150                 getPageId(info) /* page_id */,
151                 getGridX(info, true) /* grid_x_parent */,
152                 getGridY(info, true) /* grid_y_parent */,
153                 getParentPageId(info) /* page_id_parent */,
154                 getHierarchy(info) /* hierarchy */,
155                 info.getIsWork() /* is_work_profile */,
156                 0 /* origin */,
157                 getCardinality(info) /* cardinality */,
158                 info.getWidget().getSpanX(),
159                 info.getWidget().getSpanY(),
160                 getFeatures(info),
161                 getAttributes(info) /* attributes */
162         );
163     }
164 
getAttributes(LauncherAtom.ItemInfo itemInfo)165     private static byte[] getAttributes(LauncherAtom.ItemInfo itemInfo) {
166         LauncherAttributes.Builder responseBuilder = LauncherAttributes.newBuilder();
167         itemInfo.getItemAttributesList().stream().map(Attribute::getNumber).forEach(
168                 responseBuilder::addItemAttributes);
169         return responseBuilder.build().toByteArray();
170     }
171 
172     /**
173      * Builds {@link StatsEvent} from {@link LauncherAtom.ItemInfo}. Used for pulled atom callback
174      * implementation.
175      */
buildStatsEvent(LauncherAtom.ItemInfo info, @Nullable InstanceId instanceId)176     public static StatsEvent buildStatsEvent(LauncherAtom.ItemInfo info,
177             @Nullable InstanceId instanceId) {
178         return SysUiStatsLog.buildStatsEvent(
179                 SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT, // atom ID,
180                 LAUNCHER_WORKSPACE_SNAPSHOT.getId(), // event_id = 1;
181                 info.getItemCase().getNumber(), // item_id = 2;
182                 instanceId == null ? 0 : instanceId.getId(), //instance_id = 3;
183                 0, //uid = 4 [(is_uid) = true];
184                 getPackageName(info), // package_name = 5;
185                 getComponentName(info), // component_name = 6;
186                 getGridX(info, false), //grid_x = 7 [default = -1];
187                 getGridY(info, false), //grid_y = 8 [default = -1];
188                 getPageId(info), // page_id = 9 [default = -2];
189                 getGridX(info, true), //grid_x_parent = 10 [default = -1];
190                 getGridY(info, true), //grid_y_parent = 11 [default = -1];
191                 getParentPageId(info), //page_id_parent = 12 [default = -2];
192                 getHierarchy(info), // container_id = 13;
193                 info.getIsWork(), // is_work_profile = 14;
194                 0, // attribute_id = 15;
195                 getCardinality(info), // cardinality = 16;
196                 info.getWidget().getSpanX(), // span_x = 17 [default = 1];
197                 info.getWidget().getSpanY(), // span_y = 18 [default = 1];
198                 getAttributes(info) /* attributes = 19 [(log_mode) = MODE_BYTES] */,
199                 info.getIsKidsMode() /* is_kids_mode = 20 */
200         );
201     }
202 
203     /**
204      * Helps to construct and write statsd compatible log message.
205      */
206     private static class StatsCompatLogger implements StatsLogger {
207 
208         private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
209         static {
210             DEFAULT_ITEM_INFO.itemType = ITEM_TYPE_NON_ACTIONABLE;
211         }
212         private final Context mContext;
213         private final Optional<ActivityContext> mActivityContext;
214         private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
215         private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
216         private OptionalInt mRank = OptionalInt.empty();
217         private Optional<ContainerInfo> mContainerInfo = Optional.empty();
218         private int mSrcState = LAUNCHER_STATE_UNSPECIFIED;
219         private int mDstState = LAUNCHER_STATE_UNSPECIFIED;
220         private Optional<FromState> mFromState = Optional.empty();
221         private Optional<ToState> mToState = Optional.empty();
222         private Optional<String> mEditText = Optional.empty();
223         private SliceItem mSliceItem;
224         private LauncherAtom.Slice mSlice;
225         private Optional<Integer> mCardinality = Optional.empty();
226         private int mInputType = SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__UNKNOWN;
227 
StatsCompatLogger(Context context, ActivityContext activityContext)228         StatsCompatLogger(Context context, ActivityContext activityContext) {
229             mContext = context;
230             mActivityContext = Optional.ofNullable(activityContext);
231         }
232 
233         @Override
withItemInfo(ItemInfo itemInfo)234         public StatsLogger withItemInfo(ItemInfo itemInfo) {
235             if (mContainerInfo.isPresent()) {
236                 throw new IllegalArgumentException(
237                         "ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
238             }
239             this.mItemInfo = itemInfo;
240             return this;
241         }
242 
243         @Override
withInstanceId(InstanceId instanceId)244         public StatsLogger withInstanceId(InstanceId instanceId) {
245             this.mInstanceId = instanceId;
246             return this;
247         }
248 
249         @Override
withRank(int rank)250         public StatsLogger withRank(int rank) {
251             this.mRank = OptionalInt.of(rank);
252             return this;
253         }
254 
255         @Override
withSrcState(int srcState)256         public StatsLogger withSrcState(int srcState) {
257             this.mSrcState = srcState;
258             return this;
259         }
260 
261         @Override
withDstState(int dstState)262         public StatsLogger withDstState(int dstState) {
263             this.mDstState = dstState;
264             return this;
265         }
266 
267         @Override
withContainerInfo(ContainerInfo containerInfo)268         public StatsLogger withContainerInfo(ContainerInfo containerInfo) {
269             checkState(mItemInfo == DEFAULT_ITEM_INFO,
270                     "ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
271             this.mContainerInfo = Optional.of(containerInfo);
272             return this;
273         }
274 
275         @Override
withFromState(FromState fromState)276         public StatsLogger withFromState(FromState fromState) {
277             this.mFromState = Optional.of(fromState);
278             return this;
279         }
280 
281         @Override
withToState(ToState toState)282         public StatsLogger withToState(ToState toState) {
283             this.mToState = Optional.of(toState);
284             return this;
285         }
286 
287         @Override
withEditText(String editText)288         public StatsLogger withEditText(String editText) {
289             this.mEditText = Optional.of(editText);
290             return this;
291         }
292 
293         @Override
withSliceItem(@onNull SliceItem sliceItem)294         public StatsLogger withSliceItem(@NonNull SliceItem sliceItem) {
295             checkState(mItemInfo == DEFAULT_ITEM_INFO && mSlice == null,
296                     "ItemInfo, Slice and SliceItem are mutual exclusive; cannot set more than one"
297                             + " of them.");
298             this.mSliceItem = checkNotNull(sliceItem, "expected valid sliceItem but received null");
299             return this;
300         }
301 
302         @Override
withSlice(LauncherAtom.Slice slice)303         public StatsLogger withSlice(LauncherAtom.Slice slice) {
304             checkState(mItemInfo == DEFAULT_ITEM_INFO && mSliceItem == null,
305                     "ItemInfo, Slice and SliceItem are mutual exclusive; cannot set more than one"
306                             + " of them.");
307             checkNotNull(slice, "expected valid slice but received null");
308             checkNotNull(slice.getUri(), "expected valid slice uri but received null");
309             this.mSlice = slice;
310             return this;
311         }
312 
313         @Override
withCardinality(int cardinality)314         public StatsLogger withCardinality(int cardinality) {
315             this.mCardinality = Optional.of(cardinality);
316             return this;
317         }
318 
319         @Override
withInputType(int inputType)320         public StatsLogger withInputType(int inputType) {
321             this.mInputType = inputType;
322             return this;
323         }
324 
325         @Override
log(EventEnum event)326         public void log(EventEnum event) {
327             if (!Utilities.ATLEAST_R) {
328                 return;
329             }
330             if (DEBUG) {
331                 String name = (event instanceof Enum) ? ((Enum) event).name() :
332                         event.getId() + "";
333                 Log.d(TAG, name);
334             }
335             LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
336 
337             if (mSlice == null && mSliceItem != null) {
338                 mSlice = LauncherAtom.Slice.newBuilder().setUri(
339                         mSliceItem.getSlice().getUri().toString()).build();
340             }
341 
342             if (mSlice != null) {
343                 Executors.MODEL_EXECUTOR.execute(
344                         () -> {
345                             LauncherAtom.ItemInfo.Builder itemInfoBuilder =
346                                     LauncherAtom.ItemInfo.newBuilder().setSlice(mSlice);
347                             mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
348                             write(event, applyOverwrites(itemInfoBuilder.build()));
349                         });
350                 return;
351             }
352 
353             if (mItemInfo == null) {
354                 return;
355             }
356 
357             if (mItemInfo.container < 0 || appState == null) {
358                 // Write log on the model thread so that logs do not go out of order
359                 // (for eg: drop comes after drag)
360                 Executors.MODEL_EXECUTOR.execute(
361                         () -> write(event, applyOverwrites(mItemInfo.buildProto())));
362             } else {
363                 // Item is inside the folder, fetch folder info in a BG thread
364                 // and then write to StatsLog.
365                 appState.getModel().enqueueModelUpdateTask(
366                         new BaseModelUpdateTask() {
367                             @Override
368                             public void execute(@NonNull final LauncherAppState app,
369                                     @NonNull final BgDataModel dataModel,
370                                     @NonNull final AllAppsList apps) {
371                                 FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
372                                 write(event, applyOverwrites(mItemInfo.buildProto(folderInfo)));
373                             }
374                         });
375             }
376         }
377 
378         @Override
sendToInteractionJankMonitor(EventEnum event, View view)379         public void sendToInteractionJankMonitor(EventEnum event, View view) {
380             if (!(event instanceof LauncherEvent)) {
381                 return;
382             }
383             switch ((LauncherEvent) event) {
384                 case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN:
385                     InteractionJankMonitorWrapper.begin(
386                             view,
387                             InteractionJankMonitorWrapper.CUJ_ALL_APPS_SCROLL);
388                     break;
389                 case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END:
390                     InteractionJankMonitorWrapper.end(
391                             InteractionJankMonitorWrapper.CUJ_ALL_APPS_SCROLL);
392                     break;
393                 default:
394                     break;
395             }
396         }
397 
applyOverwrites(LauncherAtom.ItemInfo atomInfo)398         private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) {
399             LauncherAtom.ItemInfo.Builder itemInfoBuilder = atomInfo.toBuilder();
400 
401             mRank.ifPresent(itemInfoBuilder::setRank);
402             mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
403 
404             mActivityContext.ifPresent(activityContext ->
405                     activityContext.applyOverwritesToLogItem(itemInfoBuilder));
406 
407             if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) {
408                 FolderIcon.Builder folderIconBuilder = itemInfoBuilder
409                         .getFolderIcon()
410                         .toBuilder();
411                 mFromState.ifPresent(folderIconBuilder::setFromLabelState);
412                 mToState.ifPresent(folderIconBuilder::setToLabelState);
413                 mEditText.ifPresent(folderIconBuilder::setLabelInfo);
414                 itemInfoBuilder.setFolderIcon(folderIconBuilder);
415             }
416             return itemInfoBuilder.build();
417         }
418 
419         @WorkerThread
write(EventEnum event, LauncherAtom.ItemInfo atomInfo)420         private void write(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
421             InstanceId instanceId = mInstanceId;
422             int srcState = mSrcState;
423             int dstState = mDstState;
424             int inputType = mInputType;
425             if (IS_VERBOSE) {
426                 String name = (event instanceof Enum) ? ((Enum) event).name() :
427                         event.getId() + "";
428                 StringBuilder logStringBuilder = new StringBuilder("\n");
429                 if (instanceId != DEFAULT_INSTANCE_ID) {
430                     logStringBuilder.append(String.format("InstanceId:%s ", instanceId));
431                 }
432                 logStringBuilder.append(name);
433                 if (srcState != LAUNCHER_STATE_UNSPECIFIED
434                         || dstState != LAUNCHER_STATE_UNSPECIFIED) {
435                     logStringBuilder.append(
436                             String.format("(State:%s->%s)", getStateString(srcState),
437                                     getStateString(dstState)));
438                 }
439                 if (atomInfo.hasContainerInfo()) {
440                     logStringBuilder.append("\n").append(atomInfo);
441                 }
442                 Log.d(TAG, logStringBuilder.toString());
443             }
444 
445             for (StatsLogConsumer consumer : LOGS_CONSUMER) {
446                 consumer.consume(event, atomInfo);
447             }
448 
449             // TODO: remove this when b/231648228 is fixed.
450             if (Utilities.isRunningInTestHarness()) {
451                 return;
452             }
453             int cardinality = mCardinality.orElseGet(() -> getCardinality(atomInfo));
454             SysUiStatsLog.write(
455                     SysUiStatsLog.LAUNCHER_EVENT,
456                     SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
457                     srcState,
458                     dstState,
459                     null /* launcher extensions, deprecated */,
460                     false /* quickstep_enabled, deprecated */,
461                     event.getId() /* event_id */,
462                     atomInfo.getItemCase().getNumber() /* target_id */,
463                     instanceId.getId() /* instance_id TODO */,
464                     0 /* uid TODO */,
465                     getPackageName(atomInfo) /* package_name */,
466                     getComponentName(atomInfo) /* component_name */,
467                     getGridX(atomInfo, false) /* grid_x */,
468                     getGridY(atomInfo, false) /* grid_y */,
469                     getPageId(atomInfo) /* page_id */,
470                     getGridX(atomInfo, true) /* grid_x_parent */,
471                     getGridY(atomInfo, true) /* grid_y_parent */,
472                     getParentPageId(atomInfo) /* page_id_parent */,
473                     getHierarchy(atomInfo) /* hierarchy */,
474                     atomInfo.getIsWork() /* is_work_profile */,
475                     atomInfo.getRank() /* rank */,
476                     atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
477                     atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
478                     atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
479                     cardinality /* cardinality */,
480                     getFeatures(atomInfo) /* features */,
481                     getSearchAttributes(atomInfo) /* searchAttributes */,
482                     getAttributes(atomInfo) /* attributes */,
483                     inputType /* input_type */
484             );
485         }
486     }
487 
488     /**
489      * Helps to construct and log statsd compatible latency events.
490      */
491     private static class StatsCompatLatencyLogger implements StatsLatencyLogger {
492         private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
493         private LatencyType mType = LatencyType.UNKNOWN;
494         private int mPackageId = 0;
495         private long mLatencyInMillis;
496         private int mQueryLength = -1;
497         private int mSubEventType = 0;
498         private int mCardinality = -1;
499 
500         @Override
withInstanceId(InstanceId instanceId)501         public StatsLatencyLogger withInstanceId(InstanceId instanceId) {
502             this.mInstanceId = instanceId;
503             return this;
504         }
505 
506         @Override
withType(LatencyType type)507         public StatsLatencyLogger withType(LatencyType type) {
508             this.mType = type;
509             return this;
510         }
511 
512         @Override
withPackageId(int packageId)513         public StatsLatencyLogger withPackageId(int packageId) {
514             this.mPackageId = packageId;
515             return this;
516         }
517 
518         @Override
withLatency(long latencyInMillis)519         public StatsLatencyLogger withLatency(long latencyInMillis) {
520             this.mLatencyInMillis = latencyInMillis;
521             return this;
522         }
523 
524         @Override
withQueryLength(int queryLength)525         public StatsLatencyLogger withQueryLength(int queryLength) {
526             this.mQueryLength = queryLength;
527             return this;
528         }
529 
530         @Override
withSubEventType(int type)531         public StatsLatencyLogger withSubEventType(int type) {
532             this.mSubEventType = type;
533             return this;
534         }
535 
536         @Override
withCardinality(int cardinality)537         public StatsLatencyLogger withCardinality(int cardinality) {
538             this.mCardinality = cardinality;
539             return this;
540         }
541 
542         @Override
log(EventEnum event)543         public void log(EventEnum event) {
544             if (IS_VERBOSE) {
545                 String name = (event instanceof Enum) ? ((Enum) event).name() :
546                         event.getId() + "";
547                 StringBuilder logStringBuilder = new StringBuilder("\n");
548                 logStringBuilder.append(String.format("InstanceId:%s ", mInstanceId));
549                 logStringBuilder.append(String.format("%s=%sms", name, mLatencyInMillis));
550                 Log.d(LATENCY_TAG, logStringBuilder.toString());
551             }
552 
553             SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_LATENCY,
554                     event.getId(), // event_id
555                     mInstanceId.getId(), // instance_id
556                     mPackageId, // package_id
557                     mLatencyInMillis, // latency_in_millis
558                     mType.getId(), //type
559                     mQueryLength, // query_length
560                     mSubEventType, // sub_event_type
561                     mCardinality // cardinality
562             );
563         }
564     }
565 
566     /**
567      * Helps to construct and log statsd compatible impression events.
568      */
569     private static class StatsCompatImpressionLogger implements StatsImpressionLogger {
570         private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
571         private State mLauncherState = State.UNKNOWN;
572         private int mQueryLength = -1;
573 
574         // Fields used for Impression Logging V2.
575         private int mResultType;
576         private boolean mAboveKeyboard = false;
577         private int mUid;
578         private int mResultSource;
579 
580         @Override
withInstanceId(InstanceId instanceId)581         public StatsImpressionLogger withInstanceId(InstanceId instanceId) {
582             this.mInstanceId = instanceId;
583             return this;
584         }
585 
586         @Override
withState(State state)587         public StatsImpressionLogger withState(State state) {
588             this.mLauncherState = state;
589             return this;
590         }
591 
592         @Override
withQueryLength(int queryLength)593         public StatsImpressionLogger withQueryLength(int queryLength) {
594             this.mQueryLength = queryLength;
595             return this;
596         }
597 
598         @Override
withResultType(int resultType)599         public StatsImpressionLogger withResultType(int resultType) {
600             mResultType = resultType;
601             return this;
602         }
603 
604 
605         @Override
withAboveKeyboard(boolean aboveKeyboard)606         public StatsImpressionLogger withAboveKeyboard(boolean aboveKeyboard) {
607             mAboveKeyboard = aboveKeyboard;
608             return this;
609         }
610 
611         @Override
withUid(int uid)612         public StatsImpressionLogger withUid(int uid) {
613             mUid = uid;
614             return this;
615         }
616 
617         @Override
withResultSource(int resultSource)618         public StatsImpressionLogger withResultSource(int resultSource) {
619             mResultSource = resultSource;
620             return this;
621         }
622 
623         @Override
log(EventEnum event)624         public void log(EventEnum event) {
625             if (IS_VERBOSE) {
626                 String name = (event instanceof Enum) ? ((Enum) event).name() :
627                         event.getId() + "";
628                 StringBuilder logStringBuilder = new StringBuilder("\n");
629                 logStringBuilder.append(String.format("InstanceId:%s ", mInstanceId));
630                 logStringBuilder.append(String.format("ImpressionEvent:%s ", name));
631                 logStringBuilder.append(String.format("\n\tLauncherState = %s ", mLauncherState));
632                 logStringBuilder.append(String.format("\tQueryLength = %s ", mQueryLength));
633                 logStringBuilder.append(String.format(
634                         "\n\t ResultType = %s is_above_keyboard = %s"
635                                 + " uid = %s result_source = %s",
636                         mResultType,
637                         mAboveKeyboard, mUid, mResultSource));
638 
639                 Log.d(IMPRESSION_TAG, logStringBuilder.toString());
640             }
641 
642 
643             SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_IMPRESSION_EVENT_V2,
644                     event.getId(), // event_id
645                     mInstanceId.getId(), // instance_id
646                     mLauncherState.getLauncherState(), // state
647                     mQueryLength, // query_length
648                     mResultType, //result type
649                     mAboveKeyboard, // above keyboard
650                     mUid, // uid
651                     mResultSource // result source
652 
653             );
654         }
655     }
656 
getCardinality(LauncherAtom.ItemInfo info)657     private static int getCardinality(LauncherAtom.ItemInfo info) {
658         if (Utilities.isRunningInTestHarness()) {
659             return 0;
660         }
661         switch (info.getContainerInfo().getContainerCase()) {
662             case PREDICTED_HOTSEAT_CONTAINER:
663                 return info.getContainerInfo().getPredictedHotseatContainer().getCardinality();
664             case TASK_BAR_CONTAINER:
665                 return info.getContainerInfo().getTaskBarContainer().getCardinality();
666             case SEARCH_RESULT_CONTAINER:
667                 return info.getContainerInfo().getSearchResultContainer().getQueryLength();
668             case EXTENDED_CONTAINERS:
669                 ExtendedContainers extendedCont = info.getContainerInfo().getExtendedContainers();
670                 if (extendedCont.getContainerCase() == DEVICE_SEARCH_RESULT_CONTAINER) {
671                     DeviceSearchResultContainer deviceSearchResultCont = extendedCont
672                             .getDeviceSearchResultContainer();
673                     return deviceSearchResultCont.hasQueryLength() ? deviceSearchResultCont
674                             .getQueryLength() : -1;
675                 }
676             default:
677                 return info.getFolderIcon().getCardinality();
678         }
679     }
680 
getPackageName(LauncherAtom.ItemInfo info)681     private static String getPackageName(LauncherAtom.ItemInfo info) {
682         switch (info.getItemCase()) {
683             case APPLICATION:
684                 return info.getApplication().getPackageName();
685             case SHORTCUT:
686                 return info.getShortcut().getShortcutName();
687             case WIDGET:
688                 return info.getWidget().getPackageName();
689             case TASK:
690                 return info.getTask().getPackageName();
691             case SEARCH_ACTION_ITEM:
692                 return info.getSearchActionItem().getPackageName();
693             default:
694                 return null;
695         }
696     }
697 
getComponentName(LauncherAtom.ItemInfo info)698     private static String getComponentName(LauncherAtom.ItemInfo info) {
699         switch (info.getItemCase()) {
700             case APPLICATION:
701                 return info.getApplication().getComponentName();
702             case SHORTCUT:
703                 return info.getShortcut().getShortcutName();
704             case WIDGET:
705                 return info.getWidget().getComponentName();
706             case TASK:
707                 return info.getTask().getComponentName();
708             case SEARCH_ACTION_ITEM:
709                 return info.getSearchActionItem().getTitle();
710             case SLICE:
711                 return info.getSlice().getUri();
712             default:
713                 return null;
714         }
715     }
716 
getGridX(LauncherAtom.ItemInfo info, boolean parent)717     private static int getGridX(LauncherAtom.ItemInfo info, boolean parent) {
718         LauncherAtom.ContainerInfo containerInfo = info.getContainerInfo();
719         if (containerInfo.getContainerCase() == FOLDER) {
720             if (parent) {
721                 return containerInfo.getFolder().getWorkspace().getGridX();
722             } else {
723                 return containerInfo.getFolder().getGridX();
724             }
725         } else if (containerInfo.getContainerCase() == EXTENDED_CONTAINERS) {
726             return containerInfo.getExtendedContainers()
727                     .getDeviceSearchResultContainer().getGridX();
728         } else {
729             return containerInfo.getWorkspace().getGridX();
730         }
731     }
732 
getGridY(LauncherAtom.ItemInfo info, boolean parent)733     private static int getGridY(LauncherAtom.ItemInfo info, boolean parent) {
734         if (info.getContainerInfo().getContainerCase() == FOLDER) {
735             if (parent) {
736                 return info.getContainerInfo().getFolder().getWorkspace().getGridY();
737             } else {
738                 return info.getContainerInfo().getFolder().getGridY();
739             }
740         } else {
741             return info.getContainerInfo().getWorkspace().getGridY();
742         }
743     }
744 
getPageId(LauncherAtom.ItemInfo info)745     private static int getPageId(LauncherAtom.ItemInfo info) {
746         if (info.hasTask()) {
747             return info.getTask().getIndex();
748         }
749         switch (info.getContainerInfo().getContainerCase()) {
750             case FOLDER:
751                 return info.getContainerInfo().getFolder().getPageIndex();
752             case HOTSEAT:
753                 return info.getContainerInfo().getHotseat().getIndex();
754             case PREDICTED_HOTSEAT_CONTAINER:
755                 return info.getContainerInfo().getPredictedHotseatContainer().getIndex();
756             case TASK_BAR_CONTAINER:
757                 return info.getContainerInfo().getTaskBarContainer().getIndex();
758             default:
759                 return info.getContainerInfo().getWorkspace().getPageIndex();
760         }
761     }
762 
getParentPageId(LauncherAtom.ItemInfo info)763     private static int getParentPageId(LauncherAtom.ItemInfo info) {
764         switch (info.getContainerInfo().getContainerCase()) {
765             case FOLDER:
766                 if (info.getContainerInfo().getFolder().getParentContainerCase()
767                         == ParentContainerCase.HOTSEAT) {
768                     return info.getContainerInfo().getFolder().getHotseat().getIndex();
769                 }
770                 return info.getContainerInfo().getFolder().getWorkspace().getPageIndex();
771             case SEARCH_RESULT_CONTAINER:
772                 return info.getContainerInfo().getSearchResultContainer().getWorkspace()
773                         .getPageIndex();
774             default:
775                 return info.getContainerInfo().getWorkspace().getPageIndex();
776         }
777     }
778 
getHierarchy(LauncherAtom.ItemInfo info)779     private static int getHierarchy(LauncherAtom.ItemInfo info) {
780         if (Utilities.isRunningInTestHarness()) {
781             return 0;
782         }
783         if (info.getContainerInfo().getContainerCase() == FOLDER) {
784             return info.getContainerInfo().getFolder().getParentContainerCase().getNumber()
785                     + FOLDER_HIERARCHY_OFFSET;
786         } else if (info.getContainerInfo().getContainerCase() == SEARCH_RESULT_CONTAINER) {
787             return info.getContainerInfo().getSearchResultContainer().getParentContainerCase()
788                     .getNumber() + SEARCH_RESULT_HIERARCHY_OFFSET;
789         } else if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS) {
790             return info.getContainerInfo().getExtendedContainers().getContainerCase().getNumber()
791                     + EXTENDED_CONTAINERS_HIERARCHY_OFFSET;
792         } else if (info.getContainerInfo().getContainerCase() == ALL_APPS_CONTAINER) {
793             return info.getContainerInfo().getAllAppsContainer().getParentContainerCase()
794                     .getNumber() + ALL_APPS_HIERARCHY_OFFSET;
795         } else {
796             return info.getContainerInfo().getContainerCase().getNumber();
797         }
798     }
799 
getStateString(int state)800     private static String getStateString(int state) {
801         switch (state) {
802             case LAUNCHER_UICHANGED__DST_STATE__BACKGROUND:
803                 return "BACKGROUND";
804             case LAUNCHER_UICHANGED__DST_STATE__HOME:
805                 return "HOME";
806             case LAUNCHER_UICHANGED__DST_STATE__OVERVIEW:
807                 return "OVERVIEW";
808             case LAUNCHER_UICHANGED__DST_STATE__ALLAPPS:
809                 return "ALLAPPS";
810             default:
811                 return "INVALID";
812         }
813     }
814 
getFeatures(LauncherAtom.ItemInfo info)815     private static int getFeatures(LauncherAtom.ItemInfo info) {
816         if (info.getItemCase().equals(LauncherAtom.ItemInfo.ItemCase.WIDGET)) {
817             return info.getWidget().getWidgetFeatures();
818         }
819         return 0;
820     }
821 
getSearchAttributes(LauncherAtom.ItemInfo info)822     private static int getSearchAttributes(LauncherAtom.ItemInfo info) {
823         if (Utilities.isRunningInTestHarness()) {
824             return 0;
825         }
826         ContainerInfo containerInfo = info.getContainerInfo();
827         if (containerInfo.getContainerCase() == EXTENDED_CONTAINERS
828                 && containerInfo.getExtendedContainers().getContainerCase()
829                 == DEVICE_SEARCH_RESULT_CONTAINER
830                 && containerInfo.getExtendedContainers()
831                 .getDeviceSearchResultContainer().hasSearchAttributes()
832         ) {
833             return searchAttributesToInt(containerInfo.getExtendedContainers()
834                     .getDeviceSearchResultContainer().getSearchAttributes());
835         }
836         return 0;
837     }
838 
searchAttributesToInt(SearchAttributes searchAttributes)839     private static int searchAttributesToInt(SearchAttributes searchAttributes) {
840         int response = 0;
841         if (searchAttributes.getCorrectedQuery()) {
842             response = response | SEARCH_ATTRIBUTES_CORRECTED_QUERY;
843         }
844         if (searchAttributes.getDirectMatch()) {
845             response = response | SEARCH_ATTRIBUTES_DIRECT_MATCH;
846         }
847         if (searchAttributes.getEntryState() == SearchAttributes.EntryState.ALL_APPS) {
848             response = response | SEARCH_ATTRIBUTES_ENTRY_STATE_ALL_APPS;
849         } else if (searchAttributes.getEntryState() == SearchAttributes.EntryState.QSB) {
850             response = response | SEARCH_ATTRIBUTES_ENTRY_STATE_QSB;
851         }
852 
853         return response;
854     }
855 
856     /**
857      * Interface to get stats log while it is dispatched to the system
858      */
859     public interface StatsLogConsumer {
860 
861         @WorkerThread
consume(EventEnum event, LauncherAtom.ItemInfo atomInfo)862         void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo);
863     }
864 }
865