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

Commit 019389a6 authored by Jan Althaus's avatar Jan Althaus
Browse files

New metrics fields for selection logs

Changes that affect logging:
- Switched to new category/actions/fields that are used exclusively by
  selection logging.
- Action types are now logged in the tron type (the information that was
  previously in the field could be derived from other fields).
- Entity types are now logged in the tron sub type.
- Delta time from previous event is now zero for first event (was time
  since epoch).
- The smart select model version is now written for all events after
  smart select has run. (i.e. better support for analysis based on
  terminal events alone)
- Restoring the logging removed in ag/2901629.

Internal only changes:
- Made debug statement conditional on DEBUG_LOG_ENABLED.
- Reduced logging tag length to fit into 23char limit.

Bug: 64914512
Test: Manually tested that logs are correct. Will follow up with automated tests.
Merged-In: If12daa2f206fb42be53587decbe2c4abfd689f41
Change-Id: Iec0f6cf1f9f2efdc8518133cf8551cd96b095ad8
parent 7bbe0623
Loading
Loading
Loading
Loading
+134 −104
Original line number Diff line number Diff line
@@ -42,17 +42,16 @@ import java.util.UUID;
//TODO: Do not allow any crashes from this class.
public final class SmartSelectionEventTracker {

    private static final String LOG_TAG = "SmartSelectionEventTracker";
    private static final String LOG_TAG = "SmartSelectEventTracker";
    private static final boolean DEBUG_LOG_ENABLED = true;

    private static final int START_EVENT_DELTA = MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS;
    private static final int PREV_EVENT_DELTA = MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS;
    private static final int ENTITY_TYPE = MetricsEvent.NOTIFICATION_TAG;
    private static final int INDEX = MetricsEvent.NOTIFICATION_SHADE_INDEX;
    private static final int TAG = MetricsEvent.FIELD_CLASS_NAME;
    private static final int SMART_INDICES = MetricsEvent.FIELD_GESTURE_LENGTH;
    private static final int EVENT_INDICES = MetricsEvent.FIELD_CONTEXT;
    private static final int SESSION_ID = MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN;
    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 VERSION_TAG = MetricsEvent.FIELD_SELECTION_VERSION_TAG;
    private static final int SMART_INDICES = MetricsEvent.FIELD_SELECTION_SMART_RANGE;
    private static final int EVENT_INDICES = MetricsEvent.FIELD_SELECTION_RANGE;
    private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;

