SQLiteConnection.java revision 47847f3f4dcf2a0dbea0bc0e4f02528e21d37a88
1e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown/*
2e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * Copyright (C) 2011 The Android Open Source Project
3e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown *
4e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
5e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * you may not use this file except in compliance with the License.
6e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * You may obtain a copy of the License at
7e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown *
8e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
9e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown *
10e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * Unless required by applicable law or agreed to in writing, software
11e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
12e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * See the License for the specific language governing permissions and
14e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * limitations under the License.
15e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown */
16e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
17e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownpackage android.database.sqlite;
18e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
19e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport dalvik.system.BlockGuard;
20e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport dalvik.system.CloseGuard;
21e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
224c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brownimport android.content.CancellationSignal;
2375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brownimport android.content.OperationCanceledException;
24e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport android.database.Cursor;
25e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport android.database.CursorWindow;
26e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport android.database.DatabaseUtils;
27e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport android.database.sqlite.SQLiteDebug.DbStats;
28e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport android.os.ParcelFileDescriptor;
29e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport android.util.Log;
30e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport android.util.LruCache;
31e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport android.util.Printer;
32e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
33e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport java.sql.Date;
34e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport java.text.SimpleDateFormat;
35e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport java.util.ArrayList;
36e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport java.util.Map;
37e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport java.util.regex.Pattern;
38e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
39e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown/**
40e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * Represents a SQLite database connection.
41e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * Each connection wraps an instance of a native <code>sqlite3</code> object.
42e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * <p>
43e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * When database connection pooling is enabled, there can be multiple active
44e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * connections to the same database.  Otherwise there is typically only one
45e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * connection per database.
46e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * </p><p>
47e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * When the SQLite WAL feature is enabled, multiple readers and one writer
48e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * can concurrently access the database.  Without WAL, readers and writers
49e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * are mutually exclusive.
50e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * </p>
51e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown *
52e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * <h2>Ownership and concurrency guarantees</h2>
53e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * <p>
54e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * Connection objects are not thread-safe.  They are acquired as needed to
55e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * perform a database operation and are then returned to the pool.  At any
56e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * given time, a connection is either owned and used by a {@link SQLiteSession}
57e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * object or the {@link SQLiteConnectionPool}.  Those classes are
58e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * responsible for serializing operations to guard against concurrent
59e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * use of a connection.
60e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * </p><p>
61e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * The guarantee of having a single owner allows this class to be implemented
62e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * without locks and greatly simplifies resource management.
63e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * </p>
64e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown *
65e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * <h2>Encapsulation guarantees</h2>
66e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * <p>
67e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * The connection object object owns *all* of the SQLite related native
68e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * objects that are associated with the connection.  What's more, there are
69e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * no other objects in the system that are capable of obtaining handles to
70e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * those native objects.  Consequently, when the connection is closed, we do
71e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * not have to worry about what other components might have references to
72e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * its associated SQLite state -- there are none.
73e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * </p><p>
74e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * Encapsulation is what ensures that the connection object's
75e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * lifecycle does not become a tortured mess of finalizers and reference
76e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * queues.
77e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * </p>
78e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown *
79a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown * <h2>Reentrance</h2>
80a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown * <p>
81a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown * This class must tolerate reentrant execution of SQLite operations because
82a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown * triggers may call custom SQLite functions that perform additional queries.
83a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown * </p>
84a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown *
85e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * @hide
86e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown */
874c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brownpublic final class SQLiteConnection implements CancellationSignal.OnCancelListener {
88e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static final String TAG = "SQLiteConnection";
892a293b61cb0efbf24994d74ed980f58b820bb35aJeff Brown    private static final boolean DEBUG = false;
90e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
91e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static final String[] EMPTY_STRING_ARRAY = new String[0];
92e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
93e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
94e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static final Pattern TRIM_SQL_PATTERN = Pattern.compile("[\\s]*\\n+[\\s]*");
95e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
96e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final CloseGuard mCloseGuard = CloseGuard.get();
97e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
98e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final SQLiteConnectionPool mPool;
99e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final SQLiteDatabaseConfiguration mConfiguration;
100e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final int mConnectionId;
101e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final boolean mIsPrimaryConnection;
1021d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown    private final boolean mIsReadOnlyConnection;
103e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final PreparedStatementCache mPreparedStatementCache;
104e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private PreparedStatement mPreparedStatementPool;
105e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
106e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // The recent operations log.
107e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final OperationLog mRecentOperations = new OperationLog();
108e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
109e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // The native SQLiteConnection pointer.  (FOR INTERNAL USE ONLY)
110e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private int mConnectionPtr;
111e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
112e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private boolean mOnlyAllowReadOnlyOperations;
113e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1144c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown    // The number of times attachCancellationSignal has been called.
1151d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown    // Because SQLite statement execution can be reentrant, we keep track of how many
1164c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown    // times we have attempted to attach a cancellation signal to the connection so that
11775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    // we can ensure that we detach the signal at the right time.
1184c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown    private int mCancellationSignalAttachCount;
11975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown
120e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native int nativeOpen(String path, int openFlags, String label,
121e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            boolean enableTrace, boolean enableProfile);
122e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native void nativeClose(int connectionPtr);
123e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native void nativeRegisterCustomFunction(int connectionPtr,
124e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            SQLiteCustomFunction function);
1251d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown    private static native void nativeRegisterLocalizedCollators(int connectionPtr, String locale);
126e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native int nativePrepareStatement(int connectionPtr, String sql);
127e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native void nativeFinalizeStatement(int connectionPtr, int statementPtr);
128e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native int nativeGetParameterCount(int connectionPtr, int statementPtr);
129e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native boolean nativeIsReadOnly(int connectionPtr, int statementPtr);
130e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native int nativeGetColumnCount(int connectionPtr, int statementPtr);
131e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native String nativeGetColumnName(int connectionPtr, int statementPtr,
132e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int index);
133e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native void nativeBindNull(int connectionPtr, int statementPtr,
134e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int index);
135e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native void nativeBindLong(int connectionPtr, int statementPtr,
136e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int index, long value);
137e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native void nativeBindDouble(int connectionPtr, int statementPtr,
138e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int index, double value);
139e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native void nativeBindString(int connectionPtr, int statementPtr,
140e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int index, String value);
141e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native void nativeBindBlob(int connectionPtr, int statementPtr,
142e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int index, byte[] value);
143e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native void nativeResetStatementAndClearBindings(
144e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int connectionPtr, int statementPtr);
145e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native void nativeExecute(int connectionPtr, int statementPtr);
146e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native long nativeExecuteForLong(int connectionPtr, int statementPtr);
147e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native String nativeExecuteForString(int connectionPtr, int statementPtr);
148e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native int nativeExecuteForBlobFileDescriptor(
149e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int connectionPtr, int statementPtr);
150e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native int nativeExecuteForChangedRowCount(int connectionPtr, int statementPtr);
151e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native long nativeExecuteForLastInsertedRowId(
152e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int connectionPtr, int statementPtr);
153e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native long nativeExecuteForCursorWindow(
154e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int connectionPtr, int statementPtr, int windowPtr,
155e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int startPos, int requiredPos, boolean countAllRows);
156e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static native int nativeGetDbLookaside(int connectionPtr);
15775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    private static native void nativeCancel(int connectionPtr);
15875ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    private static native void nativeResetCancel(int connectionPtr, boolean cancelable);
159e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
160e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private SQLiteConnection(SQLiteConnectionPool pool,
161e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            SQLiteDatabaseConfiguration configuration,
162e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int connectionId, boolean primaryConnection) {
163e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mPool = pool;
164e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mConfiguration = new SQLiteDatabaseConfiguration(configuration);
165e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mConnectionId = connectionId;
166e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mIsPrimaryConnection = primaryConnection;
1671d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        mIsReadOnlyConnection = (configuration.openFlags & SQLiteDatabase.OPEN_READONLY) != 0;
168e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mPreparedStatementCache = new PreparedStatementCache(
169e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mConfiguration.maxSqlCacheSize);
170e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mCloseGuard.open("close");
171e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
172e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
173e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    @Override
174e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    protected void finalize() throws Throwable {
175e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
176e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (mPool != null && mConnectionPtr != 0) {
177e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mPool.onConnectionLeaked();
178e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
179e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
180e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            dispose(true);
181e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } finally {
182e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            super.finalize();
183e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
184e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
185e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
186e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // Called by SQLiteConnectionPool only.
187e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    static SQLiteConnection open(SQLiteConnectionPool pool,
188e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            SQLiteDatabaseConfiguration configuration,
189e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int connectionId, boolean primaryConnection) {
190e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        SQLiteConnection connection = new SQLiteConnection(pool, configuration,
191e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                connectionId, primaryConnection);
192e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
193e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            connection.open();
194e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            return connection;
195e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (SQLiteException ex) {
196e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            connection.dispose(false);
197e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw ex;
198e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
199e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
200e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
201e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // Called by SQLiteConnectionPool only.
202e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // Closes the database closes and releases all of its associated resources.
203e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // Do not call methods on the connection after it is closed.  It will probably crash.
204e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    void close() {
205e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        dispose(false);
206e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
207e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
208e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private void open() {
209e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags,
210e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mConfiguration.label,
211e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
212e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
2135936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown        setPageSize();
214d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        setWalModeFromConfiguration();
2158dc3cc2e13b500e368f5ba1aacfaf0eddbce668cJeff Brown        setJournalSizeLimit();
2168dc3cc2e13b500e368f5ba1aacfaf0eddbce668cJeff Brown        setAutoCheckpointInterval();
217e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        setLocaleFromConfiguration();
218e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
219e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
220e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private void dispose(boolean finalized) {
221e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (mCloseGuard != null) {
222e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (finalized) {
223e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mCloseGuard.warnIfOpen();
224e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
225e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            mCloseGuard.close();
226e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
227e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
228e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (mConnectionPtr != 0) {
229a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final int cookie = mRecentOperations.beginOperation("close", null, null);
230e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            try {
231e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mPreparedStatementCache.evictAll();
232e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                nativeClose(mConnectionPtr);
233e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mConnectionPtr = 0;
234e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } finally {
235a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                mRecentOperations.endOperation(cookie);
236e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
237e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
238e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
239e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
2405936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown    private void setPageSize() {
2411d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
2421d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            final long newValue = SQLiteGlobal.getDefaultPageSize();
2431d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            long value = executeForLong("PRAGMA page_size", null, null);
2441d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            if (value != newValue) {
2451d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                execute("PRAGMA page_size=" + newValue, null, null);
2461d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            }
2475936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown        }
2485936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown    }
2495936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown
2505936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown    private void setAutoCheckpointInterval() {
2511d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
2521d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            final long newValue = SQLiteGlobal.getWALAutoCheckpoint();
2531d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            long value = executeForLong("PRAGMA wal_autocheckpoint", null, null);
2541d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            if (value != newValue) {
2551d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                executeForLong("PRAGMA wal_autocheckpoint=" + newValue, null, null);
2561d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            }
2575936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown        }
2585936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown    }
2595936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown
2605936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown    private void setJournalSizeLimit() {
2611d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
2621d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            final long newValue = SQLiteGlobal.getJournalSizeLimit();
2631d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            long value = executeForLong("PRAGMA journal_size_limit", null, null);
2641d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            if (value != newValue) {
2651d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                executeForLong("PRAGMA journal_size_limit=" + newValue, null, null);
2661d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            }
2675936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown        }
2685936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown    }
2695936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown
270d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown    private void setWalModeFromConfiguration() {
2711d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
27247847f3f4dcf2a0dbea0bc0e4f02528e21d37a88Jeff Brown            if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
273d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                setJournalMode("WAL");
274d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                setSyncMode(SQLiteGlobal.getWALSyncMode());
275d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            } else {
276d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                setJournalMode(SQLiteGlobal.getDefaultJournalMode());
277d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                setSyncMode(SQLiteGlobal.getDefaultSyncMode());
2781d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            }
2798dc3cc2e13b500e368f5ba1aacfaf0eddbce668cJeff Brown        }
2808dc3cc2e13b500e368f5ba1aacfaf0eddbce668cJeff Brown    }
2818dc3cc2e13b500e368f5ba1aacfaf0eddbce668cJeff Brown
282d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown    private void setSyncMode(String newValue) {
283d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        String value = executeForString("PRAGMA synchronous", null, null);
284d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        if (!canonicalizeSyncMode(value).equalsIgnoreCase(
285d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                canonicalizeSyncMode(newValue))) {
286d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            execute("PRAGMA synchronous=" + newValue, null, null);
287d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        }
288d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown    }
289d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown
290d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown    private static String canonicalizeSyncMode(String value) {
291d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        if (value.equals("0")) {
292d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            return "OFF";
293d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        } else if (value.equals("1")) {
294d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            return "NORMAL";
295d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        } else if (value.equals("2")) {
296d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            return "FULL";
297d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        }
298d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        return value;
299d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown    }
300d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown
301d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown    private void setJournalMode(String newValue) {
302d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        String value = executeForString("PRAGMA journal_mode", null, null);
303d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        if (!value.equalsIgnoreCase(newValue)) {
304d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            try {
305d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                String result = executeForString("PRAGMA journal_mode=" + newValue, null, null);
306d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                if (result.equalsIgnoreCase(newValue)) {
307d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                    return;
3081d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                }
309d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                // PRAGMA journal_mode silently fails and returns the original journal
310d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                // mode in some cases if the journal mode could not be changed.
311d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            } catch (SQLiteDatabaseLockedException ex) {
312d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                // This error (SQLITE_BUSY) occurs if one connection has the database
313d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                // open in WAL mode and another tries to change it to non-WAL.
3145936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown            }
315d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // Because we always disable WAL mode when a database is first opened
316d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // (even if we intend to re-enable it), we can encounter problems if
317d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // there is another open connection to the database somewhere.
318d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // This can happen for a variety of reasons such as an application opening
319d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // the same database in multiple processes at the same time or if there is a
320d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // crashing content provider service that the ActivityManager has
321d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // removed from its registry but whose process hasn't quite died yet
322d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // by the time it is restarted in a new process.
323d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            //
324d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // If we don't change the journal mode, nothing really bad happens.
325d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // In the worst case, an application that enables WAL might not actually
326d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            // get it, although it can still use connection pooling.
327d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            Log.w(TAG, "Could not change the database journal mode of '"
328d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                    + mConfiguration.label + "' from '" + value + "' to '" + newValue
329d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                    + "' because the database is locked.  This usually means that "
330d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                    + "there are other open connections to the database which prevents "
331d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                    + "the database from enabling or disabling write-ahead logging mode.  "
332d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown                    + "Proceeding without changing the journal mode.");
3335936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown        }
3345936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown    }
3355936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown
336e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private void setLocaleFromConfiguration() {
3371d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        if ((mConfiguration.openFlags & SQLiteDatabase.NO_LOCALIZED_COLLATORS) != 0) {
3381d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            return;
3391d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        }
3401d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown
3411d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        // Register the localized collators.
3421d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        final String newLocale = mConfiguration.locale.toString();
3431d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        nativeRegisterLocalizedCollators(mConnectionPtr, newLocale);
3441d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown
3451d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        // If the database is read-only, we cannot modify the android metadata table
3461d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        // or existing indexes.
3471d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        if (mIsReadOnlyConnection) {
3481d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            return;
3491d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        }
3501d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown
3511d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        try {
3521d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            // Ensure the android metadata table exists.
3531d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            execute("CREATE TABLE IF NOT EXISTS android_metadata (locale TEXT)", null, null);
3541d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown
3551d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            // Check whether the locale was actually changed.
3561d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            final String oldLocale = executeForString("SELECT locale FROM android_metadata "
3571d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                    + "UNION SELECT NULL ORDER BY locale DESC LIMIT 1", null, null);
3581d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            if (oldLocale != null && oldLocale.equals(newLocale)) {
3591d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                return;
3601d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            }
3611d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown
3621d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            // Go ahead and update the indexes using the new locale.
3631d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            execute("BEGIN", null, null);
3641d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            boolean success = false;
3651d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            try {
3661d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                execute("DELETE FROM android_metadata", null, null);
3671d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                execute("INSERT INTO android_metadata (locale) VALUES(?)",
3681d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                        new Object[] { newLocale }, null);
3691d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                execute("REINDEX LOCALIZED", null, null);
3701d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                success = true;
3711d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            } finally {
3721d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                execute(success ? "COMMIT" : "ROLLBACK", null, null);
3731d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            }
3741d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        } catch (RuntimeException ex) {
3751d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown            throw new SQLiteException("Failed to change locale for db '" + mConfiguration.label
3761d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown                    + "' to '" + newLocale + "'.", ex);
3771d9f742e001ed8280fa93fd9ba0b1125ce6d00aeJeff Brown        }
378e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
379e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
380e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // Called by SQLiteConnectionPool only.
381e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    void reconfigure(SQLiteDatabaseConfiguration configuration) {
382e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // Register custom functions.
383e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        final int functionCount = configuration.customFunctions.size();
384e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        for (int i = 0; i < functionCount; i++) {
385e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            SQLiteCustomFunction function = configuration.customFunctions.get(i);
386e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (!mConfiguration.customFunctions.contains(function)) {
387e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                nativeRegisterCustomFunction(mConnectionPtr, function);
388e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
389e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
390e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
3915936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown        // Remember what changed.
39247847f3f4dcf2a0dbea0bc0e4f02528e21d37a88Jeff Brown        boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
39347847f3f4dcf2a0dbea0bc0e4f02528e21d37a88Jeff Brown                & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
394e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
395e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
396e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // Update configuration parameters.
397e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mConfiguration.updateParametersFrom(configuration);
398e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
399e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // Update prepared statement cache size.
400e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mPreparedStatementCache.resize(configuration.maxSqlCacheSize);
401e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
402d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        // Update WAL.
403d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown        if (walModeChanged) {
404d67c8c67899481682657d41a61f3846b8d77d165Jeff Brown            setWalModeFromConfiguration();
4055936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown        }
4065936ff097eff2c736af2e43fd4a8f7db0ddcfb5aJeff Brown
407e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // Update locale.
408e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (localeChanged) {
409e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            setLocaleFromConfiguration();
410e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
411e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
412e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
413e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // Called by SQLiteConnectionPool only.
414e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // When set to true, executing write operations will throw SQLiteException.
415e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // Preparing statements that might write is ok, just don't execute them.
416e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    void setOnlyAllowReadOnlyOperations(boolean readOnly) {
417e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mOnlyAllowReadOnlyOperations = readOnly;
418e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
419e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
420e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // Called by SQLiteConnectionPool only.
421e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    // Returns true if the prepared statement cache contains the specified SQL.
422e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    boolean isPreparedStatementInCache(String sql) {
423e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return mPreparedStatementCache.get(sql) != null;
424e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
425e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
426e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
427e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Gets the unique id of this connection.
428e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @return The connection id.
429e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
430e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    public int getConnectionId() {
431e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return mConnectionId;
432e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
433e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
434e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
435e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Returns true if this is the primary database connection.
436e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @return True if this is the primary database connection.
437e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
438e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    public boolean isPrimaryConnection() {
439e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return mIsPrimaryConnection;
440e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
441e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
442e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
443e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Prepares a statement for execution but does not bind its parameters or execute it.
444e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * <p>
445e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * This method can be used to check for syntax errors during compilation
446e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * prior to execution of the statement.  If the {@code outStatementInfo} argument
447e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * is not null, the provided {@link SQLiteStatementInfo} object is populated
448e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * with information about the statement.
449e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * </p><p>
450e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * A prepared statement makes no reference to the arguments that may eventually
451e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * be bound to it, consequently it it possible to cache certain prepared statements
452e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * such as SELECT or INSERT/UPDATE statements.  If the statement is cacheable,
453e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * then it will be stored in the cache for later.
454e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * </p><p>
455e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * To take advantage of this behavior as an optimization, the connection pool
456e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * provides a method to acquire a connection that already has a given SQL statement
457e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * in its prepared statement cache so that it is ready for execution.
458e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * </p>
459e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
460e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param sql The SQL statement to prepare.
461e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param outStatementInfo The {@link SQLiteStatementInfo} object to populate
462e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * with information about the statement, or null if none.
463e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
464e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @throws SQLiteException if an error occurs, such as a syntax error.
465e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
466e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    public void prepare(String sql, SQLiteStatementInfo outStatementInfo) {
467e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (sql == null) {
468e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new IllegalArgumentException("sql must not be null.");
469e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
470e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
471a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        final int cookie = mRecentOperations.beginOperation("prepare", sql, null);
472e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
473a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final PreparedStatement statement = acquirePreparedStatement(sql);
474e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            try {
475e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                if (outStatementInfo != null) {
476e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    outStatementInfo.numParameters = statement.mNumParameters;
477e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    outStatementInfo.readOnly = statement.mReadOnly;
478e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
479e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    final int columnCount = nativeGetColumnCount(
480e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            mConnectionPtr, statement.mStatementPtr);
481e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    if (columnCount == 0) {
482e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        outStatementInfo.columnNames = EMPTY_STRING_ARRAY;
483e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    } else {
484e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        outStatementInfo.columnNames = new String[columnCount];
485e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        for (int i = 0; i < columnCount; i++) {
486e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            outStatementInfo.columnNames[i] = nativeGetColumnName(
487e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                                    mConnectionPtr, statement.mStatementPtr, i);
488e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        }
489e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    }
490e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                }
491e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } finally {
492e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                releasePreparedStatement(statement);
493e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
494e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (RuntimeException ex) {
495a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.failOperation(cookie, ex);
496e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw ex;
497e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } finally {
498a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.endOperation(cookie);
499e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
500e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
501e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
502e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
503e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Executes a statement that does not return a result.
504e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
505e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param sql The SQL statement to execute.
506e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param bindArgs The arguments to bind, or null if none.
5074c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
508e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
509e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @throws SQLiteException if an error occurs, such as a syntax error
510e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * or invalid number of bind arguments.
51175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @throws OperationCanceledException if the operation was canceled.
512e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
51375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    public void execute(String sql, Object[] bindArgs,
5144c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            CancellationSignal cancellationSignal) {
515e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (sql == null) {
516e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new IllegalArgumentException("sql must not be null.");
517e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
518e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
519a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        final int cookie = mRecentOperations.beginOperation("execute", sql, bindArgs);
520e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
521a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final PreparedStatement statement = acquirePreparedStatement(sql);
522e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            try {
523e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                throwIfStatementForbidden(statement);
524e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                bindArguments(statement, bindArgs);
525e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                applyBlockGuardPolicy(statement);
5264c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                attachCancellationSignal(cancellationSignal);
52775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                try {
52875ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                    nativeExecute(mConnectionPtr, statement.mStatementPtr);
52975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                } finally {
5304c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                    detachCancellationSignal(cancellationSignal);
53175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                }
532e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } finally {
533e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                releasePreparedStatement(statement);
534e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
535e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (RuntimeException ex) {
536a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.failOperation(cookie, ex);
537e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw ex;
538e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } finally {
539a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.endOperation(cookie);
540e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
541e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
542e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
543e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
544e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Executes a statement that returns a single <code>long</code> result.
545e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
546e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param sql The SQL statement to execute.
547e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param bindArgs The arguments to bind, or null if none.
5484c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
549e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @return The value of the first column in the first row of the result set
550e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * as a <code>long</code>, or zero if none.
551e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
552e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @throws SQLiteException if an error occurs, such as a syntax error
553e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * or invalid number of bind arguments.
55475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @throws OperationCanceledException if the operation was canceled.
555e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
55675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    public long executeForLong(String sql, Object[] bindArgs,
5574c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            CancellationSignal cancellationSignal) {
558e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (sql == null) {
559e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new IllegalArgumentException("sql must not be null.");
560e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
561e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
562a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        final int cookie = mRecentOperations.beginOperation("executeForLong", sql, bindArgs);
563e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
564a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final PreparedStatement statement = acquirePreparedStatement(sql);
565e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            try {
566e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                throwIfStatementForbidden(statement);
567e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                bindArguments(statement, bindArgs);
568e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                applyBlockGuardPolicy(statement);
5694c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                attachCancellationSignal(cancellationSignal);
57075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                try {
57175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                    return nativeExecuteForLong(mConnectionPtr, statement.mStatementPtr);
57275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                } finally {
5734c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                    detachCancellationSignal(cancellationSignal);
57475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                }
575e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } finally {
576e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                releasePreparedStatement(statement);
577e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
578e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (RuntimeException ex) {
579a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.failOperation(cookie, ex);
580e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw ex;
581e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } finally {
582a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.endOperation(cookie);
583e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
584e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
585e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
586e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
587e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Executes a statement that returns a single {@link String} result.
588e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
589e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param sql The SQL statement to execute.
590e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param bindArgs The arguments to bind, or null if none.
5914c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
592e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @return The value of the first column in the first row of the result set
593e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * as a <code>String</code>, or null if none.
594e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
595e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @throws SQLiteException if an error occurs, such as a syntax error
596e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * or invalid number of bind arguments.
59775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @throws OperationCanceledException if the operation was canceled.
598e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
59975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    public String executeForString(String sql, Object[] bindArgs,
6004c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            CancellationSignal cancellationSignal) {
601e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (sql == null) {
602e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new IllegalArgumentException("sql must not be null.");
603e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
604e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
605a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        final int cookie = mRecentOperations.beginOperation("executeForString", sql, bindArgs);
606e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
607a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final PreparedStatement statement = acquirePreparedStatement(sql);
608e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            try {
609e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                throwIfStatementForbidden(statement);
610e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                bindArguments(statement, bindArgs);
611e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                applyBlockGuardPolicy(statement);
6124c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                attachCancellationSignal(cancellationSignal);
61375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                try {
61475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                    return nativeExecuteForString(mConnectionPtr, statement.mStatementPtr);
61575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                } finally {
6164c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                    detachCancellationSignal(cancellationSignal);
61775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                }
618e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } finally {
619e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                releasePreparedStatement(statement);
620e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
621e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (RuntimeException ex) {
622a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.failOperation(cookie, ex);
623e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw ex;
624e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } finally {
625a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.endOperation(cookie);
626e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
627e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
628e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
629e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
630e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Executes a statement that returns a single BLOB result as a
631e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * file descriptor to a shared memory region.
632e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
633e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param sql The SQL statement to execute.
634e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param bindArgs The arguments to bind, or null if none.
6354c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
636e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @return The file descriptor for a shared memory region that contains
637e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * the value of the first column in the first row of the result set as a BLOB,
638e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * or null if none.
639e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
640e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @throws SQLiteException if an error occurs, such as a syntax error
641e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * or invalid number of bind arguments.
64275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @throws OperationCanceledException if the operation was canceled.
643e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
64475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
6454c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            CancellationSignal cancellationSignal) {
646e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (sql == null) {
647e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new IllegalArgumentException("sql must not be null.");
648e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
649e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
650a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        final int cookie = mRecentOperations.beginOperation("executeForBlobFileDescriptor",
651a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                sql, bindArgs);
652e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
653a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final PreparedStatement statement = acquirePreparedStatement(sql);
654e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            try {
655e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                throwIfStatementForbidden(statement);
656e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                bindArguments(statement, bindArgs);
657e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                applyBlockGuardPolicy(statement);
6584c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                attachCancellationSignal(cancellationSignal);
65975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                try {
66075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                    int fd = nativeExecuteForBlobFileDescriptor(
66175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                            mConnectionPtr, statement.mStatementPtr);
66275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                    return fd >= 0 ? ParcelFileDescriptor.adoptFd(fd) : null;
66375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                } finally {
6644c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                    detachCancellationSignal(cancellationSignal);
66575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                }
666e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } finally {
667e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                releasePreparedStatement(statement);
668e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
669e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (RuntimeException ex) {
670a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.failOperation(cookie, ex);
671e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw ex;
672e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } finally {
673a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.endOperation(cookie);
674e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
675e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
676e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
677e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
678e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Executes a statement that returns a count of the number of rows
679e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * that were changed.  Use for UPDATE or DELETE SQL statements.
680e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
681e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param sql The SQL statement to execute.
682e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param bindArgs The arguments to bind, or null if none.
6834c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
684e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @return The number of rows that were changed.
685e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
686e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @throws SQLiteException if an error occurs, such as a syntax error
687e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * or invalid number of bind arguments.
68875ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @throws OperationCanceledException if the operation was canceled.
689e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
69075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    public int executeForChangedRowCount(String sql, Object[] bindArgs,
6914c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            CancellationSignal cancellationSignal) {
692e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (sql == null) {
693e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new IllegalArgumentException("sql must not be null.");
694e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
695e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
696a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        final int cookie = mRecentOperations.beginOperation("executeForChangedRowCount",
697a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                sql, bindArgs);
698e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
699a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final PreparedStatement statement = acquirePreparedStatement(sql);
700e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            try {
701e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                throwIfStatementForbidden(statement);
702e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                bindArguments(statement, bindArgs);
703e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                applyBlockGuardPolicy(statement);
7044c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                attachCancellationSignal(cancellationSignal);
70575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                try {
70675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                    return nativeExecuteForChangedRowCount(
70775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                            mConnectionPtr, statement.mStatementPtr);
70875ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                } finally {
7094c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                    detachCancellationSignal(cancellationSignal);
71075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                }
711e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } finally {
712e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                releasePreparedStatement(statement);
713e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
714e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (RuntimeException ex) {
715a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.failOperation(cookie, ex);
716e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw ex;
717e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } finally {
718a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.endOperation(cookie);
719e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
720e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
721e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
722e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
723e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Executes a statement that returns the row id of the last row inserted
724e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * by the statement.  Use for INSERT SQL statements.
725e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
726e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param sql The SQL statement to execute.
727e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param bindArgs The arguments to bind, or null if none.
7284c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
729e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @return The row id of the last row that was inserted, or 0 if none.
730e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
731e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @throws SQLiteException if an error occurs, such as a syntax error
732e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * or invalid number of bind arguments.
73375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @throws OperationCanceledException if the operation was canceled.
734e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
73575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    public long executeForLastInsertedRowId(String sql, Object[] bindArgs,
7364c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            CancellationSignal cancellationSignal) {
737e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (sql == null) {
738e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new IllegalArgumentException("sql must not be null.");
739e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
740e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
741a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        final int cookie = mRecentOperations.beginOperation("executeForLastInsertedRowId",
742a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                sql, bindArgs);
743e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
744a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final PreparedStatement statement = acquirePreparedStatement(sql);
745e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            try {
746e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                throwIfStatementForbidden(statement);
747e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                bindArguments(statement, bindArgs);
748e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                applyBlockGuardPolicy(statement);
7494c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                attachCancellationSignal(cancellationSignal);
75075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                try {
75175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                    return nativeExecuteForLastInsertedRowId(
75275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                            mConnectionPtr, statement.mStatementPtr);
75375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                } finally {
7544c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                    detachCancellationSignal(cancellationSignal);
75575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                }
756e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } finally {
757e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                releasePreparedStatement(statement);
758e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
759e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (RuntimeException ex) {
760a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.failOperation(cookie, ex);
761e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw ex;
762e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } finally {
763a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mRecentOperations.endOperation(cookie);
764e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
765e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
766e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
767e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
768e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Executes a statement and populates the specified {@link CursorWindow}
769e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * with a range of results.  Returns the number of rows that were counted
770e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * during query execution.
771e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
772e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param sql The SQL statement to execute.
773e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param bindArgs The arguments to bind, or null if none.
774e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param window The cursor window to clear and fill.
775e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param startPos The start position for filling the window.
776e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param requiredPos The position of a row that MUST be in the window.
777e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * If it won't fit, then the query should discard part of what it filled
778e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * so that it does.  Must be greater than or equal to <code>startPos</code>.
779e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param countAllRows True to count all rows that the query would return
780e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * regagless of whether they fit in the window.
7814c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
782e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @return The number of rows that were counted during query execution.  Might
783e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * not be all rows in the result set unless <code>countAllRows</code> is true.
784e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
785e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @throws SQLiteException if an error occurs, such as a syntax error
786e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * or invalid number of bind arguments.
78775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @throws OperationCanceledException if the operation was canceled.
788e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
789e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    public int executeForCursorWindow(String sql, Object[] bindArgs,
79075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown            CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
7914c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            CancellationSignal cancellationSignal) {
792e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (sql == null) {
793e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new IllegalArgumentException("sql must not be null.");
794e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
795e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (window == null) {
796e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new IllegalArgumentException("window must not be null.");
797e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
798e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
79903bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown        window.acquireReference();
800e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
80103bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown            int actualPos = -1;
80203bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown            int countedRows = -1;
80303bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown            int filledRows = -1;
80403bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown            final int cookie = mRecentOperations.beginOperation("executeForCursorWindow",
80503bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                    sql, bindArgs);
806e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            try {
80703bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                final PreparedStatement statement = acquirePreparedStatement(sql);
80875ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                try {
80903bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                    throwIfStatementForbidden(statement);
81003bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                    bindArguments(statement, bindArgs);
81103bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                    applyBlockGuardPolicy(statement);
81203bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                    attachCancellationSignal(cancellationSignal);
81303bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                    try {
81403bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                        final long result = nativeExecuteForCursorWindow(
81503bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                                mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
81603bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                                startPos, requiredPos, countAllRows);
81703bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                        actualPos = (int)(result >> 32);
81803bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                        countedRows = (int)result;
81903bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                        filledRows = window.getNumRows();
82003bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                        window.setStartPosition(actualPos);
82103bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                        return countedRows;
82203bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                    } finally {
82303bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                        detachCancellationSignal(cancellationSignal);
82403bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                    }
82575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                } finally {
82603bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                    releasePreparedStatement(statement);
82775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                }
82803bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown            } catch (RuntimeException ex) {
82903bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                mRecentOperations.failOperation(cookie, ex);
83003bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                throw ex;
831e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } finally {
83203bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                if (mRecentOperations.endOperationDeferLog(cookie)) {
83303bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                    mRecentOperations.logOperation(cookie, "window='" + window
83403bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                            + "', startPos=" + startPos
83503bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                            + ", actualPos=" + actualPos
83603bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                            + ", filledRows=" + filledRows
83703bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                            + ", countedRows=" + countedRows);
83803bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown                }
839e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
840e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } finally {
84103bd302aebbb77f4f95789a269c8a5463ac5a840Jeff Brown            window.releaseReference();
842e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
843e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
844e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
845e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private PreparedStatement acquirePreparedStatement(String sql) {
846e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        PreparedStatement statement = mPreparedStatementCache.get(sql);
847a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        boolean skipCache = false;
848e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (statement != null) {
849a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            if (!statement.mInUse) {
850a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                return statement;
851a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            }
852a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            // The statement is already in the cache but is in use (this statement appears
853a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            // to be not only re-entrant but recursive!).  So prepare a new copy of the
854a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            // statement but do not cache it.
855a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            skipCache = true;
856e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
857e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
858e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        final int statementPtr = nativePrepareStatement(mConnectionPtr, sql);
859e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
860e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            final int numParameters = nativeGetParameterCount(mConnectionPtr, statementPtr);
861e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            final int type = DatabaseUtils.getSqlStatementType(sql);
862e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            final boolean readOnly = nativeIsReadOnly(mConnectionPtr, statementPtr);
863e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            statement = obtainPreparedStatement(sql, statementPtr, numParameters, type, readOnly);
864a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            if (!skipCache && isCacheable(type)) {
865e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mPreparedStatementCache.put(sql, statement);
866e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                statement.mInCache = true;
867e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
868e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (RuntimeException ex) {
869e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            // Finalize the statement if an exception occurred and we did not add
870e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            // it to the cache.  If it is already in the cache, then leave it there.
871e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (statement == null || !statement.mInCache) {
872e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                nativeFinalizeStatement(mConnectionPtr, statementPtr);
873e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
874e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw ex;
875e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
876a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        statement.mInUse = true;
877e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return statement;
878e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
879e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
880e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private void releasePreparedStatement(PreparedStatement statement) {
881a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        statement.mInUse = false;
882e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (statement.mInCache) {
883e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            try {
884e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                nativeResetStatementAndClearBindings(mConnectionPtr, statement.mStatementPtr);
885e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } catch (SQLiteException ex) {
886a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                // The statement could not be reset due to an error.  Remove it from the cache.
887a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                // When remove() is called, the cache will invoke its entryRemoved() callback,
888a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                // which will in turn call finalizePreparedStatement() to finalize and
889a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                // recycle the statement.
8902a293b61cb0efbf24994d74ed980f58b820bb35aJeff Brown                if (DEBUG) {
8912a293b61cb0efbf24994d74ed980f58b820bb35aJeff Brown                    Log.d(TAG, "Could not reset prepared statement due to an exception.  "
892e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            + "Removing it from the cache.  SQL: "
893e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            + trimSqlForDisplay(statement.mSql), ex);
894e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                }
8952a293b61cb0efbf24994d74ed980f58b820bb35aJeff Brown
896e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mPreparedStatementCache.remove(statement.mSql);
897e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
898e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } else {
899a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            finalizePreparedStatement(statement);
900e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
901e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
902e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
903a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown    private void finalizePreparedStatement(PreparedStatement statement) {
904a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        nativeFinalizeStatement(mConnectionPtr, statement.mStatementPtr);
905a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        recyclePreparedStatement(statement);
906a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown    }
907a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown
9084c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown    private void attachCancellationSignal(CancellationSignal cancellationSignal) {
9094c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown        if (cancellationSignal != null) {
9104c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            cancellationSignal.throwIfCanceled();
91175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown
9124c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            mCancellationSignalAttachCount += 1;
9134c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            if (mCancellationSignalAttachCount == 1) {
9144c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                // Reset cancellation flag before executing the statement.
91575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                nativeResetCancel(mConnectionPtr, true /*cancelable*/);
91675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown
91775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                // After this point, onCancel() may be called concurrently.
9184c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                cancellationSignal.setOnCancelListener(this);
91975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown            }
92075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown        }
92175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    }
92275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown
9234c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown    private void detachCancellationSignal(CancellationSignal cancellationSignal) {
9244c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown        if (cancellationSignal != null) {
9254c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            assert mCancellationSignalAttachCount > 0;
92675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown
9274c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            mCancellationSignalAttachCount -= 1;
9284c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            if (mCancellationSignalAttachCount == 0) {
92975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                // After this point, onCancel() cannot be called concurrently.
9304c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                cancellationSignal.setOnCancelListener(null);
93175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown
9324c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                // Reset cancellation flag after executing the statement.
93375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                nativeResetCancel(mConnectionPtr, false /*cancelable*/);
93475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown            }
93575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown        }
93675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    }
93775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown
9384c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown    // CancellationSignal.OnCancelListener callback.
93975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    // This method may be called on a different thread than the executing statement.
9404c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown    // However, it will only be called between calls to attachCancellationSignal and
9414c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown    // detachCancellationSignal, while a statement is executing.  We can safely assume
94275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    // that the SQLite connection is still alive.
94375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    @Override
94475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    public void onCancel() {
94575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown        nativeCancel(mConnectionPtr);
94675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    }
94775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown
948e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private void bindArguments(PreparedStatement statement, Object[] bindArgs) {
949e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        final int count = bindArgs != null ? bindArgs.length : 0;
950e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (count != statement.mNumParameters) {
951e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new SQLiteBindOrColumnIndexOutOfRangeException(
952e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    "Expected " + statement.mNumParameters + " bind arguments but "
953e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    + bindArgs.length + " were provided.");
954e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
955e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (count == 0) {
956e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            return;
957e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
958e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
959e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        final int statementPtr = statement.mStatementPtr;
960e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        for (int i = 0; i < count; i++) {
961e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            final Object arg = bindArgs[i];
962e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            switch (DatabaseUtils.getTypeOfObject(arg)) {
963e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                case Cursor.FIELD_TYPE_NULL:
964e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    nativeBindNull(mConnectionPtr, statementPtr, i + 1);
965e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    break;
966e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                case Cursor.FIELD_TYPE_INTEGER:
967e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    nativeBindLong(mConnectionPtr, statementPtr, i + 1,
968e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            ((Number)arg).longValue());
969e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    break;
970e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                case Cursor.FIELD_TYPE_FLOAT:
971e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    nativeBindDouble(mConnectionPtr, statementPtr, i + 1,
972e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            ((Number)arg).doubleValue());
973e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    break;
974e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                case Cursor.FIELD_TYPE_BLOB:
975e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    nativeBindBlob(mConnectionPtr, statementPtr, i + 1, (byte[])arg);
976e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    break;
977e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                case Cursor.FIELD_TYPE_STRING:
978e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                default:
979e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    if (arg instanceof Boolean) {
980e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        // Provide compatibility with legacy applications which may pass
981e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        // Boolean values in bind args.
982e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        nativeBindLong(mConnectionPtr, statementPtr, i + 1,
983e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                                ((Boolean)arg).booleanValue() ? 1 : 0);
984e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    } else {
985e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        nativeBindString(mConnectionPtr, statementPtr, i + 1, arg.toString());
986e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    }
987e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    break;
988e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
989e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
990e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
991e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
992e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private void throwIfStatementForbidden(PreparedStatement statement) {
993e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (mOnlyAllowReadOnlyOperations && !statement.mReadOnly) {
994e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new SQLiteException("Cannot execute this statement because it "
995e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    + "might modify the database but the connection is read-only.");
996e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
997e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
998e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
999e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static boolean isCacheable(int statementType) {
1000e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (statementType == DatabaseUtils.STATEMENT_UPDATE
1001e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                || statementType == DatabaseUtils.STATEMENT_SELECT) {
1002e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            return true;
1003e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1004e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return false;
1005e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1006e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1007e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private void applyBlockGuardPolicy(PreparedStatement statement) {
1008e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (!mConfiguration.isInMemoryDb()) {
1009e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (statement.mReadOnly) {
1010e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                BlockGuard.getThreadPolicy().onReadFromDisk();
1011e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } else {
1012e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                BlockGuard.getThreadPolicy().onWriteToDisk();
1013e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1014e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1015e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1016e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1017e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
1018e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Dumps debugging information about this connection.
1019e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
1020e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param printer The printer to receive the dump, not null.
1021a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown     * @param verbose True to dump more verbose information.
1022e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
1023a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown    public void dump(Printer printer, boolean verbose) {
1024a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        dumpUnsafe(printer, verbose);
1025e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1026e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1027e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
1028e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Dumps debugging information about this connection, in the case where the
1029e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * caller might not actually own the connection.
1030e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
1031e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * This function is written so that it may be called by a thread that does not
1032e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * own the connection.  We need to be very careful because the connection state is
1033e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * not synchronized.
1034e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
1035e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * At worst, the method may return stale or slightly wrong data, however
1036e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * it should not crash.  This is ok as it is only used for diagnostic purposes.
1037e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
1038e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param printer The printer to receive the dump, not null.
1039a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown     * @param verbose True to dump more verbose information.
1040e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
1041a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown    void dumpUnsafe(Printer printer, boolean verbose) {
1042e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        printer.println("Connection #" + mConnectionId + ":");
1043a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        if (verbose) {
1044a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            printer.println("  connectionPtr: 0x" + Integer.toHexString(mConnectionPtr));
1045a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        }
1046e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        printer.println("  isPrimaryConnection: " + mIsPrimaryConnection);
1047e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        printer.println("  onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations);
1048e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1049e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mRecentOperations.dump(printer);
1050a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown
1051a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        if (verbose) {
1052a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            mPreparedStatementCache.dump(printer);
1053a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        }
1054e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1055e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1056e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
1057e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Describes the currently executing operation, in the case where the
1058e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * caller might not actually own the connection.
1059e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
1060e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * This function is written so that it may be called by a thread that does not
1061e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * own the connection.  We need to be very careful because the connection state is
1062e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * not synchronized.
1063e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
1064e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * At worst, the method may return stale or slightly wrong data, however
1065e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * it should not crash.  This is ok as it is only used for diagnostic purposes.
1066e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
1067e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @return A description of the current operation including how long it has been running,
1068e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * or null if none.
1069e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
1070e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    String describeCurrentOperationUnsafe() {
1071e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return mRecentOperations.describeCurrentOperation();
1072e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1073e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1074e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
1075e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Collects statistics about database connection memory usage.
1076e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
1077e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param dbStatsList The list to populate.
1078e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
1079e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    void collectDbStats(ArrayList<DbStats> dbStatsList) {
1080e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // Get information about the main database.
1081e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        int lookaside = nativeGetDbLookaside(mConnectionPtr);
1082e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        long pageCount = 0;
1083e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        long pageSize = 0;
1084e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
108575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown            pageCount = executeForLong("PRAGMA page_count;", null, null);
108675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown            pageSize = executeForLong("PRAGMA page_size;", null, null);
1087e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (SQLiteException ex) {
1088e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            // Ignore.
1089e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1090e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        dbStatsList.add(getMainDbStatsUnsafe(lookaside, pageCount, pageSize));
1091e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1092e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // Get information about attached databases.
1093e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // We ignore the first row in the database list because it corresponds to
1094e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // the main database which we have already described.
1095e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        CursorWindow window = new CursorWindow("collectDbStats");
1096e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        try {
109775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown            executeForCursorWindow("PRAGMA database_list;", null, window, 0, 0, false, null);
1098e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            for (int i = 1; i < window.getNumRows(); i++) {
1099e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                String name = window.getString(i, 1);
1100e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                String path = window.getString(i, 2);
1101e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                pageCount = 0;
1102e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                pageSize = 0;
1103e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                try {
110475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                    pageCount = executeForLong("PRAGMA " + name + ".page_count;", null, null);
110575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                    pageSize = executeForLong("PRAGMA " + name + ".page_size;", null, null);
1106e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                } catch (SQLiteException ex) {
1107e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    // Ignore.
1108e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                }
1109e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                String label = "  (attached) " + name;
1110e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                if (!path.isEmpty()) {
1111e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    label += ": " + path;
1112e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                }
1113e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                dbStatsList.add(new DbStats(label, pageCount, pageSize, 0, 0, 0, 0));
1114e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1115e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } catch (SQLiteException ex) {
1116e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            // Ignore.
1117e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } finally {
1118e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            window.close();
1119e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1120e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1121e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1122e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
1123e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Collects statistics about database connection memory usage, in the case where the
1124e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * caller might not actually own the connection.
1125e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
1126e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @return The statistics object, never null.
1127e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
1128e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    void collectDbStatsUnsafe(ArrayList<DbStats> dbStatsList) {
1129e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        dbStatsList.add(getMainDbStatsUnsafe(0, 0, 0));
1130e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1131e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1132e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private DbStats getMainDbStatsUnsafe(int lookaside, long pageCount, long pageSize) {
1133e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // The prepared statement cache is thread-safe so we can access its statistics
1134e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // even if we do not own the database connection.
1135e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        String label = mConfiguration.path;
1136e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (!mIsPrimaryConnection) {
1137e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            label += " (" + mConnectionId + ")";
1138e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1139e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return new DbStats(label, pageCount, pageSize, lookaside,
1140e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mPreparedStatementCache.hitCount(),
1141e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mPreparedStatementCache.missCount(),
1142e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mPreparedStatementCache.size());
1143e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1144e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1145e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    @Override
1146e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    public String toString() {
1147e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return "SQLiteConnection: " + mConfiguration.path + " (" + mConnectionId + ")";
1148e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1149e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1150e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private PreparedStatement obtainPreparedStatement(String sql, int statementPtr,
1151e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            int numParameters, int type, boolean readOnly) {
1152e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        PreparedStatement statement = mPreparedStatementPool;
1153e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (statement != null) {
1154e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            mPreparedStatementPool = statement.mPoolNext;
1155e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            statement.mPoolNext = null;
1156e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            statement.mInCache = false;
1157e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } else {
1158e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            statement = new PreparedStatement();
1159e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1160e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        statement.mSql = sql;
1161e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        statement.mStatementPtr = statementPtr;
1162e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        statement.mNumParameters = numParameters;
1163e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        statement.mType = type;
1164e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        statement.mReadOnly = readOnly;
1165e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return statement;
1166e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1167e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1168e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private void recyclePreparedStatement(PreparedStatement statement) {
1169e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        statement.mSql = null;
1170e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        statement.mPoolNext = mPreparedStatementPool;
1171e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mPreparedStatementPool = statement;
1172e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1173e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1174e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static String trimSqlForDisplay(String sql) {
1175e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return TRIM_SQL_PATTERN.matcher(sql).replaceAll(" ");
1176e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1177e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1178e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
1179e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Holder type for a prepared statement.
1180e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     *
1181e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Although this object holds a pointer to a native statement object, it
1182e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * does not have a finalizer.  This is deliberate.  The {@link SQLiteConnection}
1183e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * owns the statement object and will take care of freeing it when needed.
1184e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * In particular, closing the connection requires a guarantee of deterministic
1185e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * resource disposal because all native statement objects must be freed before
1186e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * the native database object can be closed.  So no finalizers here.
1187e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
1188e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static final class PreparedStatement {
1189e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // Next item in pool.
1190e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public PreparedStatement mPoolNext;
1191e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1192e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // The SQL from which the statement was prepared.
1193e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public String mSql;
1194e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1195e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // The native sqlite3_stmt object pointer.
1196e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // Lifetime is managed explicitly by the connection.
1197e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public int mStatementPtr;
1198e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1199e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // The number of parameters that the prepared statement has.
1200e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public int mNumParameters;
1201e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1202e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // The statement type.
1203e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public int mType;
1204e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1205e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // True if the statement is read-only.
1206e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public boolean mReadOnly;
1207e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1208e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        // True if the statement is in the cache.
1209e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public boolean mInCache;
1210a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown
1211a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        // True if the statement is in use (currently executing).
1212a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        // We need this flag because due to the use of custom functions in triggers, it's
1213a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        // possible for SQLite calls to be re-entrant.  Consequently we need to prevent
1214a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        // in use statements from being finalized until they are no longer in use.
1215a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        public boolean mInUse;
1216e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1217e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1218e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final class PreparedStatementCache
1219e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            extends LruCache<String, PreparedStatement> {
1220e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public PreparedStatementCache(int size) {
1221e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            super(size);
1222e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1223e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1224e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        @Override
1225e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        protected void entryRemoved(boolean evicted, String key,
1226e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                PreparedStatement oldValue, PreparedStatement newValue) {
1227e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            oldValue.mInCache = false;
1228a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            if (!oldValue.mInUse) {
1229a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                finalizePreparedStatement(oldValue);
1230a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            }
1231e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1232e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1233e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public void dump(Printer printer) {
1234e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            printer.println("  Prepared statement cache:");
1235e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            Map<String, PreparedStatement> cache = snapshot();
1236e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (!cache.isEmpty()) {
1237e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                int i = 0;
1238e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                for (Map.Entry<String, PreparedStatement> entry : cache.entrySet()) {
1239e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    PreparedStatement statement = entry.getValue();
1240e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    if (statement.mInCache) { // might be false due to a race with entryRemoved
1241e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        String sql = entry.getKey();
1242e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        printer.println("    " + i + ": statementPtr=0x"
1243e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                                + Integer.toHexString(statement.mStatementPtr)
1244e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                                + ", numParameters=" + statement.mNumParameters
1245e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                                + ", type=" + statement.mType
1246e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                                + ", readOnly=" + statement.mReadOnly
1247e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                                + ", sql=\"" + trimSqlForDisplay(sql) + "\"");
1248e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    }
1249e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    i += 1;
1250e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                }
1251e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } else {
1252e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                printer.println("    <none>");
1253e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1254e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1255e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1256e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1257e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static final class OperationLog {
12582a293b61cb0efbf24994d74ed980f58b820bb35aJeff Brown        private static final int MAX_RECENT_OPERATIONS = 20;
1259a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        private static final int COOKIE_GENERATION_SHIFT = 8;
1260a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        private static final int COOKIE_INDEX_MASK = 0xff;
1261e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1262e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        private final Operation[] mOperations = new Operation[MAX_RECENT_OPERATIONS];
1263e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        private int mIndex;
1264a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        private int mGeneration;
1265e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1266a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        public int beginOperation(String kind, String sql, Object[] bindArgs) {
1267e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            synchronized (mOperations) {
1268e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS;
1269e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                Operation operation = mOperations[index];
1270e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                if (operation == null) {
1271e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    operation = new Operation();
1272e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    mOperations[index] = operation;
1273e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                } else {
1274e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    operation.mFinished = false;
1275e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    operation.mException = null;
1276e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    if (operation.mBindArgs != null) {
1277e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        operation.mBindArgs.clear();
1278e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    }
1279e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                }
1280e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                operation.mStartTime = System.currentTimeMillis();
1281e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                operation.mKind = kind;
1282e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                operation.mSql = sql;
1283e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                if (bindArgs != null) {
1284e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    if (operation.mBindArgs == null) {
1285e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        operation.mBindArgs = new ArrayList<Object>();
1286e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    } else {
1287e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        operation.mBindArgs.clear();
1288e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    }
1289e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    for (int i = 0; i < bindArgs.length; i++) {
1290e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        final Object arg = bindArgs[i];
1291e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        if (arg != null && arg instanceof byte[]) {
1292e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            // Don't hold onto the real byte array longer than necessary.
1293e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            operation.mBindArgs.add(EMPTY_BYTE_ARRAY);
1294e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        } else {
1295e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            operation.mBindArgs.add(arg);
1296e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        }
1297e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    }
1298e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                }
1299a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                operation.mCookie = newOperationCookieLocked(index);
1300e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mIndex = index;
1301a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                return operation.mCookie;
1302e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1303e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1304e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1305a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        public void failOperation(int cookie, Exception ex) {
1306e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            synchronized (mOperations) {
1307a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                final Operation operation = getOperationLocked(cookie);
1308a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                if (operation != null) {
1309a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                    operation.mException = ex;
1310a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                }
1311e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1312e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1313e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1314a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        public void endOperation(int cookie) {
1315e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            synchronized (mOperations) {
1316a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                if (endOperationDeferLogLocked(cookie)) {
1317a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                    logOperationLocked(cookie, null);
1318a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                }
1319e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1320e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1321e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1322a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        public boolean endOperationDeferLog(int cookie) {
1323a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            synchronized (mOperations) {
1324a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                return endOperationDeferLogLocked(cookie);
1325a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            }
1326e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1327e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1328a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        public void logOperation(int cookie, String detail) {
1329e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            synchronized (mOperations) {
1330a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                logOperationLocked(cookie, detail);
1331e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1332e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1333e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1334a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        private boolean endOperationDeferLogLocked(int cookie) {
1335a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final Operation operation = getOperationLocked(cookie);
1336a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            if (operation != null) {
1337a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                operation.mEndTime = System.currentTimeMillis();
1338a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                operation.mFinished = true;
1339a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
1340a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown                                operation.mEndTime - operation.mStartTime);
1341e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1342a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            return false;
1343e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1344e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1345a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        private void logOperationLocked(int cookie, String detail) {
1346a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final Operation operation = getOperationLocked(cookie);
1347e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            StringBuilder msg = new StringBuilder();
1348e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            operation.describe(msg);
1349e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (detail != null) {
1350e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                msg.append(", ").append(detail);
1351e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1352e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            Log.d(TAG, msg.toString());
1353e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1354e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1355a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        private int newOperationCookieLocked(int index) {
1356a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final int generation = mGeneration++;
1357a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            return generation << COOKIE_GENERATION_SHIFT | index;
1358a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        }
1359a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown
1360a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        private Operation getOperationLocked(int cookie) {
1361a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final int index = cookie & COOKIE_INDEX_MASK;
1362a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            final Operation operation = mOperations[index];
1363a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown            return operation.mCookie == cookie ? operation : null;
1364a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        }
1365a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown
1366e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public String describeCurrentOperation() {
1367e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            synchronized (mOperations) {
1368e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                final Operation operation = mOperations[mIndex];
1369e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                if (operation != null && !operation.mFinished) {
1370e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    StringBuilder msg = new StringBuilder();
1371e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    operation.describe(msg);
1372e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    return msg.toString();
1373e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                }
1374e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                return null;
1375e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1376e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1377e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1378e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public void dump(Printer printer) {
1379e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            synchronized (mOperations) {
1380e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                printer.println("  Most recently executed operations:");
1381e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                int index = mIndex;
1382e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                Operation operation = mOperations[index];
1383e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                if (operation != null) {
1384e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    int n = 0;
1385e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    do {
1386e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        StringBuilder msg = new StringBuilder();
1387e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        msg.append("    ").append(n).append(": [");
1388e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        msg.append(operation.getFormattedStartTime());
1389e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        msg.append("] ");
1390e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        operation.describe(msg);
1391e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        printer.println(msg.toString());
1392e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1393e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        if (index > 0) {
1394e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            index -= 1;
1395e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        } else {
1396e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                            index = MAX_RECENT_OPERATIONS - 1;
1397e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        }
1398e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        n += 1;
1399e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        operation = mOperations[index];
1400e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    } while (operation != null && n < MAX_RECENT_OPERATIONS);
1401e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                } else {
1402e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    printer.println("    <none>");
1403e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                }
1404e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1405e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1406e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1407e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1408e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static final class Operation {
1409e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        private static final SimpleDateFormat sDateFormat =
1410e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
1411e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1412e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public long mStartTime;
1413e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public long mEndTime;
1414e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public String mKind;
1415e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public String mSql;
1416e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public ArrayList<Object> mBindArgs;
1417e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public boolean mFinished;
1418e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public Exception mException;
1419a9be4154e8dac0de3db5ee42e878beb0639e70e6Jeff Brown        public int mCookie;
1420e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1421e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        public void describe(StringBuilder msg) {
1422e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            msg.append(mKind);
1423e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (mFinished) {
1424e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                msg.append(" took ").append(mEndTime - mStartTime).append("ms");
1425e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            } else {
1426e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                msg.append(" started ").append(System.currentTimeMillis() - mStartTime)
1427e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        .append("ms ago");
1428e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1429e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            msg.append(" - ").append(getStatus());
1430e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (mSql != null) {
1431e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                msg.append(", sql=\"").append(trimSqlForDisplay(mSql)).append("\"");
1432e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1433e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (mBindArgs != null && mBindArgs.size() != 0) {
1434e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                msg.append(", bindArgs=[");
1435e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                final int count = mBindArgs.size();
1436e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                for (int i = 0; i < count; i++) {
1437e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    final Object arg = mBindArgs.get(i);
1438e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    if (i != 0) {
1439e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        msg.append(", ");
1440e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    }
1441e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    if (arg == null) {
1442e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        msg.append("null");
1443e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    } else if (arg instanceof byte[]) {
1444e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        msg.append("<byte[]>");
1445e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    } else if (arg instanceof String) {
1446e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        msg.append("\"").append((String)arg).append("\"");
1447e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    } else {
1448e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                        msg.append(arg);
1449e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    }
1450e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                }
1451e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                msg.append("]");
1452e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1453e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (mException != null) {
1454e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                msg.append(", exception=\"").append(mException.getMessage()).append("\"");
1455e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1456e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1457e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1458e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        private String getStatus() {
1459e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            if (!mFinished) {
1460e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                return "running";
1461e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
1462e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            return mException != null ? "failed" : "succeeded";
1463e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1464e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
1465e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        private String getFormattedStartTime() {
1466e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            return sDateFormat.format(new Date(mStartTime));
1467e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
1468e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
1469e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown}
1470