diff options
Diffstat (limited to 'java/src/com/android/inputmethod/research/ResearchLog.java')
-rw-r--r-- | java/src/com/android/inputmethod/research/ResearchLog.java | 178 |
1 files changed, 51 insertions, 127 deletions
diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 70c38e909..99d84938f 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -1,39 +1,33 @@ /* * 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 + * 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 + * 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. + * 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 android.content.SharedPreferences; -import android.os.SystemClock; +import android.content.Context; import android.util.JsonWriter; import android.util.Log; -import android.view.inputmethod.CompletionInfo; -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; import java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; +import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; @@ -51,12 +45,14 @@ import java.util.concurrent.TimeUnit; */ public class ResearchLog { private static final String TAG = ResearchLog.class.getSimpleName(); - private static final boolean DEBUG = false; + private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; private static final long FLUSH_DELAY_IN_MS = 1000 * 5; private static final int ABORT_TIMEOUT_IN_MS = 1000 * 4; /* package */ final ScheduledExecutorService mExecutor; /* package */ final File mFile; + private final Context mContext; + private JsonWriter mJsonWriter = NULL_JSON_WRITER; // true if at least one byte of data has been written out to the log file. This must be // remembered because JsonWriter requires that calls matching calls to beginObject and @@ -85,12 +81,10 @@ public class ResearchLog { } } - public ResearchLog(final File outputFile) { - if (outputFile == null) { - throw new IllegalArgumentException(); - } + public ResearchLog(final File outputFile, final Context context) { mExecutor = Executors.newSingleThreadScheduledExecutor(); mFile = outputFile; + mContext = context; } public synchronized void close(final Runnable onClosed) { @@ -115,7 +109,7 @@ public class ResearchLog { Log.d(TAG, "error when closing ResearchLog:"); e.printStackTrace(); } finally { - if (mFile.exists()) { + if (mFile != null && mFile.exists()) { mFile.setWritable(false, false); } if (onClosed != null) { @@ -142,7 +136,9 @@ public class ResearchLog { mHasWrittenData = false; } } finally { - mIsAbortSuccessful = mFile.delete(); + if (mFile != null) { + mIsAbortSuccessful = mFile.delete(); + } } return null; } @@ -188,133 +184,61 @@ public class ResearchLog { mFlushFuture = mExecutor.schedule(mFlushCallable, FLUSH_DELAY_IN_MS, TimeUnit.MILLISECONDS); } - public synchronized void publish(final LogUnit logUnit, final boolean isIncludingPrivateData) { + public synchronized void publish(final LogUnit logUnit, final boolean canIncludePrivateData) { try { mExecutor.submit(new Callable<Object>() { @Override public Object call() throws Exception { - logUnit.publishTo(ResearchLog.this, isIncludingPrivateData); + logUnit.publishTo(ResearchLog.this, canIncludePrivateData); scheduleFlush(); return null; } }); } catch (RejectedExecutionException e) { // TODO: Add code to record loss of data, and report. + if (DEBUG) { + Log.d(TAG, "ResearchLog.publish() rejecting scheduled execution"); + } } } - private static final String CURRENT_TIME_KEY = "_ct"; - private static final String UPTIME_KEY = "_ut"; - private static final String EVENT_TYPE_KEY = "_ty"; - - void outputEvent(final String[] keys, final Object[] values) { - // Not thread safe. - if (keys.length == 0) { - return; - } - if (DEBUG) { - if (keys.length != values.length + 1) { - Log.d(TAG, "Key and Value list sizes do not match. " + keys[0]); - } - } + /** + * Return a JsonWriter for this ResearchLog. It is initialized the first time this method is + * called. The cached value is returned in future calls. + */ + public JsonWriter getInitializedJsonWriterLocked() { + if (mJsonWriter != NULL_JSON_WRITER || mFile == null) return mJsonWriter; try { - if (mJsonWriter == NULL_JSON_WRITER) { - mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile))); - mJsonWriter.beginArray(); + final JsonWriter jsonWriter = createJsonWriter(mContext, mFile); + if (jsonWriter != null) { + jsonWriter.beginArray(); + mJsonWriter = jsonWriter; mHasWrittenData = true; } - mJsonWriter.beginObject(); - mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis()); - mJsonWriter.name(UPTIME_KEY).value(SystemClock.uptimeMillis()); - mJsonWriter.name(EVENT_TYPE_KEY).value(keys[0]); - final int length = values.length; - for (int i = 0; i < length; i++) { - mJsonWriter.name(keys[i + 1]); - Object value = values[i]; - if (value instanceof CharSequence) { - mJsonWriter.value(value.toString()); - } else if (value instanceof Number) { - mJsonWriter.value((Number) value); - } else if (value instanceof Boolean) { - mJsonWriter.value((Boolean) value); - } else if (value instanceof CompletionInfo[]) { - CompletionInfo[] ci = (CompletionInfo[]) value; - mJsonWriter.beginArray(); - for (int j = 0; j < ci.length; j++) { - mJsonWriter.value(ci[j].toString()); - } - mJsonWriter.endArray(); - } else if (value instanceof SharedPreferences) { - SharedPreferences prefs = (SharedPreferences) value; - mJsonWriter.beginObject(); - for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) { - mJsonWriter.name(entry.getKey()); - final Object innerValue = entry.getValue(); - if (innerValue == null) { - mJsonWriter.nullValue(); - } else if (innerValue instanceof Boolean) { - mJsonWriter.value((Boolean) innerValue); - } else if (innerValue instanceof Number) { - mJsonWriter.value((Number) innerValue); - } else { - mJsonWriter.value(innerValue.toString()); - } - } - mJsonWriter.endObject(); - } else if (value instanceof Key[]) { - Key[] keyboardKeys = (Key[]) value; - mJsonWriter.beginArray(); - for (Key keyboardKey : keyboardKeys) { - mJsonWriter.beginObject(); - mJsonWriter.name("code").value(keyboardKey.mCode); - mJsonWriter.name("altCode").value(keyboardKey.getAltCode()); - mJsonWriter.name("x").value(keyboardKey.mX); - mJsonWriter.name("y").value(keyboardKey.mY); - mJsonWriter.name("w").value(keyboardKey.mWidth); - mJsonWriter.name("h").value(keyboardKey.mHeight); - mJsonWriter.endObject(); - } - mJsonWriter.endArray(); - } else if (value instanceof SuggestedWords) { - SuggestedWords words = (SuggestedWords) value; - mJsonWriter.beginObject(); - mJsonWriter.name("typedWordValid").value(words.mTypedWordValid); - mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect); - mJsonWriter.name("isPunctuationSuggestions") - .value(words.mIsPunctuationSuggestions); - mJsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions); - mJsonWriter.name("isPrediction").value(words.mIsPrediction); - mJsonWriter.name("words"); - mJsonWriter.beginArray(); - final int size = words.size(); - for (int j = 0; j < size; j++) { - SuggestedWordInfo wordInfo = words.getWordInfo(j); - mJsonWriter.value(wordInfo.toString()); - } - mJsonWriter.endArray(); - mJsonWriter.endObject(); - } else if (value == null) { - mJsonWriter.nullValue(); - } else { - Log.w(TAG, "Unrecognized type to be logged: " + - (value == null ? "<null>" : value.getClass().getName())); - mJsonWriter.nullValue(); - } - } - mJsonWriter.endObject(); - } catch (IOException e) { - e.printStackTrace(); - Log.w(TAG, "Error in JsonWriter; disabling logging"); + } catch (final IOException e) { + Log.w(TAG, "Error in JsonWriter; disabling logging", e); try { mJsonWriter.close(); - } catch (IllegalStateException e1) { + } catch (final IllegalStateException e1) { // Assume that this is just the json not being terminated properly. // Ignore - } catch (IOException e1) { - e1.printStackTrace(); + } catch (final IOException e1) { + Log.w(TAG, "Error in closing JsonWriter; disabling logging", e1); } finally { mJsonWriter = NULL_JSON_WRITER; } } + return mJsonWriter; + } + + /** + * Create the JsonWriter to write the ResearchLog to. + * + * This method may be overriden in testing to redirect the output. + */ + /* package for test */ JsonWriter createJsonWriter(final Context context, final File file) + throws IOException { + return new JsonWriter(new BufferedWriter(new OutputStreamWriter( + context.openFileOutput(file.getName(), Context.MODE_PRIVATE)))); } } |