Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 03a1d033 authored by Tony Mak's avatar Tony Mak
Browse files

Add g/setScore and g/setEntityTypes to TextClassifierEvent

1. Add get/setScore, so we can see how do scores affect the CTR and
eventually tune the threshold based on it.

2. Convert get/setEntity to get/setEntityTypes.
   Some events like TYPE_ACTIONS_GENERATED and TYPE_ACTIONS_SHOWN
   will need to have a list of entity types. Also, as TRON does not
   support a field of list type. And thus workaround this by having
   3 separate fields in TRON.

4. The result id of action is too easy to clash now as only messages
   are taking into account. Use timestamp and the person object as
   the input to the hash now.

5. Changes in TRON:
   a) We will put conversation_action / language detection, etc to
      category. And we will put the views to subtype.
   b) As suggested by the TRON team, we should not reuse existing fields
   in the clients directly. But we should rather introduce new fields,
   and map them to existing fields in the server. So, TRON team could
   suppress just the new logging if it goes wrong and not affect
   existing loggings.

Test: atest TextClassifierEventTest.java
Tests: atest TextClassifierEventTronLoggerTest.java

BUG: 120803809

Change-Id: I2e71436b323a200beec57ea7c1a8bcc96c9c552f
parent 07182c86
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -53560,7 +53560,7 @@ package android.view.textclassifier {
  public final class TextClassifierEvent implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public int[] getActionIndices();
    method @Nullable public String getEntityType();
    method @NonNull public String[] getEntityTypes();
    method public int getEventCategory();
    method @Nullable public android.view.textclassifier.TextClassificationContext getEventContext();
    method public int getEventIndex();
@@ -53573,6 +53573,7 @@ package android.view.textclassifier {
    method public int getRelativeWordEndIndex();
    method public int getRelativeWordStartIndex();
    method @Nullable public String getResultId();
    method public float getScore();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final int CATEGORY_CONVERSATION_ACTIONS = 3; // 0x3
    field public static final int CATEGORY_LANGUAGE_DETECTION = 4; // 0x4
@@ -53607,7 +53608,7 @@ package android.view.textclassifier {
    ctor public TextClassifierEvent.Builder(int, int);
    method @NonNull public android.view.textclassifier.TextClassifierEvent build();
    method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setActionIndices(@NonNull int...);
    method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEntityType(@Nullable String);
    method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEntityTypes(@NonNull java.lang.String...);
    method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventContext(@Nullable android.view.textclassifier.TextClassificationContext);
    method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventIndex(int);
    method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventTime(long);
@@ -53618,6 +53619,7 @@ package android.view.textclassifier {
    method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordEndIndex(int);
    method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordStartIndex(int);
    method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setResultId(@Nullable String);
    method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setScore(float);
  }
  public final class TextLanguage implements android.os.Parcelable {
+7 −4
Original line number Diff line number Diff line
@@ -103,10 +103,9 @@ public final class ActionsSuggestionsHelper {
        final String modelName = String.format(
                Locale.US, "%s_v%d", localesJoiner.toString(), modelVersion);
        final int hash = Objects.hash(
                messages.stream()
                        .map(ConversationActions.Message::getText)
                        .collect(Collectors.toList()),
                context.getPackageName());
                messages.stream().mapToInt(ActionsSuggestionsHelper::hashMessage),
                context.getPackageName(),
                System.currentTimeMillis());
        return SelectionSessionLogger.SignatureParser.createSignature(
                SelectionSessionLogger.CLASSIFIER_ID, modelName, hash);
    }
@@ -128,4 +127,8 @@ public final class ActionsSuggestionsHelper {
            return result;
        }
    }

    private static int hashMessage(ConversationActions.Message message) {
        return Objects.hash(message.getAuthor(), message.getText(), message.getReferenceTime());
    }
}
+44 −19
Original line number Diff line number Diff line
@@ -72,7 +72,7 @@ public final class TextClassifierEvent implements Parcelable {
             TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION,
             TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION,
             TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL,
             TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY})
             TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED})
    public @interface Type {
        // For custom event types, use range 1,000,000+.
    }
