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