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 package android.view.textclassifier;
17 
18 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
20 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
21 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE;
22 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SESSION_ID;
23 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE;
24 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
25 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_VERSION;
26 
27 import android.metrics.LogMaker;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.internal.logging.MetricsLogger;
31 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
32 import com.android.internal.util.Preconditions;
33 
34 
35 /**
36  * Log {@link TextClassifierEvent} by using Tron, only support language detection and
37  * conversation actions.
38  *
39  * @hide
40  */
41 public final class TextClassifierEventTronLogger {
42 
43     private static final String TAG = "TCEventTronLogger";
44 
45     private final MetricsLogger mMetricsLogger;
46 
TextClassifierEventTronLogger()47     public TextClassifierEventTronLogger() {
48         this(new MetricsLogger());
49     }
50 
51     @VisibleForTesting
TextClassifierEventTronLogger(MetricsLogger metricsLogger)52     public TextClassifierEventTronLogger(MetricsLogger metricsLogger) {
53         mMetricsLogger = Preconditions.checkNotNull(metricsLogger);
54     }
55 
56     /** Emits a text classifier event to the logs. */
writeEvent(TextClassifierEvent event)57     public void writeEvent(TextClassifierEvent event) {
58         Preconditions.checkNotNull(event);
59 
60         int category = getCategory(event);
61         if (category == -1) {
62             Log.w(TAG, "Unknown category: " + event.getEventCategory());
63             return;
64         }
65         final LogMaker log = new LogMaker(category)
66                 .setSubtype(getLogType(event))
67                 .addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId())
68                 .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event));
69         if (event.getScores().length >= 1) {
70             log.addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScores()[0]);
71         }
72         String[] entityTypes = event.getEntityTypes();
73         // The old logger does not support a field of list type, and thus workaround by store them
74         // in three separate fields. This is not an issue with the new logger.
75         if (entityTypes.length >= 1) {
76             log.addTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE, entityTypes[0]);
77         }
78         if (entityTypes.length >= 2) {
79             log.addTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE, entityTypes[1]);
80         }
81         if (entityTypes.length >= 3) {
82             log.addTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE, entityTypes[2]);
83         }
84         TextClassificationContext eventContext = event.getEventContext();
85         if (eventContext != null) {
86             log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE, eventContext.getWidgetType());
87             log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION,
88                     eventContext.getWidgetVersion());
89             log.setPackageName(eventContext.getPackageName());
90         }
91         mMetricsLogger.write(log);
92         debugLog(log);
93     }
94 
getModelName(TextClassifierEvent event)95     private static String getModelName(TextClassifierEvent event) {
96         if (event.getModelName() != null) {
97             return event.getModelName();
98         }
99         return SelectionSessionLogger.SignatureParser.getModelName(event.getResultId());
100     }
101 
getCategory(TextClassifierEvent event)102     private static int getCategory(TextClassifierEvent event) {
103         switch (event.getEventCategory()) {
104             case TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS:
105                 return MetricsEvent.CONVERSATION_ACTIONS;
106             case TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION:
107                 return MetricsEvent.LANGUAGE_DETECTION;
108         }
109         return -1;
110     }
111 
getLogType(TextClassifierEvent event)112     private static int getLogType(TextClassifierEvent event) {
113         switch (event.getEventType()) {
114             case TextClassifierEvent.TYPE_SMART_ACTION:
115                 return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
116             case TextClassifierEvent.TYPE_ACTIONS_SHOWN:
117                 return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN;
118             case TextClassifierEvent.TYPE_MANUAL_REPLY:
119                 return MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY;
120             case TextClassifierEvent.TYPE_ACTIONS_GENERATED:
121                 return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED;
122             default:
123                 return MetricsEvent.VIEW_UNKNOWN;
124         }
125     }
126 
toCategoryName(int category)127     private String toCategoryName(int category) {
128         switch (category) {
129             case MetricsEvent.CONVERSATION_ACTIONS:
130                 return "conversation_actions";
131             case MetricsEvent.LANGUAGE_DETECTION:
132                 return "language_detection";
133         }
134         return "unknown";
135     }
136 
toEventName(int logType)137     private String toEventName(int logType) {
138         switch (logType) {
139             case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
140                 return "smart_share";
141             case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN:
142                 return "actions_shown";
143             case MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY:
144                 return "manual_reply";
145             case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED:
146                 return "actions_generated";
147         }
148         return "unknown";
149     }
150 
debugLog(LogMaker log)151     private void debugLog(LogMaker log) {
152         if (!Log.ENABLE_FULL_LOGGING) {
153             return;
154         }
155         final String id = String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID));
156         final String categoryName = toCategoryName(log.getCategory());
157         final String eventName = toEventName(log.getSubtype());
158         final String widgetType =
159                 String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE));
160         final String widgetVersion =
161                 String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION));
162         final String model = String.valueOf(log.getTaggedData(FIELD_TEXTCLASSIFIER_MODEL));
163         final String firstEntityType =
164                 String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE));
165         final String secondEntityType =
166                 String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE));
167         final String thirdEntityType =
168                 String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE));
169         final String score =
170                 String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE));
171 
172         StringBuilder builder = new StringBuilder();
173         builder.append("writeEvent: ");
174         builder.append("id=").append(id);
175         builder.append(", category=").append(categoryName);
176         builder.append(", eventName=").append(eventName);
177         builder.append(", widgetType=").append(widgetType);
178         builder.append(", widgetVersion=").append(widgetVersion);
179         builder.append(", model=").append(model);
180         builder.append(", firstEntityType=").append(firstEntityType);
181         builder.append(", secondEntityType=").append(secondEntityType);
182         builder.append(", thirdEntityType=").append(thirdEntityType);
183         builder.append(", score=").append(score);
184 
185         Log.v(TAG, builder.toString());
186     }
187 }
188