@@ -121,7 +121,7 @@ public final class TextClassifierEvent implements Parcelable {

    @Category private final int mEventCategory;
    @Type private final int mEventType;
    @Nullable private final String mEntityType;
    @Nullable private final String[] mEntityTypes;
    @Nullable private final TextClassificationContext mEventContext;
    @Nullable private final String mResultId;
    private final int mEventIndex;
@@ -139,11 +139,12 @@ public final class TextClassifierEvent implements Parcelable {

    // Language detection.
    @Nullable private final String mLanguage;
    private final float mScore;

    private TextClassifierEvent(
            int eventCategory,
            int eventType,
            String entityType,
            String[] entityTypes,
            TextClassificationContext eventContext,
            String resultId,
            int eventIndex,
@@ -154,10 +155,11 @@ public final class TextClassifierEvent implements Parcelable {
            int relativeSuggestedWordStartIndex,
            int relativeSuggestedWordEndIndex,
            int[] actionIndex,
            String language) {
            String language,
            float score) {
        mEventCategory = eventCategory;
        mEventType = eventType;
        mEntityType = entityType;
        mEntityTypes = entityTypes;
        mEventContext = eventContext;
        mResultId = resultId;
        mEventIndex = eventIndex;
@@ -169,6 +171,7 @@ public final class TextClassifierEvent implements Parcelable {
        mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex;
        mActionIndices = actionIndex;
        mLanguage = language;
        mScore = score;
    }

    @Override
@@ -180,7 +183,7 @@ public final class TextClassifierEvent implements Parcelable {
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mEventCategory);
        dest.writeInt(mEventType);
        dest.writeString(mEntityType);
        dest.writeStringArray(mEntityTypes);
        dest.writeParcelable(mEventContext, flags);
        dest.writeString(mResultId);
        dest.writeInt(mEventIndex);
@@ -192,13 +195,14 @@ public final class TextClassifierEvent implements Parcelable {
        dest.writeInt(mRelativeSuggestedWordEndIndex);
        dest.writeIntArray(mActionIndices);
        dest.writeString(mLanguage);
        dest.writeFloat(mScore);
    }

    private static TextClassifierEvent readFromParcel(Parcel in) {
        return new TextClassifierEvent(
                /* eventCategory= */ in.readInt(),
                /* eventType= */ in.readInt(),
                /* entityType= */ in.readString(),
                /* entityTypes=*/ in.readStringArray(),
                /* eventContext= */ in.readParcelable(null),
                /* resultId= */ in.readString(),
                /* eventIndex= */ in.readInt(),
@@ -209,7 +213,8 @@ public final class TextClassifierEvent implements Parcelable {
                /* relativeSuggestedWordStartIndex= */ in.readInt(),
                /* relativeSuggestedWordEndIndex= */ in.readInt(),
                /* actionIndices= */ in.createIntArray(),
                /* language= */ in.readString());
                /* language= */ in.readString(),
                /* score= */ in.readFloat());
    }

    /**
@@ -229,11 +234,11 @@ public final class TextClassifierEvent implements Parcelable {
    }

    /**
     * Returns the entity type. e.g. {@link TextClassifier#TYPE_ADDRESS}.
     * Returns an array of entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}.
     */
    @Nullable
    public String getEntityType() {
        return mEntityType;
    @NonNull
    public String[] getEntityTypes() {
        return mEntityTypes;
    }

    /**
@@ -326,6 +331,13 @@ public final class TextClassifierEvent implements Parcelable {
        return mLanguage;
    }

    /**
     * Returns the score of the suggestion.
     */
    public float getScore() {
        return mScore;
    }

    /**
     * Builder to build a text classifier event.
     */
@@ -333,7 +345,7 @@ public final class TextClassifierEvent implements Parcelable {

        private final int mEventCategory;
        private final int mEventType;
        @Nullable private String mEntityType;
        private String[] mEntityTypes = new String[0];
        @Nullable private TextClassificationContext mEventContext;
        @Nullable private String mResultId;
        private int mEventIndex;
@@ -345,6 +357,7 @@ public final class TextClassifierEvent implements Parcelable {
        private int mRelativeSuggestedWordEndIndex;
        private int[] mActionIndices = new int[0];
        @Nullable private String mLanguage;
        private float mScore;

        /**
         * Creates a builder for building {@link TextClassifierEvent}s.
@@ -358,11 +371,12 @@ public final class TextClassifierEvent implements Parcelable {
        }

        /**
         * Sets the entity type. e.g. {@link TextClassifier#TYPE_ADDRESS}.
         * Sets the entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}.
         */
        @NonNull
        public Builder setEntityType(@Nullable String entityType) {
            mEntityType = entityType;
        public Builder setEntityTypes(@NonNull String... entityTypes) {
            mEntityTypes = new String[entityTypes.length];
            System.arraycopy(entityTypes, 0, mEntityTypes, 0, entityTypes.length);
            return this;
        }

@@ -477,6 +491,15 @@ public final class TextClassifierEvent implements Parcelable {
            return this;
        }

        /**
         * Sets the score of the suggestion.
         */
        @NonNull
        public Builder setScore(float score) {
            mScore = score;
            return this;
        }

        /**
         * Builds and returns a text classifier event.
         */
@@ -486,7 +509,7 @@ public final class TextClassifierEvent implements Parcelable {
            return new TextClassifierEvent(
                    mEventCategory,
                    mEventType,
                    mEntityType,
                    mEntityTypes,
                    mEventContext,
                    mResultId,
                    mEventIndex,
@@ -497,7 +520,8 @@ public final class TextClassifierEvent implements Parcelable {
                    mRelativeSuggestedWordStartIndex,
                    mRelativeSuggestedWordEndIndex,
                    mActionIndices,
                    mLanguage);
                    mLanguage,
                    mScore);
        }
        // TODO: Add build(boolean validate).
    }
@@ -507,7 +531,7 @@ public final class TextClassifierEvent implements Parcelable {
        StringBuilder out = new StringBuilder(128);
        out.append("TextClassifierEvent{");
        out.append("mEventCategory=").append(mEventCategory);
        out.append(", mEventType=").append(mEventType);
        out.append(", mEventTypes=").append(Arrays.toString(mEntityTypes));
        out.append(", mEventContext=").append(mEventContext);
        out.append(", mResultId=").append(mResultId);
        out.append(", mEventIndex=").append(mEventIndex);
@@ -519,6 +543,7 @@ public final class TextClassifierEvent implements Parcelable {
        out.append(", mRelativeSuggestedWordEndIndex=").append(mRelativeSuggestedWordEndIndex);
        out.append(", mActionIndices=").append(Arrays.toString(mActionIndices));
        out.append(", mLanguage=").append(mLanguage);
        out.append(", mScore=").append(mScore);
        out.append("}");
        return out.toString();
    }
+45 −15
Original line number Diff line number Diff line
@@ -15,12 +15,15 @@
 */
package android.view.textclassifier;

import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_SESSION_ID;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SESSION_ID;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_VERSION;

import android.metrics.LogMaker;

@@ -60,16 +63,30 @@ public final class TextClassifierEventTronLogger {
            return;
        }
        final LogMaker log = new LogMaker(category)
                .setType(getLogType(event))
                .addTaggedData(FIELD_SELECTION_SESSION_ID, event.getResultId())
                .setSubtype(getLogType(event))
                .addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId())
                .addTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME, event.getEventTime())
                .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL,
                        SelectionSessionLogger.SignatureParser.getModelName(event.getResultId()))
                .addTaggedData(FIELD_SELECTION_ENTITY_TYPE, event.getEntityType());
                .addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScore());

        String[] entityTypes = event.getEntityTypes();
        // TRON does not support a field of list type, and thus workaround by store them
        // in three separate fields. This is no longer an issue once we have moved to Westworld.
        if (entityTypes.length >= 1) {
            log.addTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE, entityTypes[0]);
        }
        if (entityTypes.length >= 2) {
            log.addTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE, entityTypes[1]);
        }
        if (entityTypes.length >= 3) {
            log.addTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE, entityTypes[2]);
        }
        TextClassificationContext eventContext = event.getEventContext();
        if (eventContext != null) {
            log.addTaggedData(FIELD_SELECTION_WIDGET_TYPE, eventContext.getWidgetType());
            log.addTaggedData(FIELD_SELECTION_WIDGET_VERSION, eventContext.getWidgetVersion());
            log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE, eventContext.getWidgetType());
            log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION,
                    eventContext.getWidgetVersion());
            log.setPackageName(eventContext.getPackageName());
        }
        mMetricsLogger.write(log);
