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

Commit cb6854eb authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement TextClassifier.getLogger API"

parents 4878292b 3bb44361
Loading
Loading
Loading
Loading
+70 −0
Original line number Diff line number Diff line
@@ -50095,6 +50095,7 @@ package android.view.textclassifier {
    method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence, android.view.textclassifier.TextLinks.Options);
    method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence);
    method public default java.util.Collection<java.lang.String> getEntitiesForPreset(int);
    method public default android.view.textclassifier.logging.Logger getLogger(android.view.textclassifier.logging.Logger.Config);
    method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options);
    method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
    method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
@@ -50190,6 +50191,75 @@ package android.view.textclassifier {
}
package android.view.textclassifier.logging {
  public abstract class Logger {
    ctor public Logger(android.view.textclassifier.logging.Logger.Config);
    method public java.text.BreakIterator getTokenIterator(java.util.Locale);
    method public boolean isSmartSelection(java.lang.String);
    method public final void logSelectionActionEvent(int, int, int);
    method public final void logSelectionActionEvent(int, int, int, android.view.textclassifier.TextClassification);
    method public final void logSelectionModifiedEvent(int, int);
    method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextClassification);
    method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextSelection);
    method public final void logSelectionStartedEvent(int);
    method public abstract void writeEvent(android.view.textclassifier.logging.SelectionEvent);
    field public static final int OUT_OF_BOUNDS = 2147483647; // 0x7fffffff
    field public static final int OUT_OF_BOUNDS_NEGATIVE = -2147483648; // 0x80000000
    field public static final java.lang.String WIDGET_CUSTOM_EDITTEXT = "customedit";
    field public static final java.lang.String WIDGET_CUSTOM_TEXTVIEW = "customview";
    field public static final java.lang.String WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
    field public static final java.lang.String WIDGET_EDITTEXT = "edittext";
    field public static final java.lang.String WIDGET_EDIT_WEBVIEW = "edit-webview";
    field public static final java.lang.String WIDGET_TEXTVIEW = "textview";
    field public static final java.lang.String WIDGET_UNKNOWN = "unknown";
    field public static final java.lang.String WIDGET_UNSELECTABLE_TEXTVIEW = "nosel-textview";
    field public static final java.lang.String WIDGET_WEBVIEW = "webview";
  }
  public static final class Logger.Config {
    ctor public Logger.Config(android.content.Context, java.lang.String, java.lang.String);
    method public java.lang.String getPackageName();
    method public java.lang.String getWidgetType();
    method public java.lang.String getWidgetVersion();
  }
  public final class SelectionEvent {
    method public long getDurationSincePreviousEvent();
    method public long getDurationSinceSessionStart();
    method public int getEnd();
    method public java.lang.String getEntityType();
    method public int getEventIndex();
    method public long getEventTime();
    method public int getEventType();
    method public java.lang.String getPackageName();
    method public java.lang.String getSessionId();
    method public java.lang.String getSignature();
    method public int getSmartEnd();
    method public int getSmartStart();
    method public int getStart();
    method public java.lang.String getWidgetType();
    method public java.lang.String getWidgetVersion();
    field public static final int ACTION_ABANDON = 107; // 0x6b
    field public static final int ACTION_COPY = 101; // 0x65
    field public static final int ACTION_CUT = 103; // 0x67
    field public static final int ACTION_DRAG = 106; // 0x6a
    field public static final int ACTION_OTHER = 108; // 0x6c
    field public static final int ACTION_OVERTYPE = 100; // 0x64
    field public static final int ACTION_PASTE = 102; // 0x66
    field public static final int ACTION_RESET = 201; // 0xc9
    field public static final int ACTION_SELECT_ALL = 200; // 0xc8
    field public static final int ACTION_SHARE = 104; // 0x68
    field public static final int ACTION_SMART_SHARE = 105; // 0x69
    field public static final int EVENT_AUTO_SELECTION = 5; // 0x5
    field public static final int EVENT_SELECTION_MODIFIED = 2; // 0x2
    field public static final int EVENT_SELECTION_STARTED = 1; // 0x1
    field public static final int EVENT_SMART_SELECTION_MULTI = 4; // 0x4
    field public static final int EVENT_SMART_SELECTION_SINGLE = 3; // 0x3
  }
}
package android.view.textservice {
  public final class SentenceSuggestionsInfo implements android.os.Parcelable {
+7 −5
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
import android.util.Slog;
import android.view.textclassifier.logging.Logger;

import com.android.internal.util.Preconditions;

@@ -319,14 +320,15 @@ public interface TextClassifier {
    }

    /**
     * Logs a TextClassifier event.
     * Returns a helper for logging TextClassifier related events.
     *
     * @param source the text classifier used to generate this event
     * @param event the text classifier related event
     * @hide
     * @param config logger configuration
     */
    @WorkerThread
    default void logEvent(String source, String event) {}
    default Logger getLogger(@NonNull Logger.Config config) {
        Preconditions.checkNotNull(config);
        return Logger.DISABLED;
    }

    /**
     * Returns this TextClassifier's settings.
+20 −11
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ import android.provider.ContactsContract;
import android.provider.Settings;
import android.text.util.Linkify;
import android.util.Patterns;
import android.view.textclassifier.logging.DefaultLogger;
import android.view.textclassifier.logging.Logger;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -43,6 +45,7 @@ import com.android.internal.util.Preconditions;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -104,6 +107,12 @@ public final class TextClassifierImpl implements TextClassifier {
    @GuardedBy("mLock") // Do not access outside this lock.
    private SmartSelection mSmartSelection;

    private final Object mLoggerLock = new Object();
    @GuardedBy("mLoggerLock") // Do not access outside this lock.
    private WeakReference<Logger.Config> mLoggerConfig = new WeakReference<>(null);
    @GuardedBy("mLoggerLock") // Do not access outside this lock.
    private Logger mLogger;  // Should never be null if mLoggerConfig.get() is not null.

    private TextClassifierConstants mSettings;

    public TextClassifierImpl(Context context) {
@@ -245,11 +254,15 @@ public final class TextClassifierImpl implements TextClassifier {
        }
    }

    /** @hide */
    @Override
    public void logEvent(String source, String event) {
        if (LOG_TAG.equals(source)) {
            mMetricsLogger.count(event, 1);
    public Logger getLogger(@NonNull Logger.Config config) {
        Preconditions.checkNotNull(config);
        synchronized (mLoggerLock) {
            if (mLoggerConfig.get() == null || !mLoggerConfig.get().equals(config)) {
                mLoggerConfig = new WeakReference<>(config);
                mLogger = new DefaultLogger(config);
            }
            return mLogger;
        }
    }

@@ -285,11 +298,7 @@ public final class TextClassifierImpl implements TextClassifier {

    private String getSignature(String text, int start, int end) {
        synchronized (mLock) {
            final String versionInfo = (mLocale != null)
                    ? String.format(Locale.US, "%s_v%d", mLocale.toLanguageTag(), mVersion)
                    : "";
            final int hash = Objects.hash(text, start, end, mContext.getPackageName());
            return String.format(Locale.US, "%s|%s|%d", LOG_TAG, versionInfo, hash);
            return DefaultLogger.createSignature(text, start, end, mContext, mVersion, mLocale);
        }
    }

@@ -328,7 +337,7 @@ public final class TextClassifierImpl implements TextClassifier {
                return factoryFd;
            } else {
                throw new FileNotFoundException(
                        String.format("No model file found for %s", locale));
                        String.format(Locale.US, "No model file found for %s", locale));
            }
        }

@@ -342,7 +351,7 @@ public final class TextClassifierImpl implements TextClassifier {
            } else {
                closeAndLogError(updateFd);
                throw new FileNotFoundException(
                        String.format("No model file found for %s", locale));
                        String.format(Locale.US, "No model file found for %s", locale));
            }
        }

+263 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view.textclassifier.logging;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.metrics.LogMaker;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.Preconditions;

import java.util.Locale;
import java.util.Objects;

/**
 * Default Logger.
 * Used internally by TextClassifierImpl.
 * @hide
 */
public final class DefaultLogger extends Logger {

    private static final String LOG_TAG = "DefaultLogger";
    private static final String CLASSIFIER_ID = "androidtc";

    private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
    private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
    private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
    private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
    private static final int WIDGET_VERSION = MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
    private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
    private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
    private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
    private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
    private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
    private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
    private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;

    private static final String ZERO = "0";
    private static final String UNKNOWN = "unknown";

    private final MetricsLogger mMetricsLogger;

    public DefaultLogger(@NonNull Config config) {
        super(config);
        mMetricsLogger = new MetricsLogger();
    }

    @VisibleForTesting
    public DefaultLogger(@NonNull Config config, @NonNull MetricsLogger metricsLogger) {
        super(config);
        mMetricsLogger = Preconditions.checkNotNull(metricsLogger);
    }

    @Override
    public boolean isSmartSelection(@NonNull String signature) {
        return CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature));
    }

    @Override
    public void writeEvent(@NonNull SelectionEvent event) {
        Preconditions.checkNotNull(event);
        final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
                .setType(getLogType(event))
                .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL)
                .setPackageName(event.getPackageName())
                .addTaggedData(START_EVENT_DELTA, event.getDurationSinceSessionStart())
                .addTaggedData(PREV_EVENT_DELTA, event.getDurationSincePreviousEvent())
                .addTaggedData(INDEX, event.getEventIndex())
                .addTaggedData(WIDGET_TYPE, event.getWidgetType())
                .addTaggedData(WIDGET_VERSION, event.getWidgetVersion())
                .addTaggedData(MODEL_NAME, SignatureParser.getModelName(event.getSignature()))
                .addTaggedData(ENTITY_TYPE, event.getEntityType())
                .addTaggedData(SMART_START, event.getSmartStart())
                .addTaggedData(SMART_END, event.getSmartEnd())
                .addTaggedData(EVENT_START, event.getStart())
                .addTaggedData(EVENT_END, event.getEnd())
                .addTaggedData(SESSION_ID, event.getSessionId());
        mMetricsLogger.write(log);
        debugLog(log);
    }

    private static int getLogType(SelectionEvent event) {
        switch (event.getEventType()) {
            case SelectionEvent.ACTION_OVERTYPE:
                return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
            case SelectionEvent.ACTION_COPY:
                return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
            case SelectionEvent.ACTION_PASTE:
                return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
            case SelectionEvent.ACTION_CUT:
                return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
            case SelectionEvent.ACTION_SHARE:
                return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
            case SelectionEvent.ACTION_SMART_SHARE:
                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
            case SelectionEvent.ACTION_DRAG:
                return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
            case SelectionEvent.ACTION_ABANDON:
                return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
            case SelectionEvent.ACTION_OTHER:
                return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
            case SelectionEvent.ACTION_SELECT_ALL:
                return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
            case SelectionEvent.ACTION_RESET:
                return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
            case SelectionEvent.EVENT_SELECTION_STARTED:
                return MetricsEvent.ACTION_TEXT_SELECTION_START;
            case SelectionEvent.EVENT_SELECTION_MODIFIED:
                return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
            case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:
                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
            case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
            case SelectionEvent.EVENT_AUTO_SELECTION:
                return MetricsEvent.ACTION_TEXT_SELECTION_AUTO;
            default:
                return MetricsEvent.VIEW_UNKNOWN;
        }
    }

    private static String getLogTypeString(int logType) {
        switch (logType) {
            case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
                return "OVERTYPE";
            case MetricsEvent.ACTION_TEXT_SELECTION_COPY:
                return "COPY";
            case MetricsEvent.ACTION_TEXT_SELECTION_PASTE:
                return "PASTE";
            case MetricsEvent.ACTION_TEXT_SELECTION_CUT:
                return "CUT";
            case MetricsEvent.ACTION_TEXT_SELECTION_SHARE:
                return "SHARE";
            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
                return "SMART_SHARE";
            case MetricsEvent.ACTION_TEXT_SELECTION_DRAG:
                return "DRAG";
            case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON:
                return "ABANDON";
            case MetricsEvent.ACTION_TEXT_SELECTION_OTHER:
                return "OTHER";
            case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL:
                return "SELECT_ALL";
            case MetricsEvent.ACTION_TEXT_SELECTION_RESET:
                return "RESET";
            case MetricsEvent.ACTION_TEXT_SELECTION_START:
                return "SELECTION_STARTED";
            case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY:
                return "SELECTION_MODIFIED";
            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE:
                return "SMART_SELECTION_SINGLE";
            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI:
                return "SMART_SELECTION_MULTI";
            case MetricsEvent.ACTION_TEXT_SELECTION_AUTO:
                return "AUTO_SELECTION";
            default:
                return UNKNOWN;
        }
    }

    private static void debugLog(LogMaker log) {
        if (!DEBUG_LOG_ENABLED) return;

        final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
        final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
        final String widget = widgetVersion.isEmpty()
                ? widgetType : widgetType + "-" + widgetVersion;
        final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
        if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
            String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
            sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
            Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
        }

        final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
        final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
        final String type = getLogTypeString(log.getType());
        final int smartStart = Integer.parseInt(
                Objects.toString(log.getTaggedData(SMART_START), ZERO));
        final int smartEnd = Integer.parseInt(
                Objects.toString(log.getTaggedData(SMART_END), ZERO));
        final int eventStart = Integer.parseInt(
                Objects.toString(log.getTaggedData(EVENT_START), ZERO));
        final int eventEnd = Integer.parseInt(
                Objects.toString(log.getTaggedData(EVENT_END), ZERO));

        Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
                index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model));
    }

    /**
     * Creates a signature string that may be used to tag TextClassifier results.
     */
    public static String createSignature(
            String text, int start, int end, Context context, int modelVersion,
            @Nullable Locale locale) {
        Preconditions.checkNotNull(text);
        Preconditions.checkNotNull(context);
        final String modelName = (locale != null)
                ? String.format(Locale.US, "%s_v%d", locale.toLanguageTag(), modelVersion)
                : "";
        final int hash = Objects.hash(text, start, end, context.getPackageName());
        return SignatureParser.createSignature(CLASSIFIER_ID, modelName, hash);
    }

    /**
     * Helper for creating and parsing signature strings for
     * {@link android.view.textclassifier.TextClassifierImpl}.
     */
    @VisibleForTesting
    public static final class SignatureParser {

        static String createSignature(String classifierId, String modelName, int hash) {
            return String.format(Locale.US, "%s|%s|%d", classifierId, modelName, hash);
        }

        static String getClassifierId(String signature) {
            Preconditions.checkNotNull(signature);
            final int end = signature.indexOf("|");
            if (end >= 0) {
                return signature.substring(0, end);
            }
            return "";
        }

        static String getModelName(String signature) {
            Preconditions.checkNotNull(signature);
            final int start = signature.indexOf("|");
            final int end = signature.indexOf("|", start);
            if (start >= 0 && end >= start) {
                return signature.substring(start, end);
            }
            return "";
        }

        static int getHash(String signature) {
            Preconditions.checkNotNull(signature);
            final int index1 = signature.indexOf("|");
            final int index2 = signature.indexOf("|", index1);
            if (index2 > 0) {
                return Integer.parseInt(signature.substring(index2));
            }
            return 0;
        }
    }
}
+429 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading