am c5da4365: Merge "[Rlog2] ResearchLogging fix multi-space logging"

* commit 'c5da4365fbe6ff23a8db381ee7de6fa43fd7086b':
  [Rlog2] ResearchLogging fix multi-space logging
main
Kurt Partridge 2012-12-17 16:36:03 -08:00 committed by Android Git Automerger
commit 81bbef3dac
5 changed files with 315 additions and 395 deletions

View File

@ -386,9 +386,6 @@ public final class RichInputConnection {
// TextView flash the text for a second based on indices contained in the argument. // TextView flash the text for a second based on indices contained in the argument.
if (null != mIC) { if (null != mIC) {
mIC.commitCorrection(correctionInfo); mIC.commitCorrection(correctionInfo);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.richInputConnection_commitCorrection(correctionInfo);
}
} }
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
} }

View File

@ -16,8 +16,9 @@
package com.android.inputmethod.research; package com.android.inputmethod.research;
import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.research.ResearchLogger.LogStatement;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -35,48 +36,54 @@ import java.util.List;
* been published recently, or whether the LogUnit contains numbers, etc. * been published recently, or whether the LogUnit contains numbers, etc.
*/ */
/* package */ class LogUnit { /* package */ class LogUnit {
private final List<String[]> mKeysList; private final ArrayList<LogStatement> mLogStatementList;
private final List<Object[]> mValuesList; private final ArrayList<Object[]> mValuesList;
// Assume that mTimeList is sorted in increasing order. Do not insert null values into // Assume that mTimeList is sorted in increasing order. Do not insert null values into
// mTimeList. // mTimeList.
private final List<Long> mTimeList; private final ArrayList<Long> mTimeList;
private final List<Boolean> mIsPotentiallyPrivate;
private String mWord; private String mWord;
private boolean mMayContainDigit; private boolean mMayContainDigit;
private boolean mIsPartOfMegaword;
public LogUnit() { public LogUnit() {
mKeysList = CollectionUtils.newArrayList(); mLogStatementList = new ArrayList<LogStatement>();
mValuesList = CollectionUtils.newArrayList(); mValuesList = new ArrayList<Object[]>();
mTimeList = CollectionUtils.newArrayList(); mTimeList = new ArrayList<Long>();
mIsPotentiallyPrivate = CollectionUtils.newArrayList(); mIsPartOfMegaword = false;
} }
private LogUnit(final List<String[]> keysList, final List<Object[]> valuesList, private LogUnit(final ArrayList<LogStatement> logStatementList,
final List<Long> timeList, final List<Boolean> isPotentiallyPrivate) { final ArrayList<Object[]> valuesList,
mKeysList = keysList; final ArrayList<Long> timeList,
final boolean isPartOfMegaword) {
mLogStatementList = logStatementList;
mValuesList = valuesList; mValuesList = valuesList;
mTimeList = timeList; mTimeList = timeList;
mIsPotentiallyPrivate = isPotentiallyPrivate; mIsPartOfMegaword = isPartOfMegaword;
} }
/** /**
* Adds a new log statement. The time parameter in successive calls to this method must be * Adds a new log statement. The time parameter in successive calls to this method must be
* monotonically increasing, or splitByTime() will not work. * monotonically increasing, or splitByTime() will not work.
*/ */
public void addLogStatement(final String[] keys, final Object[] values, public void addLogStatement(final LogStatement logStatement, final Object[] values,
final long time, final boolean isPotentiallyPrivate) { final long time) {
mKeysList.add(keys); mLogStatementList.add(logStatement);
mValuesList.add(values); mValuesList.add(values);
mTimeList.add(time); mTimeList.add(time);
mIsPotentiallyPrivate.add(isPotentiallyPrivate);
} }
public void publishTo(final ResearchLog researchLog, final boolean isIncludingPrivateData) { public void publishTo(final ResearchLog researchLog, final boolean isIncludingPrivateData) {
final int size = mKeysList.size(); final int size = mLogStatementList.size();
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
if (!mIsPotentiallyPrivate.get(i) || isIncludingPrivateData) { final LogStatement logStatement = mLogStatementList.get(i);
researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i), mTimeList.get(i)); if (!isIncludingPrivateData && logStatement.mIsPotentiallyPrivate) {
continue;
} }
if (mIsPartOfMegaword && logStatement.mIsPotentiallyRevealing) {
continue;
}
researchLog.outputEvent(mLogStatementList.get(i), mValuesList.get(i), mTimeList.get(i));
} }
} }
@ -101,7 +108,7 @@ import java.util.List;
} }
public boolean isEmpty() { public boolean isEmpty() {
return mKeysList.isEmpty(); return mLogStatementList.isEmpty();
} }
/** /**
@ -112,14 +119,27 @@ import java.util.List;
// Assume that mTimeList is in sorted order. // Assume that mTimeList is in sorted order.
final int length = mTimeList.size(); final int length = mTimeList.size();
for (int index = 0; index < length; index++) { for (int index = 0; index < length; index++) {
if (mTimeList.get(index) >= maxTime) { if (mTimeList.get(index) > maxTime) {
final List<LogStatement> laterLogStatements =
mLogStatementList.subList(index, length);
final List<Object[]> laterValues = mValuesList.subList(index, length);
final List<Long> laterTimes = mTimeList.subList(index, length);
// Create the LogUnit containing the later logStatements and associated data.
final LogUnit newLogUnit = new LogUnit( final LogUnit newLogUnit = new LogUnit(
mKeysList.subList(index, length), new ArrayList<LogStatement>(laterLogStatements),
mValuesList.subList(index, length), new ArrayList<Object[]>(laterValues),
mTimeList.subList(index, length), new ArrayList<Long>(laterTimes),
mIsPotentiallyPrivate.subList(index, length)); true /* isPartOfMegaword */);
newLogUnit.mWord = null; newLogUnit.mWord = null;
newLogUnit.mMayContainDigit = mMayContainDigit; newLogUnit.mMayContainDigit = mMayContainDigit;
// Purge the logStatements and associated data from this LogUnit.
laterLogStatements.clear();
laterValues.clear();
laterTimes.clear();
mIsPartOfMegaword = true;
return newLogUnit; return newLogUnit;
} }
} }