@@ -94,6 +111,8 @@ public final class TextClassifierEventTronLogger {
                return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN;
            case TextClassifierEvent.TYPE_MANUAL_REPLY:
                return MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY;
            case TextClassifierEvent.TYPE_ACTIONS_GENERATED:
                return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED;
            default:
                return MetricsEvent.VIEW_UNKNOWN;
        }
@@ -127,14 +146,22 @@ public final class TextClassifierEventTronLogger {
        if (!Log.ENABLE_FULL_LOGGING) {
            return;
        }
        final String id = String.valueOf(log.getTaggedData(FIELD_SELECTION_SESSION_ID));
        final String id = String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID));
        final String categoryName = toCategoryName(log.getCategory());
        final String eventName = toEventName(log.getType());
        final String widgetType = String.valueOf(log.getTaggedData(FIELD_SELECTION_WIDGET_TYPE));
        final String eventName = toEventName(log.getSubtype());
        final String widgetType =
                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE));
        final String widgetVersion =
                String.valueOf(log.getTaggedData(FIELD_SELECTION_WIDGET_VERSION));
                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION));
        final String model = String.valueOf(log.getTaggedData(FIELD_TEXTCLASSIFIER_MODEL));
        final String entityType = String.valueOf(log.getTaggedData(FIELD_SELECTION_ENTITY_TYPE));
        final String firstEntityType =
                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE));
        final String secondEntityType =
                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE));
        final String thirdEntityType =
                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE));
        final String score =
                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE));

        StringBuilder builder = new StringBuilder();
        builder.append("writeEvent: ");
