aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2017-03-01 18:17:55 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2017-03-01 18:17:55 +0000
commit7fee0934416ba2d240a3ff4d43419ba415337a85 (patch)
tree9e9e25e21a92627b46a9d6ba34bc4afd3208ab28 /src
parentbeeee64617684297013c023ece2eb2d5e8f94376 (diff)
parent607e7e3208b8b40633b3e2c1990cf8af2e7bf174 (diff)
downloadContactsProvider-7fee0934416ba2d240a3ff4d43419ba415337a85.tar.gz
Merge "Shutdown worker thread when not needed"
Diffstat (limited to 'src')
-rw-r--r--src/com/android/providers/contacts/CallLogProvider.java41
-rw-r--r--src/com/android/providers/contacts/ContactsProvider2.java28
-rw-r--r--src/com/android/providers/contacts/ContactsTaskScheduler.java159
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();
+ }
+}