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