19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.database.sqlite;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19ce38b98feb1e7c9c1799eb270c40798d833aa9aeVasu Noriimport android.database.DatabaseUtils;
20a7771df3696954f0e279407e8894a916a7cb26ccJeff Brownimport android.os.CancellationSignal;
21e25539fdfdf884eee55107efbcc893f44e82e00eVasu Nori
22e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brownimport java.util.Arrays;
233695709457c66354261502b8bf23d59604a59ce4Vasu Nori
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * A base class for compiled SQLite programs.
26e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * <p>
27e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * This class is not thread-safe.
28e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown * </p>
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class SQLiteProgram extends SQLiteClosable {
31e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private static final String[] EMPTY_STRING_ARRAY = new String[0];
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
33e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final SQLiteDatabase mDatabase;
34e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final String mSql;
35e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final boolean mReadOnly;
36e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final String[] mColumnNames;
37e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final int mNumParameters;
38e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private final Object[] mBindArgs;
393695709457c66354261502b8bf23d59604a59ce4Vasu Nori
4075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
414c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            CancellationSignal cancellationSignalForPrepare) {
42e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mDatabase = db;
43d606b4bf2c1a2308b40785860853cfb95a77bf58Vasu Nori        mSql = sql.trim();
44e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
454e874edf69ce9900eb847629dc4d3616972a3468Vasu Nori        int n = DatabaseUtils.getSqlStatementType(mSql);
464e874edf69ce9900eb847629dc4d3616972a3468Vasu Nori        switch (n) {
474e874edf69ce9900eb847629dc4d3616972a3468Vasu Nori            case DatabaseUtils.STATEMENT_BEGIN:
484e874edf69ce9900eb847629dc4d3616972a3468Vasu Nori            case DatabaseUtils.STATEMENT_COMMIT:
494e874edf69ce9900eb847629dc4d3616972a3468Vasu Nori            case DatabaseUtils.STATEMENT_ABORT:
50e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mReadOnly = false;
51e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mColumnNames = EMPTY_STRING_ARRAY;
52e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mNumParameters = 0;
534e874edf69ce9900eb847629dc4d3616972a3468Vasu Nori                break;
54e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
554e874edf69ce9900eb847629dc4d3616972a3468Vasu Nori            default:
56e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                boolean assumeReadOnly = (n == DatabaseUtils.STATEMENT_SELECT);
57e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                SQLiteStatementInfo info = new SQLiteStatementInfo();
58e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                db.getThreadSession().prepare(mSql,
5975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                        db.getThreadDefaultConnectionFlags(assumeReadOnly),
604c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                        cancellationSignalForPrepare, info);
61e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mReadOnly = info.readOnly;
62e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mColumnNames = info.columnNames;
63e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                mNumParameters = info.numParameters;
64e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                break;
652827d6d974beabb12344040a002dcb52dd7106b5Vasu Nori        }
667501010b71d57264a06f82937f5fb29cb9f4b509Vasu Nori
67b2679481b57d87945df02983f95ff8e6c9ba5928Jeff Brown        if (bindArgs != null && bindArgs.length > mNumParameters) {
68b2679481b57d87945df02983f95ff8e6c9ba5928Jeff Brown            throw new IllegalArgumentException("Too many bind arguments.  "
69b2679481b57d87945df02983f95ff8e6c9ba5928Jeff Brown                    + bindArgs.length + " arguments were provided but the statement needs "
70b2679481b57d87945df02983f95ff8e6c9ba5928Jeff Brown                    + mNumParameters + " arguments.");
71b2679481b57d87945df02983f95ff8e6c9ba5928Jeff Brown        }
72b2679481b57d87945df02983f95ff8e6c9ba5928Jeff Brown
73e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (mNumParameters != 0) {
74e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            mBindArgs = new Object[mNumParameters];
75b2679481b57d87945df02983f95ff8e6c9ba5928Jeff Brown            if (bindArgs != null) {
76b2679481b57d87945df02983f95ff8e6c9ba5928Jeff Brown                System.arraycopy(bindArgs, 0, mBindArgs, 0, bindArgs.length);
77b2679481b57d87945df02983f95ff8e6c9ba5928Jeff Brown            }
78e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        } else {
79e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            mBindArgs = null;
80b729dcc8a94bc2c2a1ecda47d791be0d6f1d160aVasu Nori        }
81b729dcc8a94bc2c2a1ecda47d791be0d6f1d160aVasu Nori    }
82b729dcc8a94bc2c2a1ecda47d791be0d6f1d160aVasu Nori
83e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    final SQLiteDatabase getDatabase() {
84e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return mDatabase;
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
865a03f36ef845f73eb4473193dbb0f93dd12a51afVasu Nori
87e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    final String getSql() {
88e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return mSql;
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
91e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    final Object[] getBindArgs() {
92e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return mBindArgs;
93e495d1f74af13aec8d5d825e93e4cfe1e4fe7468Vasu Nori    }
94e495d1f74af13aec8d5d825e93e4cfe1e4fe7468Vasu Nori
95e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    final String[] getColumnNames() {
96e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return mColumnNames;
9759d60420ba9246eee152852b6a597a0aba7f704dVasu Nori    }
9859d60420ba9246eee152852b6a597a0aba7f704dVasu Nori
99e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /** @hide */
100e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    protected final SQLiteSession getSession() {
101e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return mDatabase.getThreadSession();
1025a03f36ef845f73eb4473193dbb0f93dd12a51afVasu Nori    }
1035a03f36ef845f73eb4473193dbb0f93dd12a51afVasu Nori
104e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /** @hide */
105e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    protected final int getConnectionFlags() {
106e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return mDatabase.getThreadDefaultConnectionFlags(mReadOnly);
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
109e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /** @hide */
110e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    protected final void onCorruption() {
111e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mDatabase.onCorruption();
112e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
113e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown
114e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    /**
115e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Unimplemented.
116e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @deprecated This method is deprecated and must not be used.
117e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     */
118e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    @Deprecated
119e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    public final int getUniqueId() {
120e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        return -1;
1210732f7912ccec9a1cc379b535ac0b56ae50972b3Vasu Nori    }
1220732f7912ccec9a1cc379b535ac0b56ae50972b3Vasu Nori
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Bind a NULL value to this statement. The value remains bound until
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #clearBindings} is called.
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param index The 1-based index to the parameter to bind null to
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void bindNull(int index) {
130e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        bind(index, null);
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Bind a long value to this statement. The value remains bound until
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #clearBindings} is called.
1360732f7912ccec9a1cc379b535ac0b56ae50972b3Vasu Nori     *addToBindArgs
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param index The 1-based index to the parameter to bind
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param value The value to bind
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void bindLong(int index, long value) {
141e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        bind(index, value);
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Bind a double value to this statement. The value remains bound until
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #clearBindings} is called.
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param index The 1-based index to the parameter to bind
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param value The value to bind
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void bindDouble(int index, double value) {
152e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        bind(index, value);
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Bind a String value to this statement. The value remains bound until
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #clearBindings} is called.
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param index The 1-based index to the parameter to bind
160e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param value The value to bind, must not be null
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void bindString(int index, String value) {
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (value == null) {
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalArgumentException("the bind value at index " + index + " is null");
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
166e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        bind(index, value);
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Bind a byte array value to this statement. The value remains bound until
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #clearBindings} is called.
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param index The 1-based index to the parameter to bind
174e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param value The value to bind, must not be null
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void bindBlob(int index, byte[] value) {
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (value == null) {
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalArgumentException("the bind value at index " + index + " is null");
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
180e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        bind(index, value);
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Clears all existing bindings. Unset bindings are treated as NULL.
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void clearBindings() {
187e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (mBindArgs != null) {
188e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            Arrays.fill(mBindArgs, null);
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1930732f7912ccec9a1cc379b535ac0b56ae50972b3Vasu Nori     * Given an array of String bindArgs, this method binds all of them in one single call.
1940732f7912ccec9a1cc379b535ac0b56ae50972b3Vasu Nori     *
195e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * @param bindArgs the String array of bind args, none of which must be null.
1960732f7912ccec9a1cc379b535ac0b56ae50972b3Vasu Nori     */
1970732f7912ccec9a1cc379b535ac0b56ae50972b3Vasu Nori    public void bindAllArgsAsStrings(String[] bindArgs) {
198e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (bindArgs != null) {
199e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            for (int i = bindArgs.length; i != 0; i--) {
200e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                bindString(i, bindArgs[i - 1]);
201e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            }
202e25539fdfdf884eee55107efbcc893f44e82e00eVasu Nori        }
203e25539fdfdf884eee55107efbcc893f44e82e00eVasu Nori    }
204e25539fdfdf884eee55107efbcc893f44e82e00eVasu Nori
205e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    @Override
206e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    protected void onAllReferencesReleased() {
207e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        clearBindings();
208196663234ab1b67fdd88060701e8211f67fd7854Vasu Nori    }
209196663234ab1b67fdd88060701e8211f67fd7854Vasu Nori
210e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    private void bind(int index, Object value) {
211e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        if (index < 1 || index > mNumParameters) {
212e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown            throw new IllegalArgumentException("Cannot bind argument at index "
213e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    + index + " because the index is out of range.  "
214e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown                    + "The statement has " + mNumParameters + " parameters.");
215e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        }
216e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        mBindArgs[index - 1] = value;
217e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown    }
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
219