    private static final String ZERO = "0";
    private static final String TEXTVIEW = "textview";
@@ -84,6 +83,7 @@ public final class SmartSelectionEventTracker {
    private long mSessionStartTime;
    private long mLastEventTime;
    private boolean mSmartSelectionTriggered;
    private String mVersionTag;

    public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) {
        mWidgetType = widgetType;
@@ -98,7 +98,8 @@ public final class SmartSelectionEventTracker {
    public void logEvent(@NonNull SelectionEvent event) {
        Preconditions.checkNotNull(event);

        if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null) {
        if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null
                && DEBUG_LOG_ENABLED) {
            Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
            return;
        }
@@ -114,6 +115,7 @@ public final class SmartSelectionEventTracker {
            case SelectionEvent.EventType.SMART_SELECTION_SINGLE:  // fall through
            case SelectionEvent.EventType.SMART_SELECTION_MULTI:
                mSmartSelectionTriggered = true;
                mVersionTag = getVersionTag(event);
                mSmartIndices[0] = event.mStart;
                mSmartIndices[1] = event.mEnd;
                break;
@@ -132,16 +134,16 @@ public final class SmartSelectionEventTracker {
    }

    private void writeEvent(SelectionEvent event, long now) {
        final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST)
        final long prevEventDelta = mLastEventTime == 0 ? 0 : now - mLastEventTime;
        final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
                .setType(getLogType(event))
                .setSubtype(event.mEventType)
                .setSubtype(getLogSubType(event))
                .setPackageName(mContext.getPackageName())
                .setTimestamp(now)
                .addTaggedData(START_EVENT_DELTA, now - mSessionStartTime)
                .addTaggedData(PREV_EVENT_DELTA, now - mLastEventTime)
                .addTaggedData(ENTITY_TYPE, event.mEntityType)
                .addTaggedData(PREV_EVENT_DELTA, prevEventDelta)
                .addTaggedData(INDEX, mIndex)
                .addTaggedData(TAG, getTag(event))
                .addTaggedData(VERSION_TAG, mVersionTag)
                .addTaggedData(SMART_INDICES, getSmartDelta())
                .addTaggedData(EVENT_INDICES, getEventDelta(event))
                .addTaggedData(SESSION_ID, mSessionId);
@@ -168,42 +170,120 @@ public final class SmartSelectionEventTracker {
        mSessionStartTime = 0;
        mLastEventTime = 0;
        mSmartSelectionTriggered = false;
        mVersionTag = getVersionTag(null);
        mSessionId = null;
    }

    private int getLogType(SelectionEvent event) {
    private static int getLogType(SelectionEvent event) {
        switch (event.mEventType) {
            case SelectionEvent.EventType.SELECTION_STARTED:  // fall through
            case SelectionEvent.EventType.SMART_SELECTION_SINGLE:  // fall through
            case SelectionEvent.EventType.SMART_SELECTION_MULTI:  // fall through
            case SelectionEvent.EventType.AUTO_SELECTION:
                return MetricsEvent.TYPE_OPEN;
            case SelectionEvent.ActionType.OVERTYPE:
                return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
            case SelectionEvent.ActionType.COPY:
                return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
            case SelectionEvent.ActionType.PASTE:
                return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
            case SelectionEvent.ActionType.CUT:
                return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
            case SelectionEvent.ActionType.SHARE:
                return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
            case SelectionEvent.ActionType.SMART_SHARE:
                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
            case SelectionEvent.ActionType.DRAG:
                return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
            case SelectionEvent.ActionType.ABANDON:
                return MetricsEvent.TYPE_CLOSE;
        }
        if (event.isActionType()) {
            if (event.isTerminal() && mSmartSelectionTriggered) {
                if (matchesSmartSelectionBounds(event)) {
                    // Smart selection accepted.
                    return MetricsEvent.TYPE_SUCCESS;
                } else if (containsOriginalSelection(event)) {
                    // Smart selection rejected.
                    return MetricsEvent.TYPE_FAILURE;
                }
                // User changed the original selection entirely.
            }
            return MetricsEvent.TYPE_ACTION;
        } else {
            return MetricsEvent.TYPE_UPDATE;
        }
    }

    private boolean matchesSmartSelectionBounds(SelectionEvent event) {
        return event.mStart == mSmartIndices[0] && event.mEnd == mSmartIndices[1];
                return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
            case SelectionEvent.ActionType.OTHER:
                return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
            case SelectionEvent.ActionType.SELECT_ALL:
                return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
            case SelectionEvent.ActionType.RESET:
                return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
            case SelectionEvent.EventType.SELECTION_STARTED:
                return MetricsEvent.ACTION_TEXT_SELECTION_START;
            case SelectionEvent.EventType.SELECTION_MODIFIED:
                return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
            case SelectionEvent.EventType.SMART_SELECTION_SINGLE:
                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
            case SelectionEvent.EventType.SMART_SELECTION_MULTI:
                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
            case SelectionEvent.EventType.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 int getLogSubType(SelectionEvent event) {
        switch (event.mEntityType) {
            case TextClassifier.TYPE_OTHER:
                return MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER;
            case TextClassifier.TYPE_EMAIL:
                return MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL;
            case TextClassifier.TYPE_PHONE:
                return MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE;
            case TextClassifier.TYPE_ADDRESS:
                return MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS;
            case TextClassifier.TYPE_URL:
                return MetricsEvent.TEXT_CLASSIFIER_TYPE_URL;
            default:
                return MetricsEvent.TEXT_CLASSIFIER_TYPE_UNKNOWN;
        }
    }

    private static String getLogSubTypeString(int logSubType) {
        switch (logSubType) {
            case MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER:
                return TextClassifier.TYPE_OTHER;
            case MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL:
                return TextClassifier.TYPE_EMAIL;
            case MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE:
                return TextClassifier.TYPE_PHONE;
            case MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS:
                return TextClassifier.TYPE_ADDRESS;
            case MetricsEvent.TEXT_CLASSIFIER_TYPE_URL:
                return TextClassifier.TYPE_URL;
            default:
                return TextClassifier.TYPE_UNKNOWN;
        }

    private boolean containsOriginalSelection(SelectionEvent event) {
        return event.mStart <= mOrigStart && event.mEnd > mOrigStart;
    }

    private int getSmartDelta() {
@@ -221,7 +301,7 @@ public final class SmartSelectionEventTracker {
                | (clamp(event.mEnd - mOrigStart) & 0xffff);
    }

    private String getTag(SelectionEvent event) {
    private String getVersionTag(@Nullable SelectionEvent event) {
        final String widgetType;
        switch (mWidgetType) {
            case WidgetType.TEXTVIEW:
@@ -239,7 +319,9 @@ public final class SmartSelectionEventTracker {
            default:
                widgetType = UNKNOWN;
        }
        final String version = Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG);
        final String version = event == null
                ? SelectionEvent.NO_VERSION_TAG
                : Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG);
        return String.format("%s/%s", widgetType, version);
    }

@@ -254,66 +336,17 @@ public final class SmartSelectionEventTracker {
    private static void debugLog(LogMaker log) {
        if (!DEBUG_LOG_ENABLED) return;

        final String tag = Objects.toString(log.getTaggedData(TAG), "tag");
        final String tag = Objects.toString(log.getTaggedData(VERSION_TAG), "tag");
        final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));

        final String event;
        switch (log.getSubtype()) {
            case SelectionEvent.ActionType.OVERTYPE:
                event = "OVERTYPE";
                break;
            case SelectionEvent.ActionType.COPY:
                event = "COPY";
                break;
            case SelectionEvent.ActionType.PASTE:
                event = "PASTE";
                break;
            case SelectionEvent.ActionType.CUT:
                event = "CUT";
                break;
            case SelectionEvent.ActionType.SHARE:
                event = "SHARE";
                break;
            case SelectionEvent.ActionType.SMART_SHARE:
                event = "SMART_SHARE";
                break;
            case SelectionEvent.ActionType.DRAG:
                event = "DRAG";
                break;
            case SelectionEvent.ActionType.ABANDON:
                event = "ABANDON";
                break;
            case SelectionEvent.ActionType.OTHER:
                event = "OTHER";
                break;
            case SelectionEvent.ActionType.SELECT_ALL:
                event = "SELECT_ALL";
                break;
            case SelectionEvent.ActionType.RESET:
                event = "RESET";
                break;
            case SelectionEvent.EventType.SELECTION_STARTED:
        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)", tag, sessionId));
                event = "SELECTION_STARTED";
                break;
            case SelectionEvent.EventType.SELECTION_MODIFIED:
                event = "SELECTION_MODIFIED";
                break;
            case SelectionEvent.EventType.SMART_SELECTION_SINGLE:
                event = "SMART_SELECTION_SINGLE";
                break;
            case SelectionEvent.EventType.SMART_SELECTION_MULTI:
                event = "SMART_SELECTION_MULTI";
                break;
            case SelectionEvent.EventType.AUTO_SELECTION:
                event = "AUTO_SELECTION";
                break;
            default:
                event = "UNKNOWN";
        }

        final String type = getLogTypeString(log.getType());
        final String subType = getLogSubTypeString(log.getSubtype());

        final int smartIndices = Integer.parseInt(
                Objects.toString(log.getTaggedData(SMART_INDICES), ZERO));
        final int smartStart = (short) ((smartIndices & 0xffff0000) >> 16);
@@ -324,11 +357,8 @@ public final class SmartSelectionEventTracker {
        final int eventStart = (short) ((eventIndices & 0xffff0000) >> 16);
        final int eventEnd = (short) (eventIndices & 0xffff);

        final String entity = Objects.toString(
                log.getTaggedData(ENTITY_TYPE), TextClassifier.TYPE_UNKNOWN);

        Log.d(LOG_TAG, String.format("%2d: %s, context=%d,%d - old=%d,%d [%s] (%s)",
                index, event, eventStart, eventEnd, smartStart, smartEnd, entity, tag));
        Log.d(LOG_TAG, String.format("%2d: %s/%s, context=%d,%d - old=%d,%d (%s)",
                index, type, subType, eventStart, eventEnd, smartStart, smartEnd, tag));
    }

    /**
+8 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
@@ -3921,6 +3922,10 @@ public class Editor {
                        textClassification.getLabel())
                        .setIcon(textClassification.getIcon())
                        .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
                mMetricsLogger.write(
                        new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST)
                                .setType(MetricsEvent.TYPE_OPEN)
                                .setSubtype(textClassification.getLogType()));
            }
        }

@@ -3962,6 +3967,9 @@ public class Editor {
                                .onClick(mTextView);
                    }
                }
                mMetricsLogger.action(
                        MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST,
                        textClassification.getLogType());
                stopTextActionMode();
                return true;
            }
+131 −0
Original line number Diff line number Diff line
@@ -161,6 +161,16 @@ message MetricsEvent {
    CAMERA_EXTERNAL_USED = 2;
  }

  // TextClassifier entity types.
  enum TextClassifierEntityType {
    TEXT_CLASSIFIER_TYPE_UNKNOWN = 1;
    TEXT_CLASSIFIER_TYPE_OTHER = 2;
    TEXT_CLASSIFIER_TYPE_EMAIL = 3;
    TEXT_CLASSIFIER_TYPE_PHONE = 4;
    TEXT_CLASSIFIER_TYPE_ADDRESS = 5;
    TEXT_CLASSIFIER_TYPE_URL = 6;
  }

  // Known visual elements: views or controls.
  enum View {
    // Unknown view
@@ -4320,6 +4330,127 @@ message MetricsEvent {
    // FIELD: A string value representing some state of the platform, e.g., boot reason
    FIELD_PLATFORM_REASON = 1099;

    // CATEGORY: The category for all actions relating to selection session logging.
    // OS: O MR
    TEXT_SELECTION_SESSION = 1100;

    // ACTION: A selection session started (i.e. the selection handles appeared)
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_START = 1101;

    // ACTION: The user modified the selection (e.g. by dragging the handles)
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_MODIFY = 1102;

    // ACTION: The user modified the selection by pressing the "select all" button.
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_SELECT_ALL = 1103;

    // ACTION: The user modified the selection by pressing on a word in a multi word selection
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_RESET = 1104;

    // ACTION: Smart selection made a single word prediction
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_SMART_SINGLE = 1105;

    // ACTION: Smart selection made a multi word prediction
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_SMART_MULTI = 1106;

    // ACTION: The app made an automatic selection on the user's behalf (not smart selection)
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_AUTO = 1107;

    // ACTION: A selection session ended with the user typing over the text
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_OVERTYPE = 1108;

    // ACTION: A selection session ended with the user copying the text
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_COPY = 1109;

    // ACTION: A selection session ended with the user pasting over the text
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_PASTE = 1110;

    // ACTION: A selection session ended with the user cutting the text
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_CUT = 1111;

    // ACTION: A selection session ended with the user pressing the share button
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_SHARE = 1112;

    // ACTION: A selection session ended with the user pressing the smart share button
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_SMART_SHARE = 1113;

    // ACTION: A selection session ended with the user dragging the text
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_DRAG = 1114;

    // ACTION: A selection session ended with the user abandoning the selection
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_ABANDON = 1115;

    // ACTION: A selection session ended with the user picking an unhandled action bar item
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    ACTION_TEXT_SELECTION_OTHER = 1116;

    // FIELD: Time in milliseconds from the start of the session to this event
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    FIELD_SELECTION_SINCE_START = 1117;

    // FIELD: time in milliseconds between the last event in the session and this one
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    FIELD_SELECTION_SINCE_PREVIOUS = 1118;

    // FIELD: a random uid for a selection session (lasting from select start to end)
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    FIELD_SELECTION_SESSION_ID = 1119;

    // FIELD: the sequence number of the event in the session
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    FIELD_SELECTION_SESSION_INDEX = 1120;

    // FIELD: a concatenation of the widget type and ML model version.
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    FIELD_SELECTION_VERSION_TAG = 1121;

    // FIELD: text select start offset in words (as defined by the ICU BreakIterator), stored as two
    //        packed 16bit integers. (start in MSBs, end in LSBs)
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    FIELD_SELECTION_RANGE = 1122;

    // FIELD: smart text selection start offset in words (as defined by the ICU BreakIterator),
    //        stored as two packed 16bit integers. (start in MSBs, end in LSBs)
    // CATEGORY: TEXT_SELECTION_SESSION
    // OS: O MR
    FIELD_SELECTION_SMART_RANGE = 1123;

    // ---- End O-MR1 Constants, all O-MR1 constants go above this line ----

    // Add new aosp constants above this line.