1f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge/*
2f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * Copyright (C) 2013 The Android Open Source Project
3f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge *
4f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * Licensed under the Apache License, Version 2.0 (the "License");
5f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * you may not use this file except in compliance with the License.
6f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * You may obtain a copy of the License at
7f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge *
8f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge *      http://www.apache.org/licenses/LICENSE-2.0
9f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge *
10f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * Unless required by applicable law or agreed to in writing, software
11f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * distributed under the License is distributed on an "AS IS" BASIS,
12f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * See the License for the specific language governing permissions and
14f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * limitations under the License.
15f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge */
16f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
17f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridgepackage com.android.inputmethod.research;
18f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
19b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridgeimport android.content.SharedPreferences;
20b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridgeimport android.util.JsonWriter;
21b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridgeimport android.util.Log;
22b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridgeimport android.view.MotionEvent;
23b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridgeimport android.view.inputmethod.CompletionInfo;
24b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge
25b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridgeimport com.android.inputmethod.keyboard.Key;
26b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridgeimport com.android.inputmethod.latin.SuggestedWords;
27b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridgeimport com.android.inputmethod.latin.define.ProductionFlag;
28b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge
29b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridgeimport java.io.IOException;
30b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge
31f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge/**
32f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * A template for typed information stored in the logs.
33f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge *
34f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * A LogStatement contains a name, keys, and flags about whether the {@code Object[] values}
35f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * associated with the {@code String[] keys} are likely to reveal information about the user.  The
36f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge * actual values are stored separately.
37f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge */
38531dd150eb1ddf88cb09c404a14834893c82f960Kurt Partridgepublic class LogStatement {
39b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge    private static final String TAG = LogStatement.class.getSimpleName();
409c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa    private static final boolean DEBUG = false
419c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG;
42b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge
43f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // Constants for particular statements
44f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public static final String TYPE_POINTER_TRACKER_CALL_LISTENER_ON_CODE_INPUT =
45f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge            "PointerTrackerCallListenerOnCodeInput";
46f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public static final String KEY_CODE = "code";
47f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public static final String VALUE_RESEARCH = "research";
48c20a415e151404414c0195fdd8a64918452d8027Kurt Partridge    public static final String TYPE_MAIN_KEYBOARD_VIEW_ON_LONG_PRESS =
49c20a415e151404414c0195fdd8a64918452d8027Kurt Partridge            "MainKeyboardViewOnLongPress";
50f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public static final String ACTION = "action";
51f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public static final String VALUE_DOWN = "DOWN";
52c20a415e151404414c0195fdd8a64918452d8027Kurt Partridge    public static final String TYPE_MOTION_EVENT = "MotionEvent";
53c20a415e151404414c0195fdd8a64918452d8027Kurt Partridge    public static final String KEY_IS_LOGGING_RELATED = "isLoggingRelated";
54f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
55b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge    // Keys for internal key/value pairs
56b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge    private static final String CURRENT_TIME_KEY = "_ct";
57b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge    private static final String UPTIME_KEY = "_ut";
58b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge    private static final String EVENT_TYPE_KEY = "_ty";
59b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge
60f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // Name specifying the LogStatement type.
61f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    private final String mType;
62f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
63f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // mIsPotentiallyPrivate indicates that event contains potentially private information.  If
64f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // the word that this event is a part of is determined to be privacy-sensitive, then this
65f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // event should not be included in the output log.  The system waits to output until the
66f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // containing word is known.
67f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    private final boolean mIsPotentiallyPrivate;
68f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
69f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // mIsPotentiallyRevealing indicates that this statement may disclose details about other
70f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // words typed in other LogUnits.  This can happen if the user is not inserting spaces, and
71f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // data from Suggestions and/or Composing text reveals the entire "megaword".  For example,
72f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // say the user is typing "for the win", and the system wants to record the bigram "the
73f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // win".  If the user types "forthe", omitting the space, the system will give "for the" as
74f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // a suggestion.  If the user accepts the autocorrection, the suggestion for "for the" is
75f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // included in the log for the word "the", disclosing that the previous word had been "for".
76f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // For now, we simply do not include this data when logging part of a "megaword".
77f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    private final boolean mIsPotentiallyRevealing;
78f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
79f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    // mKeys stores the names that are the attributes in the output json objects
80f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    private final String[] mKeys;
81f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    private static final String[] NULL_KEYS = new String[0];
82f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
83f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    LogStatement(final String name, final boolean isPotentiallyPrivate,
84f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge            final boolean isPotentiallyRevealing, final String... keys) {
85f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        mType = name;
86f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        mIsPotentiallyPrivate = isPotentiallyPrivate;
87f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        mIsPotentiallyRevealing = isPotentiallyRevealing;
88f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        mKeys = (keys == null) ? NULL_KEYS : keys;
89f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    }
90f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
91f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public String getType() {
92f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        return mType;
93f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    }
94f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
95f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public boolean isPotentiallyPrivate() {
96f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        return mIsPotentiallyPrivate;
97f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    }
98f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
99f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public boolean isPotentiallyRevealing() {
100f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        return mIsPotentiallyRevealing;
101f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    }
102f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
103f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public String[] getKeys() {
104f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        return mKeys;
105f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    }
106f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
107f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    /**
108f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * Utility function to test whether a key-value pair exists in a LogStatement.
109f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     *
110f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * A LogStatement is really just a template -- it does not contain the values, only the
111f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * keys.  So the values must be passed in as an argument.
112f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     *
113f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * @param queryKey the String that is tested by {@code String.equals()} to the keys in the
114f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * LogStatement
115f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * @param queryValue an Object that must be {@code Object.equals()} to the key's corresponding
116f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * value in the {@code values} array
117f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * @param values the values corresponding to mKeys
118f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     *
119f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * @returns {@true} if {@code queryKey} exists in the keys for this LogStatement, and {@code
120f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * queryValue} matches the corresponding value in {@code values}
121f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     *
122f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * @throws IllegalArgumentException if {@code values.length} is not equal to keys().length()
123f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     */
124f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public boolean containsKeyValuePair(final String queryKey, final Object queryValue,
125f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge            final Object[] values) {
126f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        if (mKeys.length != values.length) {
127f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge            throw new IllegalArgumentException("Mismatched number of keys and values.");
128f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        }
129f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        final int length = mKeys.length;
130f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        for (int i = 0; i < length; i++) {
131f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge            if (mKeys[i].equals(queryKey) && values[i].equals(queryValue)) {
132f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge                return true;
133f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge            }
134f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        }
135f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        return false;
136f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    }
137f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge
138f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    /**
139f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * Utility function to set a value in a LogStatement.
140f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     *
141f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * A LogStatement is really just a template -- it does not contain the values, only the
142f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * keys.  So the values must be passed in as an argument.
143f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     *
144f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * @param queryKey the String that is tested by {@code String.equals()} to the keys in the
145f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * LogStatement
146f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * @param values the array of values corresponding to mKeys
147f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * @param newValue the replacement value to go into the {@code values} array
148f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     *
149f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * @returns {@true} if the key exists and the value was successfully set, {@false} otherwise
150f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     *
151f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     * @throws IllegalArgumentException if {@code values.length} is not equal to keys().length()
152f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge     */
153f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    public boolean setValue(final String queryKey, final Object[] values, final Object newValue) {
154f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        if (mKeys.length != values.length) {
155f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge            throw new IllegalArgumentException("Mismatched number of keys and values.");
156f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        }
157f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        final int length = mKeys.length;
158f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        for (int i = 0; i < length; i++) {
159f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge            if (mKeys[i].equals(queryKey)) {
160f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge                values[i] = newValue;
161f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge                return true;
162f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge            }
163f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        }
164f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge        return false;
165f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge    }
166b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge
167b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge    /**
168b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge     * Write the contents out through jsonWriter.
169b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge     *
170531dd150eb1ddf88cb09c404a14834893c82f960Kurt Partridge     * The JsonWriter class must have already had {@code JsonWriter.beginArray} called on it.
171531dd150eb1ddf88cb09c404a14834893c82f960Kurt Partridge     *
172b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge     * Note that this method is not thread safe for the same jsonWriter.  Callers must ensure
173b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge     * thread safety.
174b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge     */
175b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge    public boolean outputToLocked(final JsonWriter jsonWriter, final Long time,
176b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            final Object... values) {
177b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge        if (DEBUG) {
178b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            if (mKeys.length != values.length) {
179b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                Log.d(TAG, "Key and Value list sizes do not match. " + mType);
180b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            }
181b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge        }
182b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge        try {
183b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            jsonWriter.beginObject();
184b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            jsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
185b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            jsonWriter.name(UPTIME_KEY).value(time);
186b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            jsonWriter.name(EVENT_TYPE_KEY).value(mType);
187b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            final int length = values.length;
188b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            for (int i = 0; i < length; i++) {
189b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                jsonWriter.name(mKeys[i]);
190b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                final Object value = values[i];
191b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                if (value instanceof CharSequence) {
192b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    jsonWriter.value(value.toString());
193b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                } else if (value instanceof Number) {
194b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    jsonWriter.value((Number) value);
195b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                } else if (value instanceof Boolean) {
196b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    jsonWriter.value((Boolean) value);
197b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                } else if (value instanceof CompletionInfo[]) {
198b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    JsonUtils.writeJson((CompletionInfo[]) value, jsonWriter);
199b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                } else if (value instanceof SharedPreferences) {
200b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    JsonUtils.writeJson((SharedPreferences) value, jsonWriter);
201b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                } else if (value instanceof Key[]) {
202b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    JsonUtils.writeJson((Key[]) value, jsonWriter);
203b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                } else if (value instanceof SuggestedWords) {
204b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    JsonUtils.writeJson((SuggestedWords) value, jsonWriter);
205b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                } else if (value instanceof MotionEvent) {
206b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    JsonUtils.writeJson((MotionEvent) value, jsonWriter);
207b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                } else if (value == null) {
208b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    jsonWriter.nullValue();
209b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                } else {
210b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    if (DEBUG) {
211b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                        Log.w(TAG, "Unrecognized type to be logged: "
212b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                                + (value == null ? "<null>" : value.getClass().getName()));
213b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    }
214b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                    jsonWriter.nullValue();
215b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge                }
216b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            }
217b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            jsonWriter.endObject();
218b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge        } catch (IOException e) {
219b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            e.printStackTrace();
220b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            Log.w(TAG, "Error in JsonWriter; skipping LogStatement");
221b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge            return false;
222b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge        }
223b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge        return true;
224b02a19c49d29cd7758d01349ebbfeb479a56bb37Kurt Partridge    }
225f3731188e5ee46c0bee7e9366528c826289a91bbKurt Partridge}
226