Revert "Initial checkin of AOSP Messaging app."
This reverts commit 461a34b466.
Change-Id: Iac4ca77eeaa94989e91dead49a7959c905bd3078
This commit is contained in:
@@ -1,482 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.messaging.datamodel;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteFullException;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.messaging.Factory;
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.util.Assert;
|
||||
import com.android.messaging.util.BugleGservicesKeys;
|
||||
import com.android.messaging.util.DebugUtils;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.UiUtils;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DatabaseWrapper {
|
||||
private static final String TAG = LogUtil.BUGLE_DATABASE_TAG;
|
||||
|
||||
private final SQLiteDatabase mDatabase;
|
||||
private final Context mContext;
|
||||
private final boolean mLog;
|
||||
/**
|
||||
* Set mExplainQueryPlanRegexp (via {@link BugleGservicesKeys#EXPLAIN_QUERY_PLAN_REGEXP}
|
||||
* to regex matching queries to see query plans. For example, ".*" to show all query plans.
|
||||
*/
|
||||
// See
|
||||
private final String mExplainQueryPlanRegexp;
|
||||
private static final int sTimingThreshold = 50; // in milliseconds
|
||||
|
||||
public static final int INDEX_INSERT_MESSAGE_PART = 0;
|
||||
public static final int INDEX_INSERT_MESSAGE = 1;
|
||||
public static final int INDEX_QUERY_CONVERSATIONS_LATEST_MESSAGE = 2;
|
||||
public static final int INDEX_QUERY_MESSAGES_LATEST_MESSAGE = 3;
|
||||
|
||||
private final SparseArray<SQLiteStatement> mCompiledStatements;
|
||||
|
||||
static class TransactionData {
|
||||
long time;
|
||||
boolean transactionSuccessful;
|
||||
}
|
||||
|
||||
// track transaction on a per thread basis
|
||||
private static ThreadLocal<Stack<TransactionData>> sTransactionDepth =
|
||||
new ThreadLocal<Stack<TransactionData>>() {
|
||||
@Override
|
||||
public Stack<TransactionData> initialValue() {
|
||||
return new Stack<TransactionData>();
|
||||
}
|
||||
};
|
||||
|
||||
private static String[] sFormatStrings = new String[] {
|
||||
"took %d ms to %s",
|
||||
" took %d ms to %s",
|
||||
" took %d ms to %s",
|
||||
};
|
||||
|
||||
DatabaseWrapper(final Context context, final SQLiteDatabase db) {
|
||||
mLog = LogUtil.isLoggable(LogUtil.BUGLE_DATABASE_PERF_TAG, LogUtil.VERBOSE);
|
||||
mExplainQueryPlanRegexp = Factory.get().getBugleGservices().getString(
|
||||
BugleGservicesKeys.EXPLAIN_QUERY_PLAN_REGEXP, null);
|
||||
mDatabase = db;
|
||||
mContext = context;
|
||||
mCompiledStatements = new SparseArray<SQLiteStatement>();
|
||||
}
|
||||
|
||||
public SQLiteStatement getStatementInTransaction(final int index, final String statement) {
|
||||
// Use transaction to serialize access to statements
|
||||
Assert.isTrue(mDatabase.inTransaction());
|
||||
SQLiteStatement compiled = mCompiledStatements.get(index);
|
||||
if (compiled == null) {
|
||||
compiled = mDatabase.compileStatement(statement);
|
||||
Assert.isTrue(compiled.toString().contains(statement.trim()));
|
||||
mCompiledStatements.put(index, compiled);
|
||||
}
|
||||
return compiled;
|
||||
}
|
||||
|
||||
private void maybePlayDebugNoise() {
|
||||
DebugUtils.maybePlayDebugNoise(mContext, DebugUtils.DEBUG_SOUND_DB_OP);
|
||||
}
|
||||
|
||||
private static void printTiming(final long t1, final String msg) {
|
||||
final int transactionDepth = sTransactionDepth.get().size();
|
||||
final long t2 = System.currentTimeMillis();
|
||||
final long delta = t2 - t1;
|
||||
if (delta > sTimingThreshold) {
|
||||
LogUtil.v(LogUtil.BUGLE_DATABASE_PERF_TAG, String.format(Locale.US,
|
||||
sFormatStrings[Math.min(sFormatStrings.length - 1, transactionDepth)],
|
||||
delta,
|
||||
msg));
|
||||
}
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public void beginTransaction() {
|
||||
final long t1 = System.currentTimeMillis();
|
||||
|
||||
// push the current time onto the transaction stack
|
||||
final TransactionData f = new TransactionData();
|
||||
f.time = t1;
|
||||
sTransactionDepth.get().push(f);
|
||||
|
||||
mDatabase.beginTransaction();
|
||||
}
|
||||
|
||||
public void setTransactionSuccessful() {
|
||||
final TransactionData f = sTransactionDepth.get().peek();
|
||||
f.transactionSuccessful = true;
|
||||
mDatabase.setTransactionSuccessful();
|
||||
}
|
||||
|
||||
public void endTransaction() {
|
||||
long t1 = 0;
|
||||
long transactionStartTime = 0;
|
||||
final TransactionData f = sTransactionDepth.get().pop();
|
||||
if (f.transactionSuccessful == false) {
|
||||
LogUtil.w(TAG, "endTransaction without setting successful");
|
||||
for (final StackTraceElement st : (new Exception()).getStackTrace()) {
|
||||
LogUtil.w(TAG, " " + st.toString());
|
||||
}
|
||||
}
|
||||
if (mLog) {
|
||||
transactionStartTime = f.time;
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
try {
|
||||
mDatabase.endTransaction();
|
||||
} catch (SQLiteFullException ex) {
|
||||
LogUtil.e(TAG, "Database full, unable to endTransaction", ex);
|
||||
UiUtils.showToastAtBottom(R.string.db_full);
|
||||
}
|
||||
if (mLog) {
|
||||
printTiming(t1, String.format(Locale.US,
|
||||
">>> endTransaction (total for this transaction: %d)",
|
||||
(System.currentTimeMillis() - transactionStartTime)));
|
||||
}
|
||||
}
|
||||
|
||||
public void yieldTransaction() {
|
||||
long yieldStartTime = 0;
|
||||
if (mLog) {
|
||||
yieldStartTime = System.currentTimeMillis();
|
||||
}
|
||||
final boolean wasYielded = mDatabase.yieldIfContendedSafely();
|
||||
if (wasYielded && mLog) {
|
||||
printTiming(yieldStartTime, "yieldTransaction");
|
||||
}
|
||||
}
|
||||
|
||||
public void insertWithOnConflict(final String searchTable, final String nullColumnHack,
|
||||
final ContentValues initialValues, final int conflictAlgorithm) {
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
try {
|
||||
mDatabase.insertWithOnConflict(searchTable, nullColumnHack, initialValues,
|
||||
conflictAlgorithm);
|
||||
} catch (SQLiteFullException ex) {
|
||||
LogUtil.e(TAG, "Database full, unable to insertWithOnConflict", ex);
|
||||
UiUtils.showToastAtBottom(R.string.db_full);
|
||||
}
|
||||
if (mLog) {
|
||||
printTiming(t1, String.format(Locale.US,
|
||||
"insertWithOnConflict with ", searchTable));
|
||||
}
|
||||
}
|
||||
|
||||
private void explainQueryPlan(final SQLiteQueryBuilder qb, final SQLiteDatabase db,
|
||||
final String[] projection, final String selection,
|
||||
@SuppressWarnings("unused")
|
||||
final String[] queryArgs,
|
||||
final String groupBy,
|
||||
@SuppressWarnings("unused")
|
||||
final String having,
|
||||
final String sortOrder, final String limit) {
|
||||
final String queryString = qb.buildQuery(
|
||||
projection,
|
||||
selection,
|
||||
groupBy,
|
||||
null/*having*/,
|
||||
sortOrder,
|
||||
limit);
|
||||
explainQueryPlan(db, queryString, queryArgs);
|
||||
}
|
||||
|
||||
private void explainQueryPlan(final SQLiteDatabase db, final String sql,
|
||||
final String[] queryArgs) {
|
||||
if (!Pattern.matches(mExplainQueryPlanRegexp, sql)) {
|
||||
return;
|
||||
}
|
||||
final Cursor planCursor = db.rawQuery("explain query plan " + sql, queryArgs);
|
||||
try {
|
||||
if (planCursor != null && planCursor.moveToFirst()) {
|
||||
final int detailColumn = planCursor.getColumnIndex("detail");
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
do {
|
||||
sb.append(planCursor.getString(detailColumn));
|
||||
sb.append("\n");
|
||||
} while (planCursor.moveToNext());
|
||||
if (sb.length() > 0) {
|
||||
sb.setLength(sb.length() - 1);
|
||||
}
|
||||
LogUtil.v(TAG, "for query " + sql + "\nplan is: "
|
||||
+ sb.toString());
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
LogUtil.w(TAG, "Query plan failed ", e);
|
||||
} finally {
|
||||
if (planCursor != null) {
|
||||
planCursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Cursor query(final String searchTable, final String[] projection,
|
||||
final String selection, final String[] selectionArgs, final String groupBy,
|
||||
final String having, final String orderBy, final String limit) {
|
||||
if (mExplainQueryPlanRegexp != null) {
|
||||
final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
||||
qb.setTables(searchTable);
|
||||
explainQueryPlan(qb, mDatabase, projection, selection, selectionArgs,
|
||||
groupBy, having, orderBy, limit);
|
||||
}
|
||||
|
||||
maybePlayDebugNoise();
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
final Cursor cursor = mDatabase.query(searchTable, projection, selection, selectionArgs,
|
||||
groupBy, having, orderBy, limit);
|
||||
if (mLog) {
|
||||
printTiming(
|
||||
t1,
|
||||
String.format(Locale.US, "query %s with %s ==> %d",
|
||||
searchTable, selection, cursor.getCount()));
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public Cursor query(final String searchTable, final String[] columns,
|
||||
final String selection, final String[] selectionArgs, final String groupBy,
|
||||
final String having, final String orderBy) {
|
||||
return query(
|
||||
searchTable, columns, selection, selectionArgs,
|
||||
groupBy, having, orderBy, null);
|
||||
}
|
||||
|
||||
public Cursor query(final SQLiteQueryBuilder qb,
|
||||
final String[] projection, final String selection, final String[] queryArgs,
|
||||
final String groupBy, final String having, final String sortOrder, final String limit) {
|
||||
if (mExplainQueryPlanRegexp != null) {
|
||||
explainQueryPlan(qb, mDatabase, projection, selection, queryArgs,
|
||||
groupBy, having, sortOrder, limit);
|
||||
}
|
||||
maybePlayDebugNoise();
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
final Cursor cursor = qb.query(mDatabase, projection, selection, queryArgs, groupBy,
|
||||
having, sortOrder, limit);
|
||||
if (mLog) {
|
||||
printTiming(
|
||||
t1,
|
||||
String.format(Locale.US, "query %s with %s ==> %d",
|
||||
qb.getTables(), selection, cursor.getCount()));
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public long queryNumEntries(final String table, final String selection,
|
||||
final String[] selectionArgs) {
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
maybePlayDebugNoise();
|
||||
final long retval =
|
||||
DatabaseUtils.queryNumEntries(mDatabase, table, selection, selectionArgs);
|
||||
if (mLog){
|
||||
printTiming(
|
||||
t1,
|
||||
String.format(Locale.US, "queryNumEntries %s with %s ==> %d", table,
|
||||
selection, retval));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
public Cursor rawQuery(final String sql, final String[] args) {
|
||||
if (mExplainQueryPlanRegexp != null) {
|
||||
explainQueryPlan(mDatabase, sql, args);
|
||||
}
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
maybePlayDebugNoise();
|
||||
final Cursor cursor = mDatabase.rawQuery(sql, args);
|
||||
if (mLog) {
|
||||
printTiming(
|
||||
t1,
|
||||
String.format(Locale.US, "rawQuery %s ==> %d", sql, cursor.getCount()));
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public int update(final String table, final ContentValues values,
|
||||
final String selection, final String[] selectionArgs) {
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
maybePlayDebugNoise();
|
||||
int count = 0;
|
||||
try {
|
||||
count = mDatabase.update(table, values, selection, selectionArgs);
|
||||
} catch (SQLiteFullException ex) {
|
||||
LogUtil.e(TAG, "Database full, unable to update", ex);
|
||||
UiUtils.showToastAtBottom(R.string.db_full);
|
||||
}
|
||||
if (mLog) {
|
||||
printTiming(t1, String.format(Locale.US, "update %s with %s ==> %d",
|
||||
table, selection, count));
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public int delete(final String table, final String whereClause, final String[] whereArgs) {
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
maybePlayDebugNoise();
|
||||
int count = 0;
|
||||
try {
|
||||
count = mDatabase.delete(table, whereClause, whereArgs);
|
||||
} catch (SQLiteFullException ex) {
|
||||
LogUtil.e(TAG, "Database full, unable to delete", ex);
|
||||
UiUtils.showToastAtBottom(R.string.db_full);
|
||||
}
|
||||
if (mLog) {
|
||||
printTiming(t1,
|
||||
String.format(Locale.US, "delete from %s with %s ==> %d", table,
|
||||
whereClause, count));
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public long insert(final String table, final String nullColumnHack,
|
||||
final ContentValues values) {
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
maybePlayDebugNoise();
|
||||
long rowId = -1;
|
||||
try {
|
||||
rowId = mDatabase.insert(table, nullColumnHack, values);
|
||||
} catch (SQLiteFullException ex) {
|
||||
LogUtil.e(TAG, "Database full, unable to insert", ex);
|
||||
UiUtils.showToastAtBottom(R.string.db_full);
|
||||
}
|
||||
if (mLog) {
|
||||
printTiming(t1, String.format(Locale.US, "insert to %s", table));
|
||||
}
|
||||
return rowId;
|
||||
}
|
||||
|
||||
public long replace(final String table, final String nullColumnHack,
|
||||
final ContentValues values) {
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
maybePlayDebugNoise();
|
||||
long rowId = -1;
|
||||
try {
|
||||
rowId = mDatabase.replace(table, nullColumnHack, values);
|
||||
} catch (SQLiteFullException ex) {
|
||||
LogUtil.e(TAG, "Database full, unable to replace", ex);
|
||||
UiUtils.showToastAtBottom(R.string.db_full);
|
||||
}
|
||||
if (mLog) {
|
||||
printTiming(t1, String.format(Locale.US, "replace to %s", table));
|
||||
}
|
||||
return rowId;
|
||||
}
|
||||
|
||||
public void setLocale(final Locale locale) {
|
||||
mDatabase.setLocale(locale);
|
||||
}
|
||||
|
||||
public void execSQL(final String sql, final String[] bindArgs) {
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
maybePlayDebugNoise();
|
||||
try {
|
||||
mDatabase.execSQL(sql, bindArgs);
|
||||
} catch (SQLiteFullException ex) {
|
||||
LogUtil.e(TAG, "Database full, unable to execSQL", ex);
|
||||
UiUtils.showToastAtBottom(R.string.db_full);
|
||||
}
|
||||
|
||||
if (mLog) {
|
||||
printTiming(t1, String.format(Locale.US, "execSQL %s", sql));
|
||||
}
|
||||
}
|
||||
|
||||
public void execSQL(final String sql) {
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
maybePlayDebugNoise();
|
||||
try {
|
||||
mDatabase.execSQL(sql);
|
||||
} catch (SQLiteFullException ex) {
|
||||
LogUtil.e(TAG, "Database full, unable to execSQL", ex);
|
||||
UiUtils.showToastAtBottom(R.string.db_full);
|
||||
}
|
||||
|
||||
if (mLog) {
|
||||
printTiming(t1, String.format(Locale.US, "execSQL %s", sql));
|
||||
}
|
||||
}
|
||||
|
||||
public int execSQLUpdateDelete(final String sql) {
|
||||
long t1 = 0;
|
||||
if (mLog) {
|
||||
t1 = System.currentTimeMillis();
|
||||
}
|
||||
maybePlayDebugNoise();
|
||||
final SQLiteStatement statement = mDatabase.compileStatement(sql);
|
||||
int rowsUpdated = 0;
|
||||
try {
|
||||
rowsUpdated = statement.executeUpdateDelete();
|
||||
} catch (SQLiteFullException ex) {
|
||||
LogUtil.e(TAG, "Database full, unable to execSQLUpdateDelete", ex);
|
||||
UiUtils.showToastAtBottom(R.string.db_full);
|
||||
}
|
||||
if (mLog) {
|
||||
printTiming(t1, String.format(Locale.US, "execSQLUpdateDelete %s", sql));
|
||||
}
|
||||
return rowsUpdated;
|
||||
}
|
||||
|
||||
public SQLiteDatabase getDatabase() {
|
||||
return mDatabase;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user