Loading java/src/com/android/inputmethod/latin/ResearchLogger.java +58 −8 Original line number Diff line number Diff line Loading @@ -197,6 +197,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Log.d(TAG, "stop called"); if (mLoggingHandler != null && mLoggingState == LOGGING_STATE_ON) { mLoggingState = LOGGING_STATE_STOPPING; flushEventQueue(true); // put this in the Handler queue so pending writes are processed first. mLoggingHandler.post(new Runnable() { @Override Loading Loading @@ -379,11 +380,52 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mCurrentLogUnit.addLogAtom(keys, values, false); } // Used to track how often words are logged. Too-frequent logging can leak // semantics, disclosing private data. /* package for test */ static class LoggingFrequencyState { private static final int DEFAULT_WORD_LOG_FREQUENCY = 10; private int mWordsRemainingToSkip; private final int mFrequency; /** * Tracks how often words may be uploaded. * * @param frequency 1=Every word, 2=Every other word, etc. */ public LoggingFrequencyState(int frequency) { mFrequency = frequency; mWordsRemainingToSkip = mFrequency; } public void onWordLogged() { mWordsRemainingToSkip = mFrequency; } public void onWordNotLogged() { if (mWordsRemainingToSkip > 1) { mWordsRemainingToSkip--; } } public boolean isSafeToLog() { return mWordsRemainingToSkip <= 1; } } /* package for test */ LoggingFrequencyState mLoggingFrequencyState = new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY); /* package for test */ boolean isPrivacyThreat(String word) { // currently: word not in dictionary or contains numbers. // Current checks: // - Word not in dictionary // - Word contains numbers // - Privacy-safe word not logged recently if (TextUtils.isEmpty(word)) { return false; } if (!mLoggingFrequencyState.isSafeToLog()) { return true; } final int length = word.length(); boolean hasLetter = false; for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { Loading @@ -410,15 +452,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return false; } private void onWordComplete(String word) { final boolean isPrivacyThreat = isPrivacyThreat(word); flushEventQueue(isPrivacyThreat); if (isPrivacyThreat) { mLoggingFrequencyState.onWordNotLogged(); } else { mLoggingFrequencyState.onWordLogged(); } } /** * Write out enqueued LogEvents to the log, possibly dropping privacy sensitive events. */ /* package for test */ synchronized void flushQueue(boolean removePotentiallyPrivateEvents) { /* package for test */ synchronized void flushEventQueue( boolean removePotentiallyPrivateEvents) { if (isAllowedToLog()) { mCurrentLogUnit.setRemovePotentiallyPrivateEvents(removePotentiallyPrivateEvents); mLoggingHandler.post(mCurrentLogUnit); mCurrentLogUnit = new LogUnit(); } mCurrentLogUnit = new LogUnit(); } private synchronized void outputEvent(final String[] keys, final Object[] values) { Loading Loading @@ -652,7 +705,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent( EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); researchLogger.flushQueue(researchLogger.isPrivacyThreat(autoCorrection)); } private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { Loading @@ -665,7 +717,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang }; final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); researchLogger.flushQueue(researchLogger.isPrivacyThreat(scrubbedWord)); researchLogger.onWordComplete(scrubbedWord); } private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { Loading Loading @@ -743,7 +795,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } final ResearchLogger researchLogger = getInstance(); researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values); researchLogger.flushQueue(true); // Play it safe. Remove privacy-sensitive events. researchLogger.flushEventQueue(true); // Play it safe. Remove privacy-sensitive events. } } Loading Loading @@ -824,7 +876,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent( EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); researchLogger.flushQueue(researchLogger.isPrivacyThreat(cs.toString())); } private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { Loading @@ -839,7 +890,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY, values); researchLogger.flushQueue(researchLogger.isPrivacyThreat(suggestion.toString())); } private static final String[] EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION = { Loading Loading
java/src/com/android/inputmethod/latin/ResearchLogger.java +58 −8 Original line number Diff line number Diff line Loading @@ -197,6 +197,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Log.d(TAG, "stop called"); if (mLoggingHandler != null && mLoggingState == LOGGING_STATE_ON) { mLoggingState = LOGGING_STATE_STOPPING; flushEventQueue(true); // put this in the Handler queue so pending writes are processed first. mLoggingHandler.post(new Runnable() { @Override Loading Loading @@ -379,11 +380,52 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mCurrentLogUnit.addLogAtom(keys, values, false); } // Used to track how often words are logged. Too-frequent logging can leak // semantics, disclosing private data. /* package for test */ static class LoggingFrequencyState { private static final int DEFAULT_WORD_LOG_FREQUENCY = 10; private int mWordsRemainingToSkip; private final int mFrequency; /** * Tracks how often words may be uploaded. * * @param frequency 1=Every word, 2=Every other word, etc. */ public LoggingFrequencyState(int frequency) { mFrequency = frequency; mWordsRemainingToSkip = mFrequency; } public void onWordLogged() { mWordsRemainingToSkip = mFrequency; } public void onWordNotLogged() { if (mWordsRemainingToSkip > 1) { mWordsRemainingToSkip--; } } public boolean isSafeToLog() { return mWordsRemainingToSkip <= 1; } } /* package for test */ LoggingFrequencyState mLoggingFrequencyState = new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY); /* package for test */ boolean isPrivacyThreat(String word) { // currently: word not in dictionary or contains numbers. // Current checks: // - Word not in dictionary // - Word contains numbers // - Privacy-safe word not logged recently if (TextUtils.isEmpty(word)) { return false; } if (!mLoggingFrequencyState.isSafeToLog()) { return true; } final int length = word.length(); boolean hasLetter = false; for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { Loading @@ -410,15 +452,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return false; } private void onWordComplete(String word) { final boolean isPrivacyThreat = isPrivacyThreat(word); flushEventQueue(isPrivacyThreat); if (isPrivacyThreat) { mLoggingFrequencyState.onWordNotLogged(); } else { mLoggingFrequencyState.onWordLogged(); } } /** * Write out enqueued LogEvents to the log, possibly dropping privacy sensitive events. */ /* package for test */ synchronized void flushQueue(boolean removePotentiallyPrivateEvents) { /* package for test */ synchronized void flushEventQueue( boolean removePotentiallyPrivateEvents) { if (isAllowedToLog()) { mCurrentLogUnit.setRemovePotentiallyPrivateEvents(removePotentiallyPrivateEvents); mLoggingHandler.post(mCurrentLogUnit); mCurrentLogUnit = new LogUnit(); } mCurrentLogUnit = new LogUnit(); } private synchronized void outputEvent(final String[] keys, final Object[] values) { Loading Loading @@ -652,7 +705,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent( EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); researchLogger.flushQueue(researchLogger.isPrivacyThreat(autoCorrection)); } private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { Loading @@ -665,7 +717,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang }; final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); researchLogger.flushQueue(researchLogger.isPrivacyThreat(scrubbedWord)); researchLogger.onWordComplete(scrubbedWord); } private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { Loading Loading @@ -743,7 +795,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } final ResearchLogger researchLogger = getInstance(); researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values); researchLogger.flushQueue(true); // Play it safe. Remove privacy-sensitive events. researchLogger.flushEventQueue(true); // Play it safe. Remove privacy-sensitive events. } } Loading Loading @@ -824,7 +876,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent( EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); researchLogger.flushQueue(researchLogger.isPrivacyThreat(cs.toString())); } private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { Loading @@ -839,7 +890,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY, values); researchLogger.flushQueue(researchLogger.isPrivacyThreat(suggestion.toString())); } private static final String[] EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION = { Loading