View File

@ -118,7 +118,7 @@ public class MainLogBuffer extends LogBuffer {
} }
} else { } else {
// Words not in the dictionary are a privacy threat. // Words not in the dictionary are a privacy threat.
if (!(dictionary.isValidWord(word))) { if (ResearchLogger.hasLetters(word) && !(dictionary.isValidWord(word))) {
return false; return false;
} }
} }

View File

@ -26,6 +26,7 @@ import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.research.ResearchLogger.LogStatement;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
@ -207,14 +208,11 @@ public class ResearchLog {
private static final String UPTIME_KEY = "_ut"; private static final String UPTIME_KEY = "_ut";
private static final String EVENT_TYPE_KEY = "_ty"; private static final String EVENT_TYPE_KEY = "_ty";
void outputEvent(final String[] keys, final Object[] values, final long time) { void outputEvent(final LogStatement logStatement, final Object[] values, final long time) {
// Not thread safe. // Not thread safe.
if (keys.length == 0) {
return;
}
if (DEBUG) { if (DEBUG) {
if (keys.length != values.length + 1) { if (logStatement.mKeys.length != values.length) {
Log.d(TAG, "Key and Value list sizes do not match. " + keys[0]); Log.d(TAG, "Key and Value list sizes do not match. " + logStatement.mName);
} }
} }
try { try {
@ -226,10 +224,11 @@ public class ResearchLog {
mJsonWriter.beginObject(); mJsonWriter.beginObject();
mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis()); mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
mJsonWriter.name(UPTIME_KEY).value(time); mJsonWriter.name(UPTIME_KEY).value(time);
mJsonWriter.name(EVENT_TYPE_KEY).value(keys[0]); mJsonWriter.name(EVENT_TYPE_KEY).value(logStatement.mName);
final String[] keys = logStatement.mKeys;
final int length = values.length; final int length = values.length;
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
mJsonWriter.name(keys[i + 1]); mJsonWriter.name(keys[i]);
Object value = values[i]; Object value = values[i];
if (value instanceof CharSequence) { if (value instanceof CharSequence) {
mJsonWriter.value(value.toString()); mJsonWriter.value(value.toString());

View File

@ -47,7 +47,6 @@ import android.view.MotionEvent;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnection;
import android.widget.Toast; import android.widget.Toast;
@ -88,7 +87,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info
public static final boolean DEFAULT_USABILITY_STUDY_MODE = false; public static final boolean DEFAULT_USABILITY_STUDY_MODE = false;
/* package */ static boolean sIsLogging = false; /* package */ static boolean sIsLogging = false;
private static final int OUTPUT_FORMAT_VERSION = 1; private static final int OUTPUT_FORMAT_VERSION = 5;
private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
private static final String PREF_RESEARCH_HAS_SEEN_SPLASH = "pref_research_has_seen_splash"; private static final String PREF_RESEARCH_HAS_SEEN_SPLASH = "pref_research_has_seen_splash";
/* package */ static final String FILENAME_PREFIX = "researchLog"; /* package */ static final String FILENAME_PREFIX = "researchLog";
@ -377,7 +376,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
Log.d(TAG, "stop called"); Log.d(TAG, "stop called");
} }
logStatistics(); logStatistics();
commitCurrentLogUnit(SystemClock.uptimeMillis()); commitCurrentLogUnit();
if (mMainLogBuffer != null) { if (mMainLogBuffer != null) {
publishLogBuffer(mMainLogBuffer, mMainResearchLog, false /* isIncludingPrivateData */); publishLogBuffer(mMainLogBuffer, mMainResearchLog, false /* isIncludingPrivateData */);
@ -522,15 +521,46 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
} }
*/ */
private static final String[] EVENTKEYS_FEEDBACK = { static class LogStatement {
"UserTimestamp", "contents" final String mName;
};
// mIsPotentiallyPrivate indicates that event contains potentially private information. If
// the word that this event is a part of is determined to be privacy-sensitive, then this
// event should not be included in the output log. The system waits to output until the
// containing word is known.
final boolean mIsPotentiallyPrivate;
// mIsPotentiallyRevealing indicates that this statement may disclose details about other
// words typed in other LogUnits. This can happen if the user is not inserting spaces, and
// data from Suggestions and/or Composing text reveals the entire "megaword". For example,
// say the user is typing "for the win", and the system wants to record the bigram "the
// win". If the user types "forthe", omitting the space, the system will give "for the" as
// a suggestion. If the user accepts the autocorrection, the suggestion for "for the" is
// included in the log for the word "the", disclosing that the previous word had been "for".
// For now, we simply do not include this data when logging part of a "megaword".
final boolean mIsPotentiallyRevealing;
// mKeys stores the names that are the attributes in the output json objects
final String[] mKeys;
private static final String[] NULL_KEYS = new String[0];
LogStatement(final String name, final boolean isPotentiallyPrivate,
final boolean isPotentiallyRevealing, final String... keys) {
mName = name;
mIsPotentiallyPrivate = isPotentiallyPrivate;
mIsPotentiallyRevealing = isPotentiallyRevealing;
mKeys = (keys == null) ? NULL_KEYS : keys;
}
}
private static final LogStatement LOGSTATEMENT_FEEDBACK =
new LogStatement("UserTimestamp", false, false, "contents");
public void sendFeedback(final String feedbackContents, final boolean includeHistory) { public void sendFeedback(final String feedbackContents, final boolean includeHistory) {
if (mFeedbackLogBuffer == null) { if (mFeedbackLogBuffer == null) {
return; return;
} }
if (includeHistory) { if (includeHistory) {
commitCurrentLogUnit(SystemClock.uptimeMillis()); commitCurrentLogUnit();
} else { } else {
mFeedbackLogBuffer.clear(); mFeedbackLogBuffer.clear();
} }
@ -538,8 +568,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
final Object[] values = { final Object[] values = {
feedbackContents feedbackContents
}; };
feedbackLogUnit.addLogStatement(EVENTKEYS_FEEDBACK, values, feedbackLogUnit.addLogStatement(LOGSTATEMENT_FEEDBACK, values, SystemClock.uptimeMillis());
SystemClock.uptimeMillis(), false /* isPotentiallyPrivate */);
mFeedbackLogBuffer.shiftIn(feedbackLogUnit); mFeedbackLogBuffer.shiftIn(feedbackLogUnit);
publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */); publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */);
mFeedbackLog.close(new Runnable() { mFeedbackLog.close(new Runnable() {
@ -624,25 +653,19 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
} }
} }
private static final Object[] EVENTKEYS_NULLVALUES = {}; private static final Object[] NULL_VALUES = {};
/** /**
* Buffer a research log event, flagging it as privacy-sensitive. * Buffer a research log event, flagging it as privacy-sensitive.
*
* This event contains potentially private information. If the word that this event is a part
* of is determined to be privacy-sensitive, then this event should not be included in the
* output log. The system waits to output until the containing word is known.
*
* @param keys an array containing a descriptive name for the event, followed by the keys
* @param values an array of values, either a String or Number. length should be one
* less than the keys array
*/ */
private synchronized void enqueuePotentiallyPrivateEvent(final String[] keys, private synchronized void enqueueEvent(LogStatement logStatement, Object... values) {
final Object[] values) { if (values == null) {
assert values.length + 1 == keys.length; values = NULL_VALUES;
}
assert values.length == logStatement.mKeys.length;
if (isAllowedToLog()) { if (isAllowedToLog()) {
final long time = SystemClock.uptimeMillis(); final long time = SystemClock.uptimeMillis();
mCurrentLogUnit.addLogStatement(keys, values, time, true /* isPotentiallyPrivate */); mCurrentLogUnit.addLogStatement(logStatement, values, time);
} }
} }
@ -650,33 +673,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mCurrentLogUnit.setMayContainDigit(); mCurrentLogUnit.setMayContainDigit();
} }
/** /* package for test */ void commitCurrentLogUnit() {
* Buffer a research log event, flaggint it as not privacy-sensitive.
*
* This event contains no potentially private information. Even if the word that this event
* is privacy-sensitive, this event can still safely be sent to the output log. The system
* waits until the containing word is known so that this event can be written in the proper
* temporal order with other events that may be privacy sensitive.
*
* @param keys an array containing a descriptive name for the event, followed by the keys
* @param values an array of values, either a String or Number. length should be one
* less than the keys array
*/
private synchronized void enqueueEvent(final String[] keys, final Object[] values) {
assert values.length + 1 == keys.length;
if (isAllowedToLog()) {
final long time = SystemClock.uptimeMillis();
mCurrentLogUnit.addLogStatement(keys, values, time, false /* isPotentiallyPrivate */);
}
}
/* package for test */ void commitCurrentLogUnit(final long maxTime) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "commitCurrentLogUnit" + (mCurrentLogUnit.hasWord() ? Log.d(TAG, "commitCurrentLogUnit" + (mCurrentLogUnit.hasWord() ?
": " + mCurrentLogUnit.getWord() : "")); ": " + mCurrentLogUnit.getWord() : ""));
} }
if (!mCurrentLogUnit.isEmpty()) { if (!mCurrentLogUnit.isEmpty()) {
final LogUnit newLogUnit = mCurrentLogUnit.splitByTime(maxTime);
if (mMainLogBuffer != null) { if (mMainLogBuffer != null) {
mMainLogBuffer.shiftIn(mCurrentLogUnit); mMainLogBuffer.shiftIn(mCurrentLogUnit);
if (mMainLogBuffer.isSafeToLog() && mMainResearchLog != null) { if (mMainLogBuffer.isSafeToLog() && mMainResearchLog != null) {
@ -688,37 +690,38 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (mFeedbackLogBuffer != null) { if (mFeedbackLogBuffer != null) {
mFeedbackLogBuffer.shiftIn(mCurrentLogUnit); mFeedbackLogBuffer.shiftIn(mCurrentLogUnit);
} }
mCurrentLogUnit = newLogUnit; mCurrentLogUnit = new LogUnit();
Log.d(TAG, "commitCurrentLogUnit");
} }
} }
private static final String[] EVENTKEYS_LOG_SEGMENT_START = { private static final LogStatement LOGSTATEMENT_LOG_SEGMENT_OPENING =
"logSegmentStart", "isIncludingPrivateData" new LogStatement("logSegmentStart", false, false, "isIncludingPrivateData");
}; private static final LogStatement LOGSTATEMENT_LOG_SEGMENT_CLOSING =
private static final String[] EVENTKEYS_LOG_SEGMENT_END = { new LogStatement("logSegmentEnd", false, false);
"logSegmentEnd"
};
/* package for test */ void publishLogBuffer(final LogBuffer logBuffer, /* package for test */ void publishLogBuffer(final LogBuffer logBuffer,
final ResearchLog researchLog, final boolean isIncludingPrivateData) { final ResearchLog researchLog, final boolean isIncludingPrivateData) {
final LogUnit openingLogUnit = new LogUnit(); final LogUnit openingLogUnit = new LogUnit();
final Object[] values = { final Object[] values = {
isIncludingPrivateData isIncludingPrivateData
}; };
openingLogUnit.addLogStatement(EVENTKEYS_LOG_SEGMENT_START, values, openingLogUnit.addLogStatement(LOGSTATEMENT_LOG_SEGMENT_OPENING, values,
SystemClock.uptimeMillis(), false /* isPotentiallyPrivate */); SystemClock.uptimeMillis());
researchLog.publish(openingLogUnit, true /* isIncludingPrivateData */); researchLog.publish(openingLogUnit, true /* isIncludingPrivateData */);
LogUnit logUnit; LogUnit logUnit;
while ((logUnit = logBuffer.shiftOut()) != null) { while ((logUnit = logBuffer.shiftOut()) != null) {
if (DEBUG) {
Log.d(TAG, "publishLogBuffer: " + (logUnit.hasWord() ? logUnit.getWord()
: "<wordless>"));
}
researchLog.publish(logUnit, isIncludingPrivateData); researchLog.publish(logUnit, isIncludingPrivateData);
} }
final LogUnit closingLogUnit = new LogUnit(); final LogUnit closingLogUnit = new LogUnit();
closingLogUnit.addLogStatement(EVENTKEYS_LOG_SEGMENT_END, EVENTKEYS_NULLVALUES, closingLogUnit.addLogStatement(LOGSTATEMENT_LOG_SEGMENT_CLOSING, NULL_VALUES,
SystemClock.uptimeMillis(), false /* isPotentiallyPrivate */); SystemClock.uptimeMillis());
researchLog.publish(closingLogUnit, true /* isIncludingPrivateData */); researchLog.publish(closingLogUnit, true /* isIncludingPrivateData */);
} }
private boolean hasLetters(final String word) { public static boolean hasLetters(final String word) {
final int length = word.length(); final int length = word.length();
for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
final int codePoint = word.codePointAt(i); final int codePoint = word.codePointAt(i);
@ -729,13 +732,22 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return false; return false;
} }
private void onWordComplete(final String word, final long maxTime) { private static final LogStatement LOGSTATEMENT_COMMIT_AUTOSPACE =
new LogStatement("CommitAutospace", true, false);
private void onWordComplete(final String word, final long maxTime, final boolean isPartial) {
Log.d(TAG, "onWordComplete: " + word); Log.d(TAG, "onWordComplete: " + word);
if (word != null && word.length() > 0 && hasLetters(word)) { if (word != null && word.length() > 0 && hasLetters(word)) {
mCurrentLogUnit.setWord(word); mCurrentLogUnit.setWord(word);
mStatistics.recordWordEntered(); mStatistics.recordWordEntered();
} }
commitCurrentLogUnit(maxTime); final LogUnit newLogUnit = mCurrentLogUnit.splitByTime(maxTime);
enqueueCommitText(word);
if (isPartial) {
enqueueEvent(LOGSTATEMENT_COMMIT_AUTOSPACE, NULL_VALUES);
enqueueCommitText(" ");
}
commitCurrentLogUnit();
mCurrentLogUnit = newLogUnit;
} }
private static int scrubDigitFromCodePoint(int codePoint) { private static int scrubDigitFromCodePoint(int codePoint) {
@ -788,10 +800,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return WORD_REPLACEMENT_STRING; return WORD_REPLACEMENT_STRING;
} }
private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = { private static final LogStatement LOGSTATEMENT_LATIN_IME_ON_START_INPUT_VIEW_INTERNAL =
"LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions", new LogStatement("LatinImeOnStartInputViewInternal", false, false, "uuid",
"fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion" "packageName", "inputType", "imeOptions", "fieldId", "display", "model",
}; "prefs", "versionCode", "versionName", "outputFormatVersion");
public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
final SharedPreferences prefs) { final SharedPreferences prefs) {
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
@ -807,14 +819,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
0); 0);
final Integer versionCode = packageInfo.versionCode; final Integer versionCode = packageInfo.versionCode;
final String versionName = packageInfo.versionName; final String versionName = packageInfo.versionName;
final Object[] values = { researchLogger.enqueueEvent(LOGSTATEMENT_LATIN_IME_ON_START_INPUT_VIEW_INTERNAL,
researchLogger.mUUIDString, editorInfo.packageName, researchLogger.mUUIDString, editorInfo.packageName,
Integer.toHexString(editorInfo.inputType), Integer.toHexString(editorInfo.inputType),
Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId, Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId,
Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName, Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName,
OUTPUT_FORMAT_VERSION OUTPUT_FORMAT_VERSION);
};
researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values);
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -825,23 +835,18 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
stop(); stop();
} }
private static final String[] EVENTKEYS_PREFS_CHANGED = { private static final LogStatement LOGSTATEMENT_PREFS_CHANGED =
"PrefsChanged", "prefs" new LogStatement("PrefsChanged", false, false, "prefs");
};
public static void prefsChanged(final SharedPreferences prefs) { public static void prefsChanged(final SharedPreferences prefs) {
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
final Object[] values = { researchLogger.enqueueEvent(LOGSTATEMENT_PREFS_CHANGED, prefs);
prefs
};
researchLogger.enqueueEvent(EVENTKEYS_PREFS_CHANGED, values);
} }
// Regular logging methods // Regular logging methods
private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = { private static final LogStatement LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT =
"MainKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size", new LogStatement("MainKeyboardViewProcessMotionEvent", true, false, "action",
"pressure" "eventTime", "id", "x", "y", "size", "pressure");
};
public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action, public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action,
final long eventTime, final int index, final int id, final int x, final int y) { final long eventTime, final int index, final int id, final int x, final int y) {
if (me != null) { if (me != null) {
@ -858,40 +863,32 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
} }
final float size = me.getSize(index); final float size = me.getSize(index);
final float pressure = me.getPressure(index); final float pressure = me.getPressure(index);
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT,
actionString, eventTime, id, x, y, size, pressure actionString, eventTime, id, x, y, size, pressure);
};
getInstance().enqueuePotentiallyPrivateEvent(
EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT, values);
} }
} }
private static final String[] EVENTKEYS_LATINIME_ONCODEINPUT = { private static final LogStatement LOGSTATEMENT_LATIN_IME_ON_CODE_INPUT =
"LatinIMEOnCodeInput", "code", "x", "y" new LogStatement("LatinImeOnCodeInput", true, false, "code", "x", "y");
};
public static void latinIME_onCodeInput(final int code, final int x, final int y) { public static void latinIME_onCodeInput(final int code, final int x, final int y) {
final long time = SystemClock.uptimeMillis(); final long time = SystemClock.uptimeMillis();
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
final Object[] values = { researchLogger.enqueueEvent(LOGSTATEMENT_LATIN_IME_ON_CODE_INPUT,
Constants.printableCode(scrubDigitFromCodePoint(code)), x, y Constants.printableCode(scrubDigitFromCodePoint(code)), x, y);
};
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values);
if (Character.isDigit(code)) { if (Character.isDigit(code)) {
researchLogger.setCurrentLogUnitContainsDigitFlag(); researchLogger.setCurrentLogUnitContainsDigitFlag();
} }
researchLogger.mStatistics.recordChar(code, time); researchLogger.mStatistics.recordChar(code, time);
} }
private static final LogStatement LOGSTATEMENT_LATINIME_ONDISPLAYCOMPLETIONS =
private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { new LogStatement("LatinIMEOnDisplayCompletions", true, true,
"LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions" "applicationSpecifiedCompletions");
};
public static void latinIME_onDisplayCompletions( public static void latinIME_onDisplayCompletions(
final CompletionInfo[] applicationSpecifiedCompletions) { final CompletionInfo[] applicationSpecifiedCompletions) {
final Object[] values = { // Note; passing an array as a single element in a vararg list. Must create a new
applicationSpecifiedCompletions // dummy array around it or it will get expanded.
}; getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_ONDISPLAYCOMPLETIONS,
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS, new Object[] { applicationSpecifiedCompletions });
values);
} }
public static boolean getAndClearLatinIMEExpectingUpdateSelection() { public static boolean getAndClearLatinIMEExpectingUpdateSelection() {
@ -900,13 +897,13 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return returnValue; return returnValue;
} }
private static final String[] EVENTKEYS_LATINIME_ONWINDOWHIDDEN = { private static final LogStatement LOGSTATEMENT_LATINIME_ONWINDOWHIDDEN =
"LatinIMEOnWindowHidden", "isTextTruncated", "text" new LogStatement("LatinIMEOnWindowHidden", false, false, "isTextTruncated", "text");
};
public static void latinIME_onWindowHidden(final int savedSelectionStart, public static void latinIME_onWindowHidden(final int savedSelectionStart,
final int savedSelectionEnd, final InputConnection ic) { final int savedSelectionEnd, final InputConnection ic) {
if (ic != null) { if (ic != null) {
final Object[] values = new Object[2]; final boolean isTextTruncated;
final String text;
if (OUTPUT_ENTIRE_BUFFER) { if (OUTPUT_ENTIRE_BUFFER) {
// Capture the TextView contents. This will trigger onUpdateSelection(), so we // Capture the TextView contents. This will trigger onUpdateSelection(), so we
// set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called, // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called,
@ -921,8 +918,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
ic.endBatchEdit(); ic.endBatchEdit();
sLatinIMEExpectingUpdateSelection = true; sLatinIMEExpectingUpdateSelection = true;
if (TextUtils.isEmpty(charSequence)) { if (TextUtils.isEmpty(charSequence)) {
values[0] = false; isTextTruncated = false;
values[1] = ""; text = "";
} else { } else {
if (charSequence.length() > MAX_INPUTVIEW_LENGTH_TO_CAPTURE) { if (charSequence.length() > MAX_INPUTVIEW_LENGTH_TO_CAPTURE) {
int length = MAX_INPUTVIEW_LENGTH_TO_CAPTURE; int length = MAX_INPUTVIEW_LENGTH_TO_CAPTURE;
@ -933,29 +930,33 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
} }
final CharSequence truncatedCharSequence = charSequence.subSequence(0, final CharSequence truncatedCharSequence = charSequence.subSequence(0,
length); length);
values[0] = true; isTextTruncated = true;
values[1] = truncatedCharSequence.toString(); text = truncatedCharSequence.toString();
} else { } else {
values[0] = false; isTextTruncated = false;
values[1] = charSequence.toString(); text = charSequence.toString();
} }
} }
} else { } else {
values[0] = true; isTextTruncated = true;
values[1] = ""; text = "";
} }
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values); // Assume that OUTPUT_ENTIRE_BUFFER is only true when we don't care about privacy (e.g.
researchLogger.commitCurrentLogUnit(SystemClock.uptimeMillis()); // during a live user test), so the normal isPotentiallyPrivate and
// isPotentiallyRevealing flags do not apply
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONWINDOWHIDDEN, isTextTruncated,
text);
researchLogger.commitCurrentLogUnit();
getInstance().stop(); getInstance().stop();
} }
} }
private static final String[] EVENTKEYS_LATINIME_ONUPDATESELECTION = { private static final LogStatement LOGSTATEMENT_LATINIME_ONUPDATESELECTION =
"LatinIMEOnUpdateSelection", "lastSelectionStart", "lastSelectionEnd", "oldSelStart", new LogStatement("LatinIMEOnUpdateSelection", true, false, "lastSelectionStart",
"oldSelEnd", "newSelStart", "newSelEnd", "composingSpanStart", "composingSpanEnd", "lastSelectionEnd", "oldSelStart", "oldSelEnd", "newSelStart", "newSelEnd",
"expectingUpdateSelection", "expectingUpdateSelectionFromLogger", "context" "composingSpanStart", "composingSpanEnd", "expectingUpdateSelection",
}; "expectingUpdateSelectionFromLogger", "context");
public static void latinIME_onUpdateSelection(final int lastSelectionStart, public static void latinIME_onUpdateSelection(final int lastSelectionStart,
final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd, final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd,
final int newSelStart, final int newSelEnd, final int composingSpanStart, final int newSelStart, final int newSelEnd, final int composingSpanStart,
@ -971,350 +972,253 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
} }
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
final String scrubbedWord = researchLogger.scrubWord(word); final String scrubbedWord = researchLogger.scrubWord(word);
final Object[] values = { researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONUPDATESELECTION, lastSelectionStart,
lastSelectionStart, lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, newSelEnd,
newSelEnd, composingSpanStart, composingSpanEnd, expectingUpdateSelection, composingSpanStart, composingSpanEnd, expectingUpdateSelection,
expectingUpdateSelectionFromLogger, scrubbedWord expectingUpdateSelectionFromLogger, scrubbedWord);
};
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values);
} }
private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { private static final LogStatement LOGSTATEMENT_LATINIME_PICKSUGGESTIONMANUALLY =
"LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" new LogStatement("LatinIMEPickSuggestionManually", true, false, "replacedWord", "index",
}; "suggestion", "x", "y");
public static void latinIME_pickSuggestionManually(final String replacedWord, public static void latinIME_pickSuggestionManually(final String replacedWord,
final int index, CharSequence suggestion) { final int index, CharSequence suggestion) {
final Object[] values = {
scrubDigitsFromString(replacedWord), index,
(suggestion == null ? null : scrubDigitsFromString(suggestion.toString())),
Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE
};
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY, researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_PICKSUGGESTIONMANUALLY,
values); scrubDigitsFromString(replacedWord), index,
suggestion == null ? null : scrubDigitsFromString(suggestion.toString()),
Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
} }
private static final String[] EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION = { private static final LogStatement LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION =
"LatinIMEPunctuationSuggestion", "index", "suggestion", "x", "y" new LogStatement("LatinIMEPunctuationSuggestion", false, false, "index", "suggestion",
}; "x", "y");
public static void latinIME_punctuationSuggestion(final int index, public static void latinIME_punctuationSuggestion(final int index,
final CharSequence suggestion) { final CharSequence suggestion) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION, index, suggestion,
index, suggestion, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE
};
getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values);
} }
private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = { private static final LogStatement LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT =
"LatinIMESendKeyCodePoint", "code" new LogStatement("LatinIMESendKeyCodePoint", true, false, "code");
};
public static void latinIME_sendKeyCodePoint(final int code) { public static void latinIME_sendKeyCodePoint(final int code) {
final Object[] values = {
Constants.printableCode(scrubDigitFromCodePoint(code))
};
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT,
Constants.printableCode(scrubDigitFromCodePoint(code)));
if (Character.isDigit(code)) { if (Character.isDigit(code)) {
researchLogger.setCurrentLogUnitContainsDigitFlag(); researchLogger.setCurrentLogUnitContainsDigitFlag();
} }
} }
private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { private static final LogStatement LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE =
"LatinIMESwapSwapperAndSpace" new LogStatement("LatinIMESwapSwapperAndSpace", false, false);
};
public static void latinIME_swapSwapperAndSpace() { public static void latinIME_swapSwapperAndSpace() {
getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE, EVENTKEYS_NULLVALUES); getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE);
} }
private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = { private static final LogStatement LOGSTATEMENT_MAINKEYBOARDVIEW_ONLONGPRESS =
"MainKeyboardViewOnLongPress" new LogStatement("MainKeyboardViewOnLongPress", false, false);
};
public static void mainKeyboardView_onLongPress() { public static void mainKeyboardView_onLongPress() {
getInstance().enqueueEvent(EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS, EVENTKEYS_NULLVALUES); getInstance().enqueueEvent(LOGSTATEMENT_MAINKEYBOARDVIEW_ONLONGPRESS);
} }
private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_SETKEYBOARD = { private static final LogStatement LOGSTATEMENT_MAINKEYBOARDVIEW_SETKEYBOARD =
"MainKeyboardViewSetKeyboard", "elementId", "locale", "orientation", "width", new LogStatement("MainKeyboardViewSetKeyboard", false, false, "elementId", "locale",
"modeName", "action", "navigateNext", "navigatePrevious", "clobberSettingsKey", "orientation", "width", "modeName", "action", "navigateNext",
"passwordInput", "shortcutKeyEnabled", "hasShortcutKey", "languageSwitchKeyEnabled", "navigatePrevious", "clobberSettingsKey", "passwordInput", "shortcutKeyEnabled",
"isMultiLine", "tw", "th", "keys" "hasShortcutKey", "languageSwitchKeyEnabled", "isMultiLine", "tw", "th",
}; "keys");
public static void mainKeyboardView_setKeyboard(final Keyboard keyboard) { public static void mainKeyboardView_setKeyboard(final Keyboard keyboard) {
if (keyboard != null) { final KeyboardId kid = keyboard.mId;
final KeyboardId kid = keyboard.mId; final boolean isPasswordView = kid.passwordInput();
final boolean isPasswordView = kid.passwordInput(); getInstance().setIsPasswordView(isPasswordView);
getInstance().setIsPasswordView(isPasswordView); getInstance().enqueueEvent(LOGSTATEMENT_MAINKEYBOARDVIEW_SETKEYBOARD,
final Object[] values = {
KeyboardId.elementIdToName(kid.mElementId), KeyboardId.elementIdToName(kid.mElementId),
kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
kid.mOrientation, kid.mOrientation, kid.mWidth, KeyboardId.modeName(kid.mMode), kid.imeAction(),
kid.mWidth, kid.navigateNext(), kid.navigatePrevious(), kid.mClobberSettingsKey,
KeyboardId.modeName(kid.mMode), isPasswordView, kid.mShortcutKeyEnabled, kid.mHasShortcutKey,
kid.imeAction(), kid.mLanguageSwitchKeyEnabled, kid.isMultiLine(), keyboard.mOccupiedWidth,
kid.navigateNext(), keyboard.mOccupiedHeight, keyboard.mKeys);
kid.navigatePrevious(),
kid.mClobberSettingsKey,
isPasswordView,
kid.mShortcutKeyEnabled,
kid.mHasShortcutKey,
kid.mLanguageSwitchKeyEnabled,
kid.isMultiLine(),
keyboard.mOccupiedWidth,
keyboard.mOccupiedHeight,
keyboard.mKeys
};
getInstance().enqueueEvent(EVENTKEYS_MAINKEYBOARDVIEW_SETKEYBOARD, values);
}
} }
private static final String[] EVENTKEYS_LATINIME_REVERTCOMMIT = { private static final LogStatement LOGSTATEMENT_LATINIME_REVERTCOMMIT =
"LatinIMERevertCommit", "originallyTypedWord" new LogStatement("LatinIMERevertCommit", true, false, "originallyTypedWord");
};
public static void latinIME_revertCommit(final String originallyTypedWord) { public static void latinIME_revertCommit(final String originallyTypedWord) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_REVERTCOMMIT, originallyTypedWord);
originallyTypedWord
};
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_REVERTCOMMIT, values);
} }
private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT = { private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCANCELINPUT =
"PointerTrackerCallListenerOnCancelInput" new LogStatement("PointerTrackerCallListenerOnCancelInput", false, false);
};
public static void pointerTracker_callListenerOnCancelInput() { public static void pointerTracker_callListenerOnCancelInput() {
getInstance().enqueueEvent(EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT, getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCANCELINPUT);
EVENTKEYS_NULLVALUES);
} }
private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT = { private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCODEINPUT =
"PointerTrackerCallListenerOnCodeInput", "code", "outputText", "x", "y", new LogStatement("PointerTrackerCallListenerOnCodeInput", true, false, "code",
"ignoreModifierKey", "altersCode", "isEnabled" "outputText", "x", "y", "ignoreModifierKey", "altersCode", "isEnabled");
};
public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x, public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x,
final int y, final boolean ignoreModifierKey, final boolean altersCode, final int y, final boolean ignoreModifierKey, final boolean altersCode,
final int code) { final int code) {
if (key != null) { if (key != null) {
String outputText = key.getOutputText(); String outputText = key.getOutputText();
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCODEINPUT,
Constants.printableCode(scrubDigitFromCodePoint(code)), outputText == null ? null Constants.printableCode(scrubDigitFromCodePoint(code)),
: scrubDigitsFromString(outputText.toString()), outputText == null ? null : scrubDigitsFromString(outputText.toString()),
x, y, ignoreModifierKey, altersCode, key.isEnabled() x, y, ignoreModifierKey, altersCode, key.isEnabled());
};
getInstance().enqueuePotentiallyPrivateEvent(
EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT, values);
} }
} }
private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE = { private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONRELEASE =
"PointerTrackerCallListenerOnRelease", "code", "withSliding", "ignoreModifierKey", new LogStatement("PointerTrackerCallListenerOnRelease", true, false, "code",
"isEnabled" "withSliding", "ignoreModifierKey", "isEnabled");
};
public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode, public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode,
final boolean withSliding, final boolean ignoreModifierKey) { final boolean withSliding, final boolean ignoreModifierKey) {
if (key != null) { if (key != null) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONRELEASE,
Constants.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding, Constants.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding,
ignoreModifierKey, key.isEnabled() ignoreModifierKey, key.isEnabled());
};
getInstance().enqueuePotentiallyPrivateEvent(
EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE, values);
} }
} }
private static final String[] EVENTKEYS_POINTERTRACKER_ONDOWNEVENT = { private static final LogStatement LOGSTATEMENT_POINTERTRACKER_ONDOWNEVENT =
"PointerTrackerOnDownEvent", "deltaT", "distanceSquared" new LogStatement("PointerTrackerOnDownEvent", true, false, "deltaT", "distanceSquared");
};
public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) { public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_ONDOWNEVENT, deltaT,
deltaT, distanceSquared distanceSquared);
};
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONDOWNEVENT, values);
} }
private static final String[] EVENTKEYS_POINTERTRACKER_ONMOVEEVENT = { private static final LogStatement LOGSTATEMENT_POINTERTRACKER_ONMOVEEVENT =
"PointerTrackerOnMoveEvent", "x", "y", "lastX", "lastY" new LogStatement("PointerTrackerOnMoveEvent", true, false, "x", "y", "lastX", "lastY");
};
public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX, public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX,
final int lastY) { final int lastY) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_ONMOVEEVENT, x, y, lastX, lastY);
x, y, lastX, lastY
};
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values);
} }
private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION = { private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_COMMITCOMPLETION =
"RichInputConnectionCommitCompletion", "completionInfo" new LogStatement("RichInputConnectionCommitCompletion", true, false, "completionInfo");
};
public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) { public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) {
final Object[] values = {
completionInfo
};
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent( researchLogger.enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_COMMITCOMPLETION,
EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); completionInfo);
} }
// Disabled for privacy-protection reasons. Because this event comes after private boolean isExpectingCommitText = false;
// richInputConnection_commitText, which is the event used to separate LogUnits, the public static void latinIME_commitPartialText(final CharSequence committedWord,
// data in this event can be associated with the next LogUnit, revealing information final long lastTimestampOfWordData) {
// about the current word even if it was supposed to be suppressed. The occurrance of
// autocorrection can be determined by examining the difference between the text strings in
// the last call to richInputConnection_setComposingText before
// richInputConnection_commitText, so it's not a data loss.
// TODO: Figure out how to log this event without loss of privacy.
/*
private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = {
"RichInputConnectionCommitCorrection", "typedWord", "autoCorrection"
};
*/
public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) {
/*
final String typedWord = correctionInfo.getOldText().toString();
final String autoCorrection = correctionInfo.getNewText().toString();
final Object[] values = {
scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection)
};
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent( final String scrubbedWord = scrubDigitsFromString(committedWord.toString());
EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); researchLogger.onWordComplete(scrubbedWord, lastTimestampOfWordData, true /* isPartial */);
*/ researchLogger.isExpectingCommitText = true;
} }
private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { private static final LogStatement LOGSTATEMENT_COMMITTEXT_UPDATECURSOR =
"RichInputConnectionCommitText", "typedWord", "newCursorPosition" new LogStatement("CommitTextUpdateCursor", true, false, "newCursorPosition");
}; public static void richInputConnection_commitText(final CharSequence committedWord,
public static void richInputConnection_commitText(final CharSequence typedWord,
final int newCursorPosition) { final int newCursorPosition) {
final String scrubbedWord = scrubDigitsFromString(typedWord.toString());
final Object[] values = {
scrubbedWord, newCursorPosition
};
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT, final String scrubbedWord = scrubDigitsFromString(committedWord.toString());
values); if (!researchLogger.isExpectingCommitText) {
// TODO: Replace Long.MAX_VALUE with timestamp of last data to include researchLogger.onWordComplete(scrubbedWord, Long.MAX_VALUE, false /* isPartial */);
researchLogger.onWordComplete(scrubbedWord, Long.MAX_VALUE); researchLogger.enqueueEvent(LOGSTATEMENT_COMMITTEXT_UPDATECURSOR, newCursorPosition);
}
researchLogger.isExpectingCommitText = false;
} }
private static final String[] EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = { private static final LogStatement LOGSTATEMENT_COMMITTEXT =
"RichInputConnectionDeleteSurroundingText", "beforeLength", "afterLength" new LogStatement("CommitText", true, false, "committedText");
}; private void enqueueCommitText(final CharSequence word) {
enqueueEvent(LOGSTATEMENT_COMMITTEXT, word);
}
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT =
new LogStatement("RichInputConnectionDeleteSurroundingText", true, false,
"beforeLength", "afterLength");
public static void richInputConnection_deleteSurroundingText(final int beforeLength, public static void richInputConnection_deleteSurroundingText(final int beforeLength,
final int afterLength) { final int afterLength) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT,
beforeLength, afterLength beforeLength, afterLength);
};
getInstance().enqueuePotentiallyPrivateEvent(
EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values);
} }
private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT =
"RichInputConnectionFinishComposingText" new LogStatement("RichInputConnectionFinishComposingText", false, false);
};
public static void richInputConnection_finishComposingText() { public static void richInputConnection_finishComposingText() {
getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT, getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT);
EVENTKEYS_NULLVALUES);
} }
private static final String[] EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION = { private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_PERFORMEDITORACTION =
"RichInputConnectionPerformEditorAction", "imeActionNext" new LogStatement("RichInputConnectionPerformEditorAction", false, false,
}; "imeActionNext");
public static void richInputConnection_performEditorAction(final int imeActionNext) { public static void richInputConnection_performEditorAction(final int imeActionNext) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_PERFORMEDITORACTION,
imeActionNext imeActionNext);
};
getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION, values);
} }
private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT = { private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SENDKEYEVENT =
"RichInputConnectionSendKeyEvent", "eventTime", "action", "code" new LogStatement("RichInputConnectionSendKeyEvent", true, false, "eventTime", "action",
}; "code");
public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) { public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_SENDKEYEVENT,
keyEvent.getEventTime(), keyEvent.getEventTime(), keyEvent.getAction(), keyEvent.getKeyCode());
keyEvent.getAction(),
keyEvent.getKeyCode()
};
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT,
values);
} }
private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SETCOMPOSINGTEXT =
"RichInputConnectionSetComposingText", "text", "newCursorPosition" new LogStatement("RichInputConnectionSetComposingText", true, true, "text",
}; "newCursorPosition");
public static void richInputConnection_setComposingText(final CharSequence text, public static void richInputConnection_setComposingText(final CharSequence text,
final int newCursorPosition) { final int newCursorPosition) {
if (text == null) { if (text == null) {
throw new RuntimeException("setComposingText is null"); throw new RuntimeException("setComposingText is null");
} }
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, text,
text, newCursorPosition newCursorPosition);
};
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT,
values);
} }
private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SETSELECTION =
"RichInputConnectionSetSelection", "from", "to" new LogStatement("RichInputConnectionSetSelection", true, false, "from", "to");
};
public static void richInputConnection_setSelection(final int from, final int to) { public static void richInputConnection_setSelection(final int from, final int to) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_SETSELECTION, from, to);
from, to
};
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION,
values);
} }
private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { private static final LogStatement LOGSTATEMENT_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT =
"SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent" new LogStatement("SuddenJumpingTouchEventHandlerOnTouchEvent", true, false,
}; "motionEvent");
public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) { public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) {
if (me != null) { if (me != null) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT,
me.toString() me.toString());
};
getInstance().enqueuePotentiallyPrivateEvent(
EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT, values);
} }
} }
private static final String[] EVENTKEYS_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS = { private static final LogStatement LOGSTATEMENT_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS =
"SuggestionStripViewSetSuggestions", "suggestedWords" new LogStatement("SuggestionStripViewSetSuggestions", true, true, "suggestedWords");
};
public static void suggestionStripView_setSuggestions(final SuggestedWords suggestedWords) { public static void suggestionStripView_setSuggestions(final SuggestedWords suggestedWords) {
if (suggestedWords != null) { if (suggestedWords != null) {
final Object[] values = { getInstance().enqueueEvent(LOGSTATEMENT_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS,
suggestedWords suggestedWords);
};
getInstance().enqueuePotentiallyPrivateEvent(
EVENTKEYS_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS, values);
} }
} }
private static final String[] EVENTKEYS_USER_TIMESTAMP = { private static final LogStatement LOGSTATEMENT_USER_TIMESTAMP =
"UserTimestamp" new LogStatement("UserTimestamp", false, false);
};
public void userTimestamp() { public void userTimestamp() {
getInstance().enqueueEvent(EVENTKEYS_USER_TIMESTAMP, EVENTKEYS_NULLVALUES); getInstance().enqueueEvent(LOGSTATEMENT_USER_TIMESTAMP);
} }
private static final String[] EVENTKEYS_STATISTICS = { private static final LogStatement LOGSTATEMENT_STATISTICS =
"Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", new LogStatement("Statistics", false, false, "charCount", "letterCount", "numberCount",
"wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", "spaceCount", "deleteOpsCount", "wordCount", "isEmptyUponStarting",
"averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" "isEmptinessStateKnown", "averageTimeBetweenKeys", "averageTimeBeforeDelete",
}; "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete");
private static void logStatistics() { private static void logStatistics() {
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
final Statistics statistics = researchLogger.mStatistics; final Statistics statistics = researchLogger.mStatistics;
final Object[] values = { researchLogger.enqueueEvent(LOGSTATEMENT_STATISTICS, statistics.mCharCount,
statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, statistics.mLetterCount, statistics.mNumberCount, statistics.mSpaceCount,
statistics.mSpaceCount, statistics.mDeleteKeyCount, statistics.mDeleteKeyCount, statistics.mWordCount, statistics.mIsEmptyUponStarting,
statistics.mWordCount, statistics.mIsEmptyUponStarting, statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(),
statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), statistics.mBeforeDeleteKeyCounter.getAverageTime(),
statistics.mBeforeDeleteKeyCounter.getAverageTime(), statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(),
statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), statistics.mAfterDeleteKeyCounter.getAverageTime());
statistics.mAfterDeleteKeyCounter.getAverageTime()
};
researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values);
} }
} }