1 /* 2 * Copyright (C) 2023 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.car.carlauncher.recents; 18 19 import android.annotation.IntDef; 20 import android.content.pm.ApplicationInfo; 21 import android.content.pm.PackageManager; 22 import android.os.Build; 23 import android.util.Log; 24 25 import com.android.car.carlauncher.CarLauncherStatsLog; 26 27 import java.util.UUID; 28 29 /** 30 * Helper class that directly interacts with CarLauncherStatsLog, a generated class that contains 31 * logging methods for CarRecentsActivity. 32 */ 33 public class RecentsStatsLogHelper { 34 public static final String TAG = "RecentsStatsLogHelper"; 35 private static RecentsStatsLogHelper sInstance; 36 private PackageManager mPackageManager; 37 private long mSessionId; 38 private long mStartTimeMs; 39 // Integer for taskIndex, to be logged by CarRecentsEventReported 40 public static final int UNSPECIFIED_INDEX = -1; 41 // Integer for totalTaskCount, same as above 42 public static final int UNSPECIFIED_COUNT = -1; 43 // String to be logged as packageName when packageName is not relevant to recents event 44 public static final String UNSPECIFIED_PACKAGE_NAME = "_PACKAGE_NAME_NOT_LOGGED"; 45 // Uid to be logged as uid when packageName is not relevant or cannot be resolved 46 public static final int UNSPECIFIED_PACKAGE_UID = -1; 47 48 /** 49 * IntDef representing enum values of CarRecentsEventReported.event_type. 50 */ 51 @IntDef({ 52 RecentsEventType.UNSPECIFIED, 53 RecentsEventType.SESSION_STARTED, 54 RecentsEventType.SESSION_FINISHED, 55 RecentsEventType.APP_LAUNCHED, 56 RecentsEventType.APP_DISMISSED, 57 RecentsEventType.CLEAR_ALL, 58 }) 59 public @interface RecentsEventType { 60 int UNSPECIFIED = CarLauncherStatsLog.CAR_RECENTS_EVENT_REPORTED__EVENT_TYPE__UNSPECIFIED; 61 int SESSION_STARTED = 62 CarLauncherStatsLog.CAR_RECENTS_EVENT_REPORTED__EVENT_TYPE__SESSION_STARTED; 63 int SESSION_FINISHED = 64 CarLauncherStatsLog.CAR_RECENTS_EVENT_REPORTED__EVENT_TYPE__SESSION_FINISHED; 65 int APP_LAUNCHED = 66 CarLauncherStatsLog.CAR_RECENTS_EVENT_REPORTED__EVENT_TYPE__APP_LAUNCHED; 67 int APP_DISMISSED = 68 CarLauncherStatsLog.CAR_RECENTS_EVENT_REPORTED__EVENT_TYPE__APP_DISMISSED; 69 int CLEAR_ALL = 70 CarLauncherStatsLog.CAR_RECENTS_EVENT_REPORTED__EVENT_TYPE__CLEAR_ALL; 71 } 72 73 /** 74 * Returns the current logging instance of RecentsStatsLogHelper to write this devices' 75 * CarLauncherStatsModule. 76 * 77 * @return the logging instance of RecentsStatsLogHelper. 78 */ getInstance()79 public static RecentsStatsLogHelper getInstance() { 80 if (sInstance == null) { 81 sInstance = new RecentsStatsLogHelper(); 82 } 83 return sInstance; 84 } 85 setPackageManager(PackageManager packageManager)86 public void setPackageManager(PackageManager packageManager) { 87 mPackageManager = packageManager; 88 } 89 90 /** 91 * Logs that a new recents session has started. Additionally, resets measurements and IDs such 92 * as session ID and start time. 93 */ logSessionStarted()94 public void logSessionStarted() { 95 mSessionId = UUID.randomUUID().getMostSignificantBits(); 96 mStartTimeMs = System.currentTimeMillis(); 97 writeCarRecentsEventReported(RecentsEventType.SESSION_STARTED); 98 } 99 100 /** 101 * Logs that an app launch interaction has occurred, along with the launched app's package name, 102 * the total open task count in recents, and the launched app's position. 103 */ logAppLaunched(int totalTaskCount, int eventTaskIndex, String packageName)104 public void logAppLaunched(int totalTaskCount, int eventTaskIndex, String packageName) { 105 writeCarRecentsEventReported( 106 /* eventType */ RecentsEventType.APP_LAUNCHED, 107 /* totalTaskCount */ totalTaskCount, 108 /* eventTaskIndex */ eventTaskIndex, 109 /* packageName */ packageName); 110 } 111 112 /** 113 * Logs that an app dismiss interaction has occurred, along with the dismissed app's package 114 * name, the total open task count in recents, and the dimissed app's position. 115 */ logAppDismissed(int totalTaskCount, int eventTaskIndex, String packageName)116 public void logAppDismissed(int totalTaskCount, int eventTaskIndex, String packageName) { 117 writeCarRecentsEventReported( 118 /* eventType */ RecentsStatsLogHelper.RecentsEventType.APP_DISMISSED, 119 /* totalTaskCount */ totalTaskCount, 120 /* eventTaskIndex */ eventTaskIndex, 121 /* packageName */ packageName); 122 } 123 124 /** 125 * Logs that clear all has been logged, along with the total open task count in recents. 126 */ logClearAll(int totalTaskCount)127 public void logClearAll(int totalTaskCount) { 128 writeCarRecentsEventReported( 129 /* eventType */ RecentsStatsLogHelper.RecentsEventType.CLEAR_ALL, 130 /* totalTaskCount */ totalTaskCount, 131 /* eventTaskIndex */ UNSPECIFIED_INDEX, 132 /* packageName */ UNSPECIFIED_PACKAGE_NAME); 133 } 134 135 /** 136 * Logs that the current recents session has finished. 137 */ logSessionFinished()138 public void logSessionFinished() { 139 writeCarRecentsEventReported(RecentsEventType.SESSION_FINISHED); 140 } 141 142 /** 143 * Writes to CarRecentsEvent atom with {@code eventType} as the only field, and log all other 144 * fields as unspecified. 145 * 146 * @param eventType one of {@link RecentsEventType} 147 */ writeCarRecentsEventReported(int eventType)148 private void writeCarRecentsEventReported(int eventType) { 149 writeCarRecentsEventReported(eventType, /* totalTaskCount */ UNSPECIFIED_COUNT, 150 /* eventTaskIndex */ UNSPECIFIED_INDEX, /* packageName */ UNSPECIFIED_PACKAGE_NAME); 151 } 152 153 /** 154 * Writes to CarRecentsEvent atom with all the optional fields filled. 155 * 156 * @param eventType one of {@link RecentsEventType} 157 * @param totalTaskCount the number of tasks displayed in recents screen 158 * @param eventTaskIndex the index of the recents task of this interaction 159 * @param packageName the package name of the app interacted with 160 */ writeCarRecentsEventReported(int eventType, int totalTaskCount, int eventTaskIndex, String packageName)161 private void writeCarRecentsEventReported(int eventType, int totalTaskCount, 162 int eventTaskIndex, String packageName) { 163 if (Build.isDebuggable()) { 164 Log.v(TAG, "writing CAR_RECENTS_EVENT_REPORTED with eventType=" + eventType 165 + ", packageName=" + packageName); 166 } 167 writeCarRecentsEventReported( 168 /* sessionId */ mSessionId, 169 /* eventId */ UUID.randomUUID().getMostSignificantBits(), 170 /* eventType */ eventType, 171 /* totalTaskCount */ totalTaskCount, 172 /* taskIndex */ eventTaskIndex, 173 /* timeToEventMs */ System.currentTimeMillis() - mStartTimeMs, 174 /* packageUid */ getPackageUid(packageName)); 175 } 176 getPackageUid(String packageName)177 private int getPackageUid(String packageName) { 178 if (packageName == null) { 179 return UNSPECIFIED_PACKAGE_UID; 180 } 181 try { 182 ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 183 PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA)); 184 return appInfo.uid; 185 } catch (PackageManager.NameNotFoundException e) { 186 Log.d(TAG, "getPackageUid() on " + packageName + " was not found"); 187 } 188 return UNSPECIFIED_PACKAGE_UID; 189 } 190 writeCarRecentsEventReported(long sessionId, long eventId, int eventType, int totalTaskCount, int eventTaskIndex, long timeToEventMs, int packageUid)191 private void writeCarRecentsEventReported(long sessionId, long eventId, int eventType, 192 int totalTaskCount, int eventTaskIndex, long timeToEventMs, int packageUid) { 193 CarLauncherStatsLog.write( 194 /* atomId */ CarLauncherStatsLog.CAR_RECENTS_EVENT_REPORTED, 195 /* session_id */ sessionId, 196 /* event_id */ eventId, 197 /* event_type */ eventType, 198 /* total_task_count */ totalTaskCount, 199 /* event_task_index */ eventTaskIndex, 200 /* long time_to_event_millis */ timeToEventMs, 201 /* package_uid */ packageUid); 202 } 203 } 204