@@ -144,7 +171,10 @@ public final class TextClassifierEventTronLogger {
        builder.append(", widgetType=").append(widgetType);
        builder.append(", widgetVersion=").append(widgetVersion);
        builder.append(", model=").append(model);
        builder.append(", entityType=").append(entityType);
        builder.append(", firstEntityType=").append(firstEntityType);
        builder.append(", secondEntityType=").append(secondEntityType);
        builder.append(", thirdEntityType=").append(thirdEntityType);
        builder.append(", score=").append(score);

        Log.v(TAG, builder.toString());
    }
+11 −6
Original line number Diff line number Diff line
@@ -18,9 +18,10 @@ package android.view.textclassifier.logging;

import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.CONVERSATION_ACTIONS;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;

import static com.google.common.truth.Truth.assertThat;

@@ -71,7 +72,8 @@ public class TextClassifierEventTronLoggerTest {
                new TextClassifierEvent.Builder(
                        TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS,
                        TextClassifierEvent.TYPE_SMART_ACTION)
                        .setEntityType(ConversationAction.TYPE_CALL_PHONE)
                        .setEntityTypes(ConversationAction.TYPE_CALL_PHONE)
                        .setScore(0.5f)
                        .setEventTime(EVENT_TIME)
                        .setEventContext(textClassificationContext)
                        .build();
@@ -83,15 +85,18 @@ public class TextClassifierEventTronLoggerTest {
        LogMaker logMaker = captor.getValue();
        assertThat(logMaker.getCategory()).isEqualTo(
                CONVERSATION_ACTIONS);
        assertThat(logMaker.getType()).isEqualTo(
        assertThat(logMaker.getSubtype()).isEqualTo(
                ACTION_TEXT_SELECTION_SMART_SHARE);
        assertThat(logMaker.getTaggedData(FIELD_SELECTION_ENTITY_TYPE))
        assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE))
                .isEqualTo(ConversationAction.TYPE_CALL_PHONE);
        assertThat((float) logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE))
                .isWithin(0.00001f).of(0.5f);
        assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME))
                .isEqualTo(EVENT_TIME);
        assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME);
        assertThat(logMaker.getTaggedData(FIELD_SELECTION_WIDGET_TYPE))
        assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE))
                .isEqualTo(WIDGET_TYPE);

    }

    @Test
Loading