diff options
4 files changed, 166 insertions, 87 deletions
diff --git a/src/com/android/providers/contacts/AbstractContactsProvider.java b/src/com/android/providers/contacts/AbstractContactsProvider.java index 1bc59e7e..ebf0a1b9 100644 --- a/src/com/android/providers/contacts/AbstractContactsProvider.java +++ b/src/com/android/providers/contacts/AbstractContactsProvider.java @@ -17,7 +17,6 @@ package com.android.providers.contacts; import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns; -import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns; import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; import com.android.providers.contacts.ContactsDatabaseHelper.Tables; @@ -130,6 +129,11 @@ public abstract class AbstractContactsProvider extends ContentProvider protected final SparseLongArray mUpdateInBatchStats = new SparseLongArray(); protected final SparseLongArray mDeleteInBatchStats = new SparseLongArray(); + private final SparseLongArray mOperationDurationMicroStats = new SparseLongArray(); + + private final ThreadLocal<Integer> mOperationNest = ThreadLocal.withInitial(() -> 0); + private final ThreadLocal<Long> mOperationStartNs = ThreadLocal.withInitial(() -> 0L); + @Override public boolean onCreate() { Context context = getContext(); @@ -159,6 +163,12 @@ public abstract class AbstractContactsProvider extends ContentProvider synchronized (mStatsLock) { stats.put(callingUid, stats.get(callingUid) + 1); mAllCallingUids.put(callingUid, true); + + final int nest = mOperationNest.get(); + mOperationNest.set(nest + 1); + if (nest == 0) { + mOperationStartNs.set(SystemClock.elapsedRealtimeNanos()); + } } } @@ -169,6 +179,19 @@ public abstract class AbstractContactsProvider extends ContentProvider incrementStats(inBatch ? statsInBatch : statsNonBatch); } + protected void finishOperation() { + final int callingUid = Binder.getCallingUid(); + synchronized (mStatsLock) { + final int nest = mOperationNest.get(); + mOperationNest.set(nest - 1); + if (nest == 1) { + final long duration = SystemClock.elapsedRealtimeNanos() - mOperationStartNs.get(); + mOperationDurationMicroStats.put(callingUid, + mOperationDurationMicroStats.get(callingUid) + duration / 1000L); + } + } + } + public ContactsTransaction getCurrentTransaction() { return mTransactionHolder.get(); } @@ -176,119 +199,139 @@ public abstract class AbstractContactsProvider extends ContentProvider @Override public Uri insert(Uri uri, ContentValues values) { incrementStats(mInsertStats, mInsertInBatchStats); - ContactsTransaction transaction = startTransaction(false); try { - Uri result = insertInTransaction(uri, values); - if (result != null) { - transaction.markDirty(); + ContactsTransaction transaction = startTransaction(false); + try { + Uri result = insertInTransaction(uri, values); + if (result != null) { + transaction.markDirty(); + } + transaction.markSuccessful(false); + return result; + } finally { + endTransaction(false); } - transaction.markSuccessful(false); - return result; } finally { - endTransaction(false); + finishOperation(); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { incrementStats(mDeleteStats, mDeleteInBatchStats); - ContactsTransaction transaction = startTransaction(false); try { - int deleted = deleteInTransaction(uri, selection, selectionArgs); - if (deleted > 0) { - transaction.markDirty(); + ContactsTransaction transaction = startTransaction(false); + try { + int deleted = deleteInTransaction(uri, selection, selectionArgs); + if (deleted > 0) { + transaction.markDirty(); + } + transaction.markSuccessful(false); + return deleted; + } finally { + endTransaction(false); } - transaction.markSuccessful(false); - return deleted; } finally { - endTransaction(false); + finishOperation(); } } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { incrementStats(mUpdateStats, mUpdateInBatchStats); - ContactsTransaction transaction = startTransaction(false); try { - int updated = updateInTransaction(uri, values, selection, selectionArgs); - if (updated > 0) { - transaction.markDirty(); + ContactsTransaction transaction = startTransaction(false); + try { + int updated = updateInTransaction(uri, values, selection, selectionArgs); + if (updated > 0) { + transaction.markDirty(); + } + transaction.markSuccessful(false); + return updated; + } finally { + endTransaction(false); } - transaction.markSuccessful(false); - return updated; } finally { - endTransaction(false); + finishOperation(); } } @Override public int bulkInsert(Uri uri, ContentValues[] values) { incrementStats(mBatchStats); - ContactsTransaction transaction = startTransaction(true); - int numValues = values.length; - int opCount = 0; try { - for (int i = 0; i < numValues; i++) { - insert(uri, values[i]); - if (++opCount >= BULK_INSERTS_PER_YIELD_POINT) { - opCount = 0; - try { - yield(transaction); - } catch (RuntimeException re) { - transaction.markYieldFailed(); - throw re; + ContactsTransaction transaction = startTransaction(true); + int numValues = values.length; + int opCount = 0; + try { + for (int i = 0; i < numValues; i++) { + insert(uri, values[i]); + if (++opCount >= BULK_INSERTS_PER_YIELD_POINT) { + opCount = 0; + try { + yield(transaction); + } catch (RuntimeException re) { + transaction.markYieldFailed(); + throw re; + } } } + transaction.markSuccessful(true); + } finally { + endTransaction(true); } - transaction.markSuccessful(true); + return numValues; } finally { - endTransaction(true); + finishOperation(); } - return numValues; } @Override public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { incrementStats(mBatchStats); - if (VERBOSE_LOGGING) { - Log.v(TAG, "applyBatch: " + operations.size() + " ops"); - } - int ypCount = 0; - int opCount = 0; - ContactsTransaction transaction = startTransaction(true); try { - final int numOperations = operations.size(); - final ContentProviderResult[] results = new ContentProviderResult[numOperations]; - for (int i = 0; i < numOperations; i++) { - if (++opCount >= MAX_OPERATIONS_PER_YIELD_POINT) { - throw new OperationApplicationException( - "Too many content provider operations between yield points. " - + "The maximum number of operations per yield point is " - + MAX_OPERATIONS_PER_YIELD_POINT, ypCount); - } - final ContentProviderOperation operation = operations.get(i); - if (i > 0 && operation.isYieldAllowed()) { - if (VERBOSE_LOGGING) { - Log.v(TAG, "applyBatch: " + opCount + " ops finished; about to yield..."); + if (VERBOSE_LOGGING) { + Log.v(TAG, "applyBatch: " + operations.size() + " ops"); + } + int ypCount = 0; + int opCount = 0; + ContactsTransaction transaction = startTransaction(true); + try { + final int numOperations = operations.size(); + final ContentProviderResult[] results = new ContentProviderResult[numOperations]; + for (int i = 0; i < numOperations; i++) { + if (++opCount >= MAX_OPERATIONS_PER_YIELD_POINT) { + throw new OperationApplicationException( + "Too many content provider operations between yield points. " + + "The maximum number of operations per yield point is " + + MAX_OPERATIONS_PER_YIELD_POINT, ypCount); } - opCount = 0; - try { - if (yield(transaction)) { - ypCount++; + final ContentProviderOperation operation = operations.get(i); + if (i > 0 && operation.isYieldAllowed()) { + if (VERBOSE_LOGGING) { + Log.v(TAG, "applyBatch: " + opCount + " ops finished; about to yield..."); + } + opCount = 0; + try { + if (yield(transaction)) { + ypCount++; + } + } catch (RuntimeException re) { + transaction.markYieldFailed(); + throw re; } - } catch (RuntimeException re) { - transaction.markYieldFailed(); - throw re; } - } - results[i] = operation.apply(this, results, i); + results[i] = operation.apply(this, results, i); + } + transaction.markSuccessful(true); + return results; + } finally { + endTransaction(true); } - transaction.markSuccessful(true); - return results; } finally { - endTransaction(true); + finishOperation(); } } @@ -426,20 +469,22 @@ public abstract class AbstractContactsProvider extends ContentProvider synchronized (mStatsLock) { pw.println(); pw.println(" Client activities:"); - pw.println(" UID Query Insert Update Delete Batch Insert Update Delete:"); + pw.println(" UID Query Insert Update Delete Batch Insert Update Delete" + + " Sec"); for (int i = 0; i < mAllCallingUids.size(); i++) { - final int pid = mAllCallingUids.keyAt(i); + final int uid = mAllCallingUids.keyAt(i); pw.println(String.format( - " %-9d %6d %6d %6d %6d %6d %6d %6d %6d", - pid, - mQueryStats.get(pid), - mInsertStats.get(pid), - mUpdateStats.get(pid), - mDeleteStats.get(pid), - mBatchStats.get(pid), - mInsertInBatchStats.get(pid), - mUpdateInBatchStats.get(pid), - mDeleteInBatchStats.get(pid) + " %-9d %6d %6d %6d %6d %6d %6d %6d %6d %12.3f", + uid, + mQueryStats.get(uid), + mInsertStats.get(uid), + mUpdateStats.get(uid), + mDeleteStats.get(uid), + mBatchStats.get(uid), + mInsertInBatchStats.get(uid), + mUpdateInBatchStats.get(uid), + mDeleteInBatchStats.get(uid), + (mOperationDurationMicroStats.get(uid) / 1000000.0) )); } } diff --git a/src/com/android/providers/contacts/ContactDirectoryManager.java b/src/com/android/providers/contacts/ContactDirectoryManager.java index cb6dc4a1..33e541d3 100644 --- a/src/com/android/providers/contacts/ContactDirectoryManager.java +++ b/src/com/android/providers/contacts/ContactDirectoryManager.java @@ -104,6 +104,8 @@ public class ContactDirectoryManager { private final Context mContext; private final PackageManager mPackageManager; + private volatile boolean mDirectoriesForceUpdated = false; + public ContactDirectoryManager(ContactsProvider2 contactsProvider) { mContactsProvider = contactsProvider; mContext = contactsProvider.getContext(); @@ -114,6 +116,10 @@ public class ContactDirectoryManager { return (ContactsDatabaseHelper) mContactsProvider.getDatabaseHelper(); } + public void setDirectoriesForceUpdated(boolean updated) { + mDirectoriesForceUpdated = updated; + } + /** * Scans through existing directories to see if the cached resource IDs still * match their original resource names. If not - plays it safe by refreshing all directories. @@ -230,6 +236,9 @@ public class ContactDirectoryManager { Log.d(TAG, "scanAllPackagesIfNeeded()"); } final long start = SystemClock.elapsedRealtime(); + // Reset directory updated flag to false. If it's changed to true + // then we need to rescan directories. + mDirectoriesForceUpdated = false; final int count = scanAllPackages(); getDbHelper().setProperty(DbProperties.DIRECTORY_SCAN_COMPLETE, "1"); final long end = SystemClock.elapsedRealtime(); @@ -238,6 +247,13 @@ public class ContactDirectoryManager { // Announce the change to listeners of the contacts authority mContactsProvider.notifyChange(/* syncToNetwork =*/false, /* syncToMetadataNetwork =*/false); + + // We schedule a rescan if update(DIRECTORIES) is called while we're scanning all packages. + if (mDirectoriesForceUpdated) { + mDirectoriesForceUpdated = false; + mContactsProvider.scheduleRescanDirectories(); + } + return count; } diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 66580797..0d2dd9ed 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -252,6 +252,7 @@ public class ContactsProvider2 extends AbstractContactsProvider private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9; private static final int BACKGROUND_TASK_CLEANUP_PHOTOS = 10; private static final int BACKGROUND_TASK_CLEAN_DELETE_LOG = 11; + private static final int BACKGROUND_TASK_RESCAN_DIRECTORY = 12; protected static final int STATUS_NORMAL = 0; protected static final int STATUS_UPGRADING = 1; @@ -1779,6 +1780,11 @@ public class ContactsProvider2 extends AbstractContactsProvider break; } + case BACKGROUND_TASK_RESCAN_DIRECTORY: { + updateDirectoriesInBackground(true); + break; + } + case BACKGROUND_TASK_UPDATE_LOCALE: { updateLocaleInBackground(); break; @@ -4233,6 +4239,7 @@ public class ContactsProvider2 extends AbstractContactsProvider } case DIRECTORIES: { + mContactDirectoryManager.setDirectoriesForceUpdated(true); scanPackagesByUid(Binder.getCallingUid()); count = 1; break; @@ -4936,6 +4943,10 @@ public class ContactsProvider2 extends AbstractContactsProvider scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS); } + public void scheduleRescanDirectories() { + scheduleBackgroundTask(BACKGROUND_TASK_RESCAN_DIRECTORY); + } + interface RawContactsBackupQuery { String TABLE = Tables.RAW_CONTACTS; String[] COLUMNS = new String[] { @@ -5529,12 +5540,15 @@ public class ContactsProvider2 extends AbstractContactsProvider cancellationSignal); } incrementStats(mQueryStats); + try { + // Otherwise proceed with a normal query against the contacts DB. + switchToContactMode(); - // Otherwise proceed with a normal query against the contacts DB. - switchToContactMode(); - - return queryDirectoryIfNecessary(uri, projection, selection, selectionArgs, sortOrder, - cancellationSignal); + return queryDirectoryIfNecessary(uri, projection, selection, selectionArgs, sortOrder, + cancellationSignal); + } finally { + finishOperation(); + } } private boolean isCallerFromSameUser() { diff --git a/src/com/android/providers/contacts/ProfileProvider.java b/src/com/android/providers/contacts/ProfileProvider.java index 00e7715f..6c84e4b0 100644 --- a/src/com/android/providers/contacts/ProfileProvider.java +++ b/src/com/android/providers/contacts/ProfileProvider.java @@ -71,8 +71,12 @@ public class ProfileProvider extends AbstractContactsProvider { public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { incrementStats(mQueryStats); - return mDelegate.queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1, - cancellationSignal); + try { + return mDelegate.queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1, + cancellationSignal); + } finally { + finishOperation(); + } } @Override |