diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2017-03-01 18:17:55 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2017-03-01 18:17:55 +0000 |
commit | 7fee0934416ba2d240a3ff4d43419ba415337a85 (patch) | |
tree | 9e9e25e21a92627b46a9d6ba34bc4afd3208ab28 /src | |
parent | beeee64617684297013c023ece2eb2d5e8f94376 (diff) | |
parent | 607e7e3208b8b40633b3e2c1990cf8af2e7bf174 (diff) | |
download | ContactsProvider-7fee0934416ba2d240a3ff4d43419ba415337a85.tar.gz |
Merge "Shutdown worker thread when not needed"
Diffstat (limited to 'src')
3 files changed, 179 insertions, 49 deletions
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java index c1a50b26..3d3678ef 100644 --- a/src/com/android/providers/contacts/CallLogProvider.java +++ b/src/com/android/providers/contacts/CallLogProvider.java @@ -33,10 +33,6 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.os.Binder; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Message; -import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.provider.CallLog; @@ -46,11 +42,13 @@ import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.Log; + import com.android.internal.annotations.VisibleForTesting; import com.android.providers.contacts.CallLogDatabaseHelper.DbProperties; import com.android.providers.contacts.CallLogDatabaseHelper.Tables; import com.android.providers.contacts.util.SelectionBuilder; import com.android.providers.contacts.util.UserUtils; + import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -62,7 +60,7 @@ import java.util.concurrent.CountDownLatch; public class CallLogProvider extends ContentProvider { private static final String TAG = CallLogProvider.class.getSimpleName(); - public static final boolean VERBOSE_LOGGING = false; // DO NOT SUBMIT WITH TRUE + public static final boolean VERBOSE_LOGGING = AbstractContactsProvider.VERBOSE_LOGGING; private static final int BACKGROUND_TASK_INITIALIZE = 0; private static final int BACKGROUND_TASK_ADJUST_PHONE_ACCOUNT = 1; @@ -166,8 +164,8 @@ public class CallLogProvider extends ContentProvider { private static Long sTimeForTestMillis; - private HandlerThread mBackgroundThread; - private Handler mBackgroundHandler; + private ContactsTaskScheduler mTaskScheduler; + private volatile CountDownLatch mReadAccessLatch; private CallLogDatabaseHelper mDbHelper; @@ -198,19 +196,16 @@ public class CallLogProvider extends ContentProvider { mVoicemailPermissions = new VoicemailPermissions(context); mCallLogInsertionHelper = createCallLogInsertionHelper(context); - mBackgroundThread = new HandlerThread(getProviderName() + "Worker", - Process.THREAD_PRIORITY_BACKGROUND); - mBackgroundThread.start(); - mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) { + mReadAccessLatch = new CountDownLatch(1); + + mTaskScheduler = new ContactsTaskScheduler(getClass().getSimpleName()) { @Override - public void handleMessage(Message msg) { - performBackgroundTask(msg.what, msg.obj); + public void onPerformTask(int taskId, Object arg) { + performBackgroundTask(taskId, arg); } }; - mReadAccessLatch = new CountDownLatch(1); - - scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE, null); + mTaskScheduler.scheduleTask(BACKGROUND_TASK_INITIALIZE, null); if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) { Log.d(Constants.PERFORMANCE_TAG, getProviderName() + ".onCreate finish"); @@ -452,7 +447,7 @@ public class CallLogProvider extends ContentProvider { } void adjustForNewPhoneAccount(PhoneAccountHandle handle) { - scheduleBackgroundTask(BACKGROUND_TASK_ADJUST_PHONE_ACCOUNT, handle); + mTaskScheduler.scheduleTask(BACKGROUND_TASK_ADJUST_PHONE_ACCOUNT, handle); } /** @@ -735,10 +730,6 @@ public class CallLogProvider extends ContentProvider { } } - private void scheduleBackgroundTask(int task, Object arg) { - mBackgroundHandler.obtainMessage(task, arg).sendToTarget(); - } - private void performBackgroundTask(int task, Object arg) { if (task == BACKGROUND_TASK_INITIALIZE) { try { @@ -754,12 +745,6 @@ public class CallLogProvider extends ContentProvider { @Override public void shutdown() { - if (mBackgroundHandler != null) { - mBackgroundHandler.getLooper().quit(); - try { - mBackgroundThread.join(); - } catch (InterruptedException ignore) { - } - } + mTaskScheduler.shutdownForTest(); } } diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index f3f85cf8..ceda7d0c 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -56,12 +56,8 @@ import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.CancellationSignal; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.AutoCloseInputStream; -import android.os.Process; import android.os.RemoteException; import android.os.StrictMode; import android.os.SystemClock; @@ -1532,8 +1528,7 @@ public class ContactsProvider2 extends AbstractContactsProvider private LocaleSet mCurrentLocales; private int mContactsAccountCount; - private HandlerThread mBackgroundThread; - private Handler mBackgroundHandler; + private ContactsTaskScheduler mTaskScheduler; private long mLastPhotoCleanup = 0; @@ -1601,13 +1596,10 @@ public class ContactsProvider2 extends AbstractContactsProvider mReadAccessLatch = new CountDownLatch(1); mWriteAccessLatch = new CountDownLatch(1); - mBackgroundThread = new HandlerThread("ContactsProviderWorker", - Process.THREAD_PRIORITY_BACKGROUND); - mBackgroundThread.start(); - mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) { + mTaskScheduler = new ContactsTaskScheduler(getClass().getSimpleName()) { @Override - public void handleMessage(Message msg) { - performBackgroundTask(msg.what, msg.obj); + public void onPerformTask(int taskId, Object arg) { + performBackgroundTask(taskId, arg); } }; @@ -1733,11 +1725,11 @@ public class ContactsProvider2 extends AbstractContactsProvider } protected void scheduleBackgroundTask(int task) { - mBackgroundHandler.sendEmptyMessage(task); + scheduleBackgroundTask(task, null); } protected void scheduleBackgroundTask(int task, Object arg) { - mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg)); + mTaskScheduler.scheduleTask(task, arg); } protected void performBackgroundTask(int task, Object arg) { @@ -10221,13 +10213,7 @@ public class ContactsProvider2 extends AbstractContactsProvider @Override public void shutdown() { - if (mBackgroundHandler != null) { - mBackgroundHandler.getLooper().quit(); - try { - mBackgroundThread.join(); - } catch (InterruptedException ignore) { - } - } + mTaskScheduler.shutdownForTest(); } @VisibleForTesting diff --git a/src/com/android/providers/contacts/ContactsTaskScheduler.java b/src/com/android/providers/contacts/ContactsTaskScheduler.java new file mode 100644 index 00000000..16283877 --- /dev/null +++ b/src/com/android/providers/contacts/ContactsTaskScheduler.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2017 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.providers.contacts; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.concurrent.atomic.AtomicInteger; + +import javax.annotation.concurrent.GuardedBy; + +/** + * Runs tasks in a worker thread, which is created on-demand and shuts down after a timeout. + */ +public abstract class ContactsTaskScheduler { + private static final String TAG = "ContactsTaskScheduler"; + + public static final boolean VERBOSE_LOGGING = AbstractContactsProvider.VERBOSE_LOGGING; + + private static final int SHUTDOWN_TIMEOUT_SECONDS = 60; + + private final AtomicInteger mThreadSequenceNumber = new AtomicInteger(); + + private final Object mLock = new Object(); + + /** + * Name of this scheduler for logging. + */ + private final String mName; + + @GuardedBy("mLock") + private HandlerThread mThread; + + @GuardedBy("mLock") + private MyHandler mHandler; + + private final int mShutdownTimeoutSeconds; + + public ContactsTaskScheduler(String name) { + this(name, SHUTDOWN_TIMEOUT_SECONDS); + } + + /** With explicit timeout seconds, for testing. */ + protected ContactsTaskScheduler(String name, int shutdownTimeoutSeconds) { + mName = name; + mShutdownTimeoutSeconds = shutdownTimeoutSeconds; + } + + private class MyHandler extends Handler { + public MyHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + if (VERBOSE_LOGGING) { + Log.v(TAG, "[" + mName + "] " + mThread + " dispatching " + msg.what); + } + onPerformTask(msg.what, msg.obj); + } + } + + private final Runnable mQuitter = () -> { + synchronized (mLock) { + stopThread(/* joinOnlyForTest=*/ false); + } + }; + + private boolean isRunning() { + synchronized (mLock) { + return mThread != null; + } + } + + /** Schedule a task with no arguments. */ + @VisibleForTesting + public void scheduleTask(int taskId) { + scheduleTask(taskId, null); + } + + /** Schedule a task with an argument. */ + @VisibleForTesting + public void scheduleTask(int taskId, Object arg) { + synchronized (mLock) { + if (!isRunning()) { + mThread = new HandlerThread("Worker-" + mThreadSequenceNumber.incrementAndGet()); + mThread.start(); + mHandler = new MyHandler(mThread.getLooper()); + + if (VERBOSE_LOGGING) { + Log.v(TAG, "[" + mName + "] " + mThread + " started."); + } + } + if (arg == null) { + mHandler.sendEmptyMessage(taskId); + } else { + mHandler.sendMessage(mHandler.obtainMessage(taskId, arg)); + } + + // Schedule thread shutdown. + mHandler.removeCallbacks(mQuitter); + mHandler.postDelayed(mQuitter, mShutdownTimeoutSeconds * 1000); + } + } + + public abstract void onPerformTask(int taskId, Object arg); + + @VisibleForTesting + public void shutdownForTest() { + stopThread(/* joinOnlyForTest=*/ true); + } + + private void stopThread(boolean joinOnlyForTest) { + synchronized (mLock) { + if (VERBOSE_LOGGING) { + Log.v(TAG, "[" + mName + "] " + mThread + " stopping..."); + } + if (mThread != null) { + mThread.quit(); + if (joinOnlyForTest) { + try { + mThread.join(); + } catch (InterruptedException ignore) { + } + } + } + mThread = null; + mHandler = null; + } + } + + @VisibleForTesting + public int getThreadSequenceNumber() { + return mThreadSequenceNumber.get(); + } + + @VisibleForTesting + public boolean isRunningForTest() { + return isRunning(); + } +} |