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