[Rlog27] Refactor LogBuffer

Cleanup and prepare for replaying

Change-Id: Ie09e912c6e9c0d7375168c575ccf1cfd9375dd31
main
Kurt Partridge 2012-12-23 10:40:34 -08:00
parent 13b1be988d
commit f77dd424b0
5 changed files with 163 additions and 89 deletions

View File

@ -2094,12 +2094,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
Stats.onAutoCorrection( Stats.onAutoCorrection(
typedWord, autoCorrection.toString(), separatorString, mWordComposer); typedWord, autoCorrection.toString(), separatorString, mWordComposer);
} }
mExpectingUpdateSelection = true;
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection,
autoCorrection.toString(), separatorString); separatorString);
} }
mExpectingUpdateSelection = true;
commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
separatorString); separatorString);
if (!typedWord.equals(autoCorrection)) { if (!typedWord.equals(autoCorrection)) {

View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2012 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 com.android.inputmethod.research;
import java.util.LinkedList;
/**
* A buffer that holds a fixed number of LogUnits.
*
* LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are
* actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches
* capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to
* stay under the capacity limit.
*
* This variant of a LogBuffer has a limited memory footprint because of its limited size. This
* makes it useful, for example, for recording a window of the user's most recent actions in case
* they want to report an observed error that they do not know how to reproduce.
*/
public class FixedLogBuffer extends LogBuffer {
/* package for test */ int mWordCapacity;
// The number of members of mLogUnits that are actual words.
private int mNumActualWords;
/**
* Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and
* unlimited number of non-word LogUnits), and that outputs its result to a researchLog.
*
* @param wordCapacity maximum number of words
*/
public FixedLogBuffer(final int wordCapacity) {
super();
if (wordCapacity <= 0) {
throw new IllegalArgumentException("wordCapacity must be 1 or greater.");
}
mWordCapacity = wordCapacity;
mNumActualWords = 0;
}
protected int getNumActualWords() {
return mNumActualWords;
}
/**
* Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's
* (oldest first) if word capacity is reached.
*/
@Override
public void shiftIn(final LogUnit newLogUnit) {
if (newLogUnit.getWord() == null) {
// This LogUnit isn't a word, so it doesn't count toward the word-limit.
super.shiftIn(newLogUnit);
return;
}
if (mNumActualWords == mWordCapacity) {
shiftOutThroughFirstWord();
}
super.shiftIn(newLogUnit);
mNumActualWords++; // Must be a word, or we wouldn't be here.
}
private void shiftOutThroughFirstWord() {
final LinkedList<LogUnit> logUnits = getLogUnits();
while (!logUnits.isEmpty()) {
final LogUnit logUnit = logUnits.removeFirst();
onShiftOut(logUnit);
if (logUnit.hasWord()) {
// Successfully shifted out a word-containing LogUnit and made space for the new
// LogUnit.
mNumActualWords--;
break;
}
}
}
/**
* Removes all LogUnits from the buffer without calling onShiftOut().
*/
@Override
public void clear() {
super.clear();
mNumActualWords = 0;
}
/**
* Called when a LogUnit is removed from the LogBuffer as a result of a shiftIn. LogUnits are
* removed in the order entered. This method is not called when shiftOut is called directly.
*
* Base class does nothing; subclasses may override if they want to record non-privacy sensitive
* events that fall off the end.
*/
protected void onShiftOut(final LogUnit logUnit) {
}
/**
* Called to deliberately remove the oldest LogUnit. Usually called when draining the
* LogBuffer.
*/
@Override
public LogUnit shiftOut() {
if (isEmpty()) {
return null;
}
final LogUnit logUnit = super.shiftOut();
if (logUnit.hasWord()) {
mNumActualWords--;
}
return logUnit;
}
}

View File

@ -16,102 +16,44 @@
package com.android.inputmethod.research; package com.android.inputmethod.research;
import com.android.inputmethod.latin.CollectionUtils;
import java.util.LinkedList; import java.util.LinkedList;
/** /**
* A buffer that holds a fixed number of LogUnits. * Maintain a FIFO queue of LogUnits.
* *
* LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are * This class provides an unbounded queue. This is useful when the user is aware that their actions
* actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches * are being recorded, such as when they are trying to reproduce a bug. In this case, there should
* capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to * not be artificial restrictions on how many events that can be saved.
* stay under the capacity limit.
*/ */
public class LogBuffer { public class LogBuffer {
protected final LinkedList<LogUnit> mLogUnits; // TODO: Gracefully handle situations in which this LogBuffer is consuming too much memory.
/* package for test */ int mWordCapacity; // This may happen, for example, if the user has forgotten that data is being logged.
// The number of members of mLogUnits that are actual words. private final LinkedList<LogUnit> mLogUnits;
protected int mNumActualWords;
/** public LogBuffer() {
* Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and mLogUnits = new LinkedList<LogUnit>();
* unlimited number of non-word LogUnits), and that outputs its result to a researchLog.
*
* @param wordCapacity maximum number of words
*/
LogBuffer(final int wordCapacity) {
if (wordCapacity <= 0) {
throw new IllegalArgumentException("wordCapacity must be 1 or greater.");
}
mLogUnits = CollectionUtils.newLinkedList();
mWordCapacity = wordCapacity;
mNumActualWords = 0;
} }
/** protected LinkedList<LogUnit> getLogUnits() {
* Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's return mLogUnits;
* (oldest first) if word capacity is reached.
*/
public void shiftIn(LogUnit newLogUnit) {
if (newLogUnit.getWord() == null) {
// This LogUnit isn't a word, so it doesn't count toward the word-limit.
mLogUnits.add(newLogUnit);
return;
}
if (mNumActualWords == mWordCapacity) {
shiftOutThroughFirstWord();
}
mLogUnits.add(newLogUnit);
mNumActualWords++; // Must be a word, or we wouldn't be here.
} }
private void shiftOutThroughFirstWord() {
while (!mLogUnits.isEmpty()) {
final LogUnit logUnit = mLogUnits.removeFirst();
onShiftOut(logUnit);
if (logUnit.hasWord()) {
// Successfully shifted out a word-containing LogUnit and made space for the new
// LogUnit.
mNumActualWords--;
break;
}
}
}
/**
* Removes all LogUnits from the buffer without calling onShiftOut().
*/
public void clear() { public void clear() {
mLogUnits.clear(); mLogUnits.clear();
mNumActualWords = 0;
} }
/** public void shiftIn(final LogUnit logUnit) {
* Called when a LogUnit is removed from the LogBuffer as a result of a shiftIn. LogUnits are mLogUnits.add(logUnit);
* removed in the order entered. This method is not called when shiftOut is called directly.
*
* Base class does nothing; subclasses may override.
*/
protected void onShiftOut(LogUnit logUnit) {
}
/**
* Called to deliberately remove the oldest LogUnit. Usually called when draining the
* LogBuffer.
*/
public LogUnit shiftOut() {
if (mLogUnits.isEmpty()) {
return null;
}
final LogUnit logUnit = mLogUnits.removeFirst();
if (logUnit.hasWord()) {
mNumActualWords--;
}
return logUnit;
} }
public boolean isEmpty() { public boolean isEmpty() {
return mLogUnits.isEmpty(); return mLogUnits.isEmpty();
} }
public LogUnit shiftOut() {
if (isEmpty()) {
return null;
}
return mLogUnits.removeFirst();
}
} }

View File

@ -22,15 +22,24 @@ import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.define.ProductionFlag;
import java.util.LinkedList;
import java.util.Random; import java.util.Random;
public class MainLogBuffer extends LogBuffer { /**
* Provide a log buffer of fixed length that enforces privacy restrictions.
*
* The privacy restrictions include making sure that no numbers are logged, that all logged words
* are in the dictionary, and that words are recorded infrequently enough that the user's meaning
* cannot be easily determined.
*/
public class MainLogBuffer extends FixedLogBuffer {
private static final String TAG = MainLogBuffer.class.getSimpleName(); private static final String TAG = MainLogBuffer.class.getSimpleName();
private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
// The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams. // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams.
private static final int N_GRAM_SIZE = 2; private static final int N_GRAM_SIZE = 2;
// The number of words between n-grams to omit from the log. // The number of words between n-grams to omit from the log. If debugging, record 50% of all
// words. Otherwise, only record 10%.
private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES = private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES =
ProductionFlag.IS_EXPERIMENTAL_DEBUG ? 2 : 18; ProductionFlag.IS_EXPERIMENTAL_DEBUG ? 2 : 18;
@ -56,7 +65,7 @@ public class MainLogBuffer extends LogBuffer {
mWordsUntilSafeToSample = random.nextInt(mMinWordPeriod); mWordsUntilSafeToSample = random.nextInt(mMinWordPeriod);
} }
public void setSuggest(Suggest suggest) { public void setSuggest(final Suggest suggest) {
mSuggest = suggest; mSuggest = suggest;
} }
@ -108,9 +117,10 @@ public class MainLogBuffer extends LogBuffer {
} }
// Check each word in the buffer. If any word poses a privacy threat, we cannot upload the // Check each word in the buffer. If any word poses a privacy threat, we cannot upload the
// complete buffer contents in detail. // complete buffer contents in detail.
final int length = mLogUnits.size(); final LinkedList<LogUnit> logUnits = getLogUnits();
final int length = logUnits.size();
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
final LogUnit logUnit = mLogUnits.get(i); final LogUnit logUnit = logUnits.get(i);
final String word = logUnit.getWord(); final String word = logUnit.getWord();
if (word == null) { if (word == null) {
// Digits outside words are a privacy threat. // Digits outside words are a privacy threat.
@ -133,7 +143,7 @@ public class MainLogBuffer extends LogBuffer {
} }
@Override @Override
protected void onShiftOut(LogUnit logUnit) { protected void onShiftOut(final LogUnit logUnit) {
if (mResearchLog != null) { if (mResearchLog != null) {
mResearchLog.publish(logUnit, false /* isIncludingPrivateData */); mResearchLog.publish(logUnit, false /* isIncludingPrivateData */);
} }

View File

@ -375,7 +375,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); mFeedbackLog = new ResearchLog(createLogFile(mFilesDir));
// LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold // LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold
// the feedback LogUnit itself. // the feedback LogUnit itself.
mFeedbackLogBuffer = new LogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1); mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1);
} }
} }