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.textclassifier.common.statsd; 18 19 import static com.google.common.base.Charsets.UTF_8; 20 import static com.google.common.base.Strings.nullToEmpty; 21 22 import com.android.textclassifier.common.base.TcLog; 23 import com.android.textclassifier.common.logging.ResultIdUtils; 24 import com.android.textclassifier.common.logging.TextClassificationContext; 25 import com.android.textclassifier.common.logging.TextClassificationSessionId; 26 import com.android.textclassifier.common.logging.TextClassifierEvent; 27 import com.google.common.base.Preconditions; 28 import com.google.common.collect.ImmutableList; 29 import com.google.common.hash.Hashing; 30 import java.util.List; 31 import javax.annotation.Nullable; 32 33 /** Logs {@link android.view.textclassifier.TextClassifierEvent}. */ 34 public final class TextClassifierEventLogger { 35 private static final String TAG = "TCEventLogger"; 36 37 /** Emits a text classifier event to the logs. */ writeEvent( @ullable TextClassificationSessionId sessionId, TextClassifierEvent event)38 public void writeEvent( 39 @Nullable TextClassificationSessionId sessionId, TextClassifierEvent event) { 40 Preconditions.checkNotNull(event); 41 if (TcLog.ENABLE_FULL_LOGGING) { 42 TcLog.v( 43 TAG, 44 String.format( 45 "TextClassifierEventLogger.writeEvent: sessionId=%s,event=%s", sessionId, event)); 46 } 47 if (event instanceof TextClassifierEvent.TextSelectionEvent) { 48 logTextSelectionEvent(sessionId, (TextClassifierEvent.TextSelectionEvent) event); 49 } else if (event instanceof TextClassifierEvent.TextLinkifyEvent) { 50 logTextLinkifyEvent(sessionId, (TextClassifierEvent.TextLinkifyEvent) event); 51 } else if (event instanceof TextClassifierEvent.ConversationActionsEvent) { 52 logConversationActionsEvent(sessionId, (TextClassifierEvent.ConversationActionsEvent) event); 53 } else if (event instanceof TextClassifierEvent.LanguageDetectionEvent) { 54 logLanguageDetectionEvent(sessionId, (TextClassifierEvent.LanguageDetectionEvent) event); 55 } else { 56 TcLog.w(TAG, "Unexpected events, category=" + event.getEventCategory()); 57 } 58 } 59 logTextSelectionEvent( @ullable TextClassificationSessionId sessionId, TextClassifierEvent.TextSelectionEvent event)60 private static void logTextSelectionEvent( 61 @Nullable TextClassificationSessionId sessionId, 62 TextClassifierEvent.TextSelectionEvent event) { 63 ImmutableList<String> modelNames = getModelNames(event); 64 TextClassifierStatsLog.write( 65 TextClassifierStatsLog.TEXT_SELECTION_EVENT, 66 sessionId == null ? null : sessionId.getValue(), 67 getEventType(event), 68 getItemAt(modelNames, /* index= */ 0, /* defaultValue= */ null), 69 getWidgetType(event), 70 event.getEventIndex(), 71 getItemAt(event.getEntityTypes(), /* index= */ 0), 72 event.getRelativeWordStartIndex(), 73 event.getRelativeWordEndIndex(), 74 event.getRelativeSuggestedWordStartIndex(), 75 event.getRelativeSuggestedWordEndIndex(), 76 getPackageName(event), 77 getItemAt(modelNames, /* index= */ 1, /* defaultValue= */ null)); 78 } 79 getEventType(TextClassifierEvent.TextSelectionEvent event)80 private static int getEventType(TextClassifierEvent.TextSelectionEvent event) { 81 if (event.getEventType() == TextClassifierEvent.TYPE_AUTO_SELECTION) { 82 if (ResultIdUtils.isFromDefaultTextClassifier(event.getResultId())) { 83 return event.getRelativeWordEndIndex() - event.getRelativeWordStartIndex() > 1 84 ? TextClassifierEvent.TYPE_SMART_SELECTION_MULTI 85 : TextClassifierEvent.TYPE_SMART_SELECTION_SINGLE; 86 } 87 } 88 return event.getEventType(); 89 } 90 logTextLinkifyEvent( TextClassificationSessionId sessionId, TextClassifierEvent.TextLinkifyEvent event)91 private static void logTextLinkifyEvent( 92 TextClassificationSessionId sessionId, TextClassifierEvent.TextLinkifyEvent event) { 93 ImmutableList<String> modelNames = getModelNames(event); 94 TextClassifierStatsLog.write( 95 TextClassifierStatsLog.TEXT_LINKIFY_EVENT, 96 sessionId == null ? null : sessionId.getValue(), 97 event.getEventType(), 98 getItemAt(modelNames, /* index= */ 0, /* defaultValue= */ null), 99 getWidgetType(event), 100 event.getEventIndex(), 101 getItemAt(event.getEntityTypes(), /* index= */ 0), 102 /* numOfLinks */ 0, 103 /* linkedTextLength */ 0, 104 /* textLength */ 0, 105 /* latencyInMillis */ 0L, 106 getPackageName(event), 107 getItemAt(modelNames, /* index= */ 1, /* defaultValue= */ null)); 108 } 109 logConversationActionsEvent( @ullable TextClassificationSessionId sessionId, TextClassifierEvent.ConversationActionsEvent event)110 private static void logConversationActionsEvent( 111 @Nullable TextClassificationSessionId sessionId, 112 TextClassifierEvent.ConversationActionsEvent event) { 113 String resultId = nullToEmpty(event.getResultId()); 114 ImmutableList<String> modelNames = ResultIdUtils.getModelNames(resultId); 115 TextClassifierStatsLog.write( 116 TextClassifierStatsLog.CONVERSATION_ACTIONS_EVENT, 117 // TODO: Update ExtServices to set the session id. 118 sessionId == null 119 ? Hashing.goodFastHash(64).hashString(resultId, UTF_8).toString() 120 : sessionId.getValue(), 121 event.getEventType(), 122 getItemAt(modelNames, /* index= */ 0, /* defaultValue= */ null), 123 getWidgetType(event), 124 getItemAt(event.getEntityTypes(), /* index= */ 0), 125 getItemAt(event.getEntityTypes(), /* index= */ 1), 126 getItemAt(event.getEntityTypes(), /* index= */ 2), 127 getFloatAt(event.getScores(), /* index= */ 0), 128 getPackageName(event), 129 getItemAt(modelNames, /* index= */ 1, /* defaultValue= */ null), 130 getItemAt(modelNames, /* index= */ 2, /* defaultValue= */ null)); 131 } 132 logLanguageDetectionEvent( @ullable TextClassificationSessionId sessionId, TextClassifierEvent.LanguageDetectionEvent event)133 private static void logLanguageDetectionEvent( 134 @Nullable TextClassificationSessionId sessionId, 135 TextClassifierEvent.LanguageDetectionEvent event) { 136 TextClassifierStatsLog.write( 137 TextClassifierStatsLog.LANGUAGE_DETECTION_EVENT, 138 sessionId == null ? null : sessionId.getValue(), 139 event.getEventType(), 140 getItemAt(getModelNames(event), /* index= */ 0, /* defaultValue= */ null), 141 getWidgetType(event), 142 getItemAt(event.getEntityTypes(), /* index= */ 0), 143 getFloatAt(event.getScores(), /* index= */ 0), 144 getIntAt(event.getActionIndices(), /* index= */ 0), 145 getPackageName(event)); 146 } 147 148 @Nullable getItemAt(List<T> list, int index, T defaultValue)149 private static <T> T getItemAt(List<T> list, int index, T defaultValue) { 150 if (list == null) { 151 return defaultValue; 152 } 153 if (index >= list.size()) { 154 return defaultValue; 155 } 156 return list.get(index); 157 } 158 159 @Nullable getItemAt(@ullable T[] array, int index)160 private static <T> T getItemAt(@Nullable T[] array, int index) { 161 if (array == null) { 162 return null; 163 } 164 if (index >= array.length) { 165 return null; 166 } 167 return array[index]; 168 } 169 getFloatAt(@ullable float[] array, int index)170 private static float getFloatAt(@Nullable float[] array, int index) { 171 if (array == null) { 172 return 0f; 173 } 174 if (index >= array.length) { 175 return 0f; 176 } 177 return array[index]; 178 } 179 getIntAt(@ullable int[] array, int index)180 private static int getIntAt(@Nullable int[] array, int index) { 181 if (array == null) { 182 return 0; 183 } 184 if (index >= array.length) { 185 return 0; 186 } 187 return array[index]; 188 } 189 getModelNames(TextClassifierEvent event)190 private static ImmutableList<String> getModelNames(TextClassifierEvent event) { 191 if (event.getModelName() != null) { 192 return ImmutableList.of(event.getModelName()); 193 } 194 return ResultIdUtils.getModelNames(event.getResultId()); 195 } 196 getWidgetType(TextClassifierEvent event)197 private static int getWidgetType(TextClassifierEvent event) { 198 TextClassificationContext eventContext = event.getEventContext(); 199 if (eventContext == null) { 200 return TextClassifierStatsLog.TEXT_SELECTION_EVENT__WIDGET_TYPE__WIDGET_TYPE_UNKNOWN; 201 } 202 return WidgetTypeConverter.toLoggingValue(eventContext.getWidgetType()); 203 } 204 205 @Nullable getPackageName(TextClassifierEvent event)206 private static String getPackageName(TextClassifierEvent event) { 207 TextClassificationContext eventContext = event.getEventContext(); 208 if (eventContext == null) { 209 return null; 210 } 211 return eventContext.getPackageName(); 212 } 213 } 214