Merge "Run PeopleService.onUserUnlocked() on worker thread instead of main thread" into rvc-dev

This commit is contained in:
Danning Chen
2020-03-11 05:51:26 +00:00
committed by Android (Google) Code Review
7 changed files with 156 additions and 136 deletions

View File

@@ -89,10 +89,8 @@ class ConversationStore {
* Loads conversations from disk to memory in a background thread. This should be called * Loads conversations from disk to memory in a background thread. This should be called
* after the device powers on and the user has been unlocked. * after the device powers on and the user has been unlocked.
*/ */
@MainThread @WorkerThread
void loadConversationsFromDisk() { synchronized void loadConversationsFromDisk() {
mScheduledExecutorService.execute(() -> {
synchronized (this) {
ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter = ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter =
getConversationInfosProtoDiskReadWriter(); getConversationInfosProtoDiskReadWriter();
if (conversationInfosProtoDiskReadWriter == null) { if (conversationInfosProtoDiskReadWriter == null) {
@@ -107,8 +105,6 @@ class ConversationStore {
updateConversationsInMemory(conversationInfo); updateConversationsInMemory(conversationInfo);
} }
} }
});
}
/** /**
* Immediately flushes current conversations to disk. This should be called when device is * Immediately flushes current conversations to disk. This should be called when device is

View File

@@ -28,6 +28,7 @@ import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent; import android.app.prediction.AppTargetEvent;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
@@ -87,13 +88,13 @@ public class DataManager {
private static final String TAG = "DataManager"; private static final String TAG = "DataManager";
private static final long QUERY_EVENTS_MAX_AGE_MS = DateUtils.DAY_IN_MILLIS; private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS;
private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L; private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
private final Context mContext; private final Context mContext;
private final Injector mInjector; private final Injector mInjector;
private final ScheduledExecutorService mUsageStatsQueryExecutor; private final ScheduledExecutorService mScheduledExecutor;
private final ScheduledExecutorService mDiskReadWriterExecutor; private final Object mLock = new Object();
private final SparseArray<UserData> mUserDataArray = new SparseArray<>(); private final SparseArray<UserData> mUserDataArray = new SparseArray<>();
private final SparseArray<BroadcastReceiver> mBroadcastReceivers = new SparseArray<>(); private final SparseArray<BroadcastReceiver> mBroadcastReceivers = new SparseArray<>();
@@ -118,8 +119,7 @@ public class DataManager {
DataManager(Context context, Injector injector) { DataManager(Context context, Injector injector) {
mContext = context; mContext = context;
mInjector = injector; mInjector = injector;
mUsageStatsQueryExecutor = mInjector.createScheduledExecutor(); mScheduledExecutor = mInjector.createScheduledExecutor();
mDiskReadWriterExecutor = mInjector.createScheduledExecutor();
} }
/** Initialization. Called when the system services are up running. */ /** Initialization. Called when the system services are up running. */
@@ -138,68 +138,21 @@ public class DataManager {
/** This method is called when a user is unlocked. */ /** This method is called when a user is unlocked. */
public void onUserUnlocked(int userId) { public void onUserUnlocked(int userId) {
synchronized (mLock) {
UserData userData = mUserDataArray.get(userId); UserData userData = mUserDataArray.get(userId);
if (userData == null) { if (userData == null) {
userData = new UserData(userId, mDiskReadWriterExecutor); userData = new UserData(userId, mScheduledExecutor);
mUserDataArray.put(userId, userData); mUserDataArray.put(userId, userData);
} }
userData.setUserUnlocked(); userData.setUserUnlocked();
updateDefaultDialer(userData);
updateDefaultSmsApp(userData);
ScheduledFuture<?> scheduledFuture = mUsageStatsQueryExecutor.scheduleAtFixedRate(
new UsageStatsQueryRunnable(userId), 1L, USAGE_STATS_QUERY_INTERVAL_SEC,
TimeUnit.SECONDS);
mUsageStatsQueryFutures.put(userId, scheduledFuture);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId);
mBroadcastReceivers.put(userId, broadcastReceiver);
mContext.registerReceiverAsUser(
broadcastReceiver, UserHandle.of(userId), intentFilter, null, null);
ContentObserver contactsContentObserver = new ContactsContentObserver(
BackgroundThread.getHandler());
mContactsContentObservers.put(userId, contactsContentObserver);
mContext.getContentResolver().registerContentObserver(
Contacts.CONTENT_URI, /* notifyForDescendants= */ true,
contactsContentObserver, userId);
NotificationListener notificationListener = new NotificationListener();
mNotificationListeners.put(userId, notificationListener);
try {
notificationListener.registerAsSystemService(mContext,
new ComponentName(mContext, getClass()), userId);
} catch (RemoteException e) {
// Should never occur for local calls.
} }
mScheduledExecutor.execute(() -> setupUser(userId));
PackageMonitor packageMonitor = new PerUserPackageMonitor();
packageMonitor.register(mContext, null, UserHandle.of(userId), true);
mPackageMonitors.put(userId, packageMonitor);
if (userId == UserHandle.USER_SYSTEM) {
// The call log and MMS/SMS messages are shared across user profiles. So only need to
// register the content observers once for the primary user.
// TODO: Register observers after the conversations and events being loaded from disk.
mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler());
mContext.getContentResolver().registerContentObserver(
CallLog.CONTENT_URI, /* notifyForDescendants= */ true,
mCallLogContentObserver, UserHandle.USER_SYSTEM);
mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler());
mContext.getContentResolver().registerContentObserver(
MmsSms.CONTENT_URI, /* notifyForDescendants= */ false,
mMmsSmsContentObserver, UserHandle.USER_SYSTEM);
}
DataMaintenanceService.scheduleJob(mContext, userId);
} }
/** This method is called when a user is stopping. */ /** This method is called when a user is stopping. */
public void onUserStopping(int userId) { public void onUserStopping(int userId) {
synchronized (mLock) {
ContentResolver contentResolver = mContext.getContentResolver();
if (mUserDataArray.indexOfKey(userId) >= 0) { if (mUserDataArray.indexOfKey(userId) >= 0) {
mUserDataArray.get(userId).setUserStopped(); mUserDataArray.get(userId).setUserStopped();
} }
@@ -210,8 +163,7 @@ public class DataManager {
mContext.unregisterReceiver(mBroadcastReceivers.get(userId)); mContext.unregisterReceiver(mBroadcastReceivers.get(userId));
} }
if (mContactsContentObservers.indexOfKey(userId) >= 0) { if (mContactsContentObservers.indexOfKey(userId) >= 0) {
mContext.getContentResolver().unregisterContentObserver( contentResolver.unregisterContentObserver(mContactsContentObservers.get(userId));
mContactsContentObservers.get(userId));
} }
if (mNotificationListeners.indexOfKey(userId) >= 0) { if (mNotificationListeners.indexOfKey(userId) >= 0) {
try { try {
@@ -225,17 +177,18 @@ public class DataManager {
} }
if (userId == UserHandle.USER_SYSTEM) { if (userId == UserHandle.USER_SYSTEM) {
if (mCallLogContentObserver != null) { if (mCallLogContentObserver != null) {
mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver); contentResolver.unregisterContentObserver(mCallLogContentObserver);
mCallLogContentObserver = null; mCallLogContentObserver = null;
} }
if (mMmsSmsContentObserver != null) { if (mMmsSmsContentObserver != null) {
mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver); contentResolver.unregisterContentObserver(mMmsSmsContentObserver);
mCallLogContentObserver = null; mCallLogContentObserver = null;
} }
} }
DataMaintenanceService.cancelJob(mContext, userId); DataMaintenanceService.cancelJob(mContext, userId);
} }
}
/** /**
* Iterates through all the {@link PackageData}s owned by the unlocked users who are in the * Iterates through all the {@link PackageData}s owned by the unlocked users who are in the
@@ -288,6 +241,9 @@ public class DataManager {
return; return;
} }
UserData userData = getUnlockedUserData(appTarget.getUser().getIdentifier()); UserData userData = getUnlockedUserData(appTarget.getUser().getIdentifier());
if (userData == null) {
return;
}
PackageData packageData = userData.getOrCreatePackageData(appTarget.getPackageName()); PackageData packageData = userData.getOrCreatePackageData(appTarget.getPackageName());
String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null; String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
@Event.EventType int eventType = mimeTypeToShareEventType(mimeType); @Event.EventType int eventType = mimeTypeToShareEventType(mimeType);
@@ -353,6 +309,68 @@ public class DataManager {
userData.restore(payload); userData.restore(payload);
} }
private void setupUser(@UserIdInt int userId) {
synchronized (mLock) {
UserData userData = getUnlockedUserData(userId);
if (userData == null) {
return;
}
userData.loadUserData();
updateDefaultDialer(userData);
updateDefaultSmsApp(userData);
ScheduledFuture<?> scheduledFuture = mScheduledExecutor.scheduleAtFixedRate(
new UsageStatsQueryRunnable(userId), 1L, USAGE_STATS_QUERY_INTERVAL_SEC,
TimeUnit.SECONDS);
mUsageStatsQueryFutures.put(userId, scheduledFuture);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId);
mBroadcastReceivers.put(userId, broadcastReceiver);
mContext.registerReceiverAsUser(
broadcastReceiver, UserHandle.of(userId), intentFilter, null, null);
ContentObserver contactsContentObserver = new ContactsContentObserver(
BackgroundThread.getHandler());
mContactsContentObservers.put(userId, contactsContentObserver);
mContext.getContentResolver().registerContentObserver(
Contacts.CONTENT_URI, /* notifyForDescendants= */ true,
contactsContentObserver, userId);
NotificationListener notificationListener = new NotificationListener();
mNotificationListeners.put(userId, notificationListener);
try {
notificationListener.registerAsSystemService(mContext,
new ComponentName(mContext, getClass()), userId);
} catch (RemoteException e) {
// Should never occur for local calls.
}
PackageMonitor packageMonitor = new PerUserPackageMonitor();
packageMonitor.register(mContext, null, UserHandle.of(userId), true);
mPackageMonitors.put(userId, packageMonitor);
if (userId == UserHandle.USER_SYSTEM) {
// The call log and MMS/SMS messages are shared across user profiles. So only need
// to register the content observers once for the primary user.
mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler());
mContext.getContentResolver().registerContentObserver(
CallLog.CONTENT_URI, /* notifyForDescendants= */ true,
mCallLogContentObserver, UserHandle.USER_SYSTEM);
mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler());
mContext.getContentResolver().registerContentObserver(
MmsSms.CONTENT_URI, /* notifyForDescendants= */ false,
mMmsSmsContentObserver, UserHandle.USER_SYSTEM);
}
DataMaintenanceService.scheduleJob(mContext, userId);
}
}
private int mimeTypeToShareEventType(String mimeType) { private int mimeTypeToShareEventType(String mimeType) {
if (mimeType.startsWith("text/")) { if (mimeType.startsWith("text/")) {
return Event.TYPE_SHARE_TEXT; return Event.TYPE_SHARE_TEXT;

View File

@@ -17,9 +17,9 @@
package com.android.server.people.data; package com.android.server.people.data;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.net.Uri; import android.net.Uri;
import android.util.ArrayMap; import android.util.ArrayMap;
@@ -90,10 +90,8 @@ class EventStore {
* Loads existing {@link EventHistoryImpl}s from disk. This should be called when device powers * Loads existing {@link EventHistoryImpl}s from disk. This should be called when device powers
* on and user is unlocked. * on and user is unlocked.
*/ */
@MainThread @WorkerThread
void loadFromDisk() { synchronized void loadFromDisk() {
mScheduledExecutorService.execute(() -> {
synchronized (this) {
for (@EventCategory int category = 0; category < mEventsCategoryDirs.size(); for (@EventCategory int category = 0; category < mEventsCategoryDirs.size();
category++) { category++) {
File categoryDir = mEventsCategoryDirs.get(category); File categoryDir = mEventsCategoryDirs.get(category);
@@ -103,8 +101,6 @@ class EventStore {
mEventHistoryMaps.get(category).putAll(existingEventHistoriesImpl); mEventHistoryMaps.get(category).putAll(existingEventHistoriesImpl);
} }
} }
});
}
/** /**
* Flushes all {@link EventHistoryImpl}s to disk. Should be called when device is shutting down. * Flushes all {@link EventHistoryImpl}s to disk. Should be called when device is shutting down.

View File

@@ -25,6 +25,7 @@ import static com.android.server.people.data.EventStore.CATEGORY_SMS;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.content.LocusId; import android.content.LocusId;
import android.os.FileUtils; import android.os.FileUtils;
import android.text.TextUtils; import android.text.TextUtils;
@@ -77,6 +78,7 @@ public class PackageData {
* Returns a map of package directory names as keys and their associated {@link PackageData}. * Returns a map of package directory names as keys and their associated {@link PackageData}.
* This should be called when device is powered on and unlocked. * This should be called when device is powered on and unlocked.
*/ */
@WorkerThread
@NonNull @NonNull
static Map<String, PackageData> packagesDataFromDisk(@UserIdInt int userId, static Map<String, PackageData> packagesDataFromDisk(@UserIdInt int userId,
@NonNull Predicate<String> isDefaultDialerPredicate, @NonNull Predicate<String> isDefaultDialerPredicate,

View File

@@ -19,6 +19,7 @@ package com.android.server.people.data;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.os.Environment; import android.os.Environment;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
@@ -75,12 +76,6 @@ class UserData {
void setUserUnlocked() { void setUserUnlocked() {
mIsUnlocked = true; mIsUnlocked = true;
// Ensures per user root directory for people data is present, and attempt to load
// data from disk.
mPerUserPeopleDataDir.mkdirs();
mPackageDataMap.putAll(PackageData.packagesDataFromDisk(mUserId, this::isDefaultDialer,
this::isDefaultSmsApp, mScheduledExecutorService, mPerUserPeopleDataDir));
} }
void setUserStopped() { void setUserStopped() {
@@ -91,6 +86,15 @@ class UserData {
return mIsUnlocked; return mIsUnlocked;
} }
@WorkerThread
void loadUserData() {
mPerUserPeopleDataDir.mkdir();
Map<String, PackageData> packageDataMap = PackageData.packagesDataFromDisk(
mUserId, this::isDefaultDialer, this::isDefaultSmsApp, mScheduledExecutorService,
mPerUserPeopleDataDir);
mPackageDataMap.putAll(packageDataMap);
}
/** /**
* Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise * Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise
* creates a new instance and returns it. * creates a new instance and returns it.

View File

@@ -273,9 +273,8 @@ public final class ConversationStoreTest {
// Ensure that futures were cancelled and the immediate flush occurred. // Ensure that futures were cancelled and the immediate flush occurred.
assertEquals(0, mMockScheduledExecutorService.getFutures().size()); assertEquals(0, mMockScheduledExecutorService.getFutures().size());
// Expect to see 2 executes: loadConversationFromDisk and saveConversationsToDisk. // Expect to see 1 execute: saveConversationsToDisk.
// loadConversationFromDisk gets called each time we call #resetConversationStore(). assertEquals(1, mMockScheduledExecutorService.getExecutes().size());
assertEquals(2, mMockScheduledExecutorService.getExecutes().size());
resetConversationStore(); resetConversationStore();
ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID); ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID);

View File

@@ -183,6 +183,11 @@ public final class DataManagerTest {
when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any( when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(
TimeUnit.class))).thenReturn(mScheduledFuture); TimeUnit.class))).thenReturn(mScheduledFuture);
doAnswer(ans -> {
Runnable runnable = (Runnable) ans.getArguments()[0];
runnable.run();
return null;
}).when(mExecutorService).execute(any(Runnable.class));
when(mUserManager.getEnabledProfiles(USER_ID_PRIMARY)) when(mUserManager.getEnabledProfiles(USER_ID_PRIMARY))
.thenReturn(Arrays.asList( .thenReturn(Arrays.asList(