summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java102
-rw-r--r--services/people/java/com/android/server/people/prediction/AppTargetPredictor.java6
-rw-r--r--services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java138
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java186
5 files changed, 392 insertions, 130 deletions
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 6b97c98b0029..da971da46350 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -51,7 +51,6 @@ import android.provider.Telephony.MmsSms;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -275,40 +274,35 @@ public class DataManager {
mContext.getPackageName(), intentFilter, callingUserId);
}
- /** Reports the {@link AppTargetEvent} from App Prediction Manager. */
- public void reportAppTargetEvent(@NonNull AppTargetEvent event,
+ /** Reports the sharing related {@link AppTargetEvent} from App Prediction Manager. */
+ public void reportShareTargetEvent(@NonNull AppTargetEvent event,
@Nullable IntentFilter intentFilter) {
AppTarget appTarget = event.getTarget();
- ShortcutInfo shortcutInfo = appTarget != null ? appTarget.getShortcutInfo() : null;
- if (shortcutInfo == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
- return;
- }
- PackageData packageData = getPackage(appTarget.getPackageName(),
- appTarget.getUser().getIdentifier());
- if (packageData == null) {
+ if (appTarget == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
return;
}
+ UserData userData = getUnlockedUserData(appTarget.getUser().getIdentifier());
+ PackageData packageData = userData.getOrCreatePackageData(appTarget.getPackageName());
+ String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
+ @Event.EventType int eventType = mimeTypeToShareEventType(mimeType);
+ EventHistoryImpl eventHistory;
if (ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE.equals(event.getLaunchLocation())) {
- String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
- String shortcutId = shortcutInfo.getId();
- if (packageData.getConversationStore().getConversation(shortcutId) == null
- || TextUtils.isEmpty(mimeType)) {
+ // Direct share event
+ if (appTarget.getShortcutInfo() == null) {
return;
}
- EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
- EventStore.CATEGORY_SHORTCUT_BASED, shortcutInfo.getId());
- @Event.EventType int eventType;
- if (mimeType.startsWith("text/")) {
- eventType = Event.TYPE_SHARE_TEXT;
- } else if (mimeType.startsWith("image/")) {
- eventType = Event.TYPE_SHARE_IMAGE;
- } else if (mimeType.startsWith("video/")) {
- eventType = Event.TYPE_SHARE_VIDEO;
- } else {
- eventType = Event.TYPE_SHARE_OTHER;
+ String shortcutId = appTarget.getShortcutInfo().getId();
+ if (packageData.getConversationStore().getConversation(shortcutId) == null) {
+ addOrUpdateConversationInfo(appTarget.getShortcutInfo());
}
- eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType));
+ eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
+ } else {
+ // App share event
+ eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_CLASS_BASED, appTarget.getClassName());
}
+ eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType));
}
/** Prunes the data for the specified user. */
@@ -335,6 +329,17 @@ public class DataManager {
});
}
+ private int mimeTypeToShareEventType(String mimeType) {
+ if (mimeType.startsWith("text/")) {
+ return Event.TYPE_SHARE_TEXT;
+ } else if (mimeType.startsWith("image/")) {
+ return Event.TYPE_SHARE_IMAGE;
+ } else if (mimeType.startsWith("video/")) {
+ return Event.TYPE_SHARE_VIDEO;
+ }
+ return Event.TYPE_SHARE_OTHER;
+ }
+
private void pruneUninstalledPackageData(@NonNull UserData userData) {
Set<String> installApps = new ArraySet<>();
mPackageManagerInternal.forEachInstalledPackage(
@@ -410,12 +415,13 @@ public class DataManager {
EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
}
+ private boolean isPersonShortcut(@NonNull ShortcutInfo shortcutInfo) {
+ return shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0;
+ }
+
@VisibleForTesting
@WorkerThread
- void onShortcutAddedOrUpdated(@NonNull ShortcutInfo shortcutInfo) {
- if (shortcutInfo.getPersons() == null || shortcutInfo.getPersons().length == 0) {
- return;
- }
+ void addOrUpdateConversationInfo(@NonNull ShortcutInfo shortcutInfo) {
UserData userData = getUnlockedUserData(shortcutInfo.getUserId());
if (userData == null) {
return;
@@ -431,24 +437,24 @@ public class DataManager {
builder.setShortcutId(shortcutInfo.getId());
builder.setLocusId(shortcutInfo.getLocusId());
builder.setShortcutFlags(shortcutInfo.getFlags());
-
- Person person = shortcutInfo.getPersons()[0];
- builder.setPersonImportant(person.isImportant());
- builder.setPersonBot(person.isBot());
- String contactUri = person.getUri();
- if (contactUri != null) {
- ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext);
- if (helper.query(contactUri)) {
- builder.setContactUri(helper.getContactUri());
- builder.setContactStarred(helper.isStarred());
- builder.setContactPhoneNumber(helper.getPhoneNumber());
+ builder.setContactUri(null);
+ builder.setContactPhoneNumber(null);
+ builder.setContactStarred(false);
+
+ if (shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0) {
+ Person person = shortcutInfo.getPersons()[0];
+ builder.setPersonImportant(person.isImportant());
+ builder.setPersonBot(person.isBot());
+ String contactUri = person.getUri();
+ if (contactUri != null) {
+ ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext);
+ if (helper.query(contactUri)) {
+ builder.setContactUri(helper.getContactUri());
+ builder.setContactStarred(helper.isStarred());
+ builder.setContactPhoneNumber(helper.getPhoneNumber());
+ }
}
- } else {
- builder.setContactUri(null);
- builder.setContactPhoneNumber(null);
- builder.setContactStarred(false);
}
-
conversationStore.addOrUpdate(builder.build());
}
@@ -626,7 +632,9 @@ public class DataManager {
List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId,
/*shortcutIds=*/ null);
for (ShortcutInfo shortcut : shortcuts) {
- onShortcutAddedOrUpdated(shortcut);
+ if (isPersonShortcut(shortcut)) {
+ addOrUpdateConversationInfo(shortcut);
+ }
}
});
}
diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
index 19cf8af5d66b..c89dadc3fbd6 100644
--- a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
@@ -73,6 +73,7 @@ public class AppTargetPredictor {
*/
@MainThread
public void onAppTargetEvent(AppTargetEvent event) {
+ mCallbackExecutor.execute(() -> reportAppTargetEvent(event));
}
/**
@@ -104,6 +105,11 @@ public class AppTargetPredictor {
return mUpdatePredictionsMethod;
}
+ /** To be overridden by the subclass to report app target event. */
+ @WorkerThread
+ void reportAppTargetEvent(AppTargetEvent event) {
+ }
+
/** To be overridden by the subclass to predict the targets. */
@WorkerThread
void predictTargets() {
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 90d821641149..8e5d75be12b7 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -16,7 +16,6 @@
package com.android.server.people.prediction;
-import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -28,15 +27,18 @@ import android.app.prediction.AppTargetId;
import android.content.IntentFilter;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
+import android.util.Range;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ChooserActivity;
import com.android.server.people.data.ConversationInfo;
import com.android.server.people.data.DataManager;
+import com.android.server.people.data.Event;
import com.android.server.people.data.EventHistory;
import com.android.server.people.data.PackageData;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -52,89 +54,139 @@ class ShareTargetPredictor extends AppTargetPredictor {
ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
}
- @MainThread
+ /** Reports chosen history of direct/app share targets. */
+ @WorkerThread
@Override
- public void onAppTargetEvent(AppTargetEvent event) {
- getDataManager().reportAppTargetEvent(event, mIntentFilter);
+ void reportAppTargetEvent(AppTargetEvent event) {
+ getDataManager().reportShareTargetEvent(event, mIntentFilter);
}
+ /** Provides prediction on direct share targets */
@WorkerThread
@Override
- protected void predictTargets() {
- List<ShareTarget> shareTargets = getShareTargets();
- // TODO: Rank the share targets with the data in ShareTarget.mConversationData.
- List<AppTarget> appTargets = new ArrayList<>();
- for (ShareTarget shareTarget : shareTargets) {
-
- ShortcutInfo shortcutInfo = shareTarget.getShareShortcutInfo().getShortcutInfo();
- AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
- String shareTargetClassName =
- shareTarget.getShareShortcutInfo().getTargetComponent().getClassName();
- AppTarget appTarget = new AppTarget.Builder(appTargetId, shortcutInfo)
- .setClassName(shareTargetClassName)
- .build();
- appTargets.add(appTarget);
- if (appTargets.size() >= getPredictionContext().getPredictedTargetCount()) {
- break;
- }
+ void predictTargets() {
+ List<ShareTarget> shareTargets = getDirectShareTargets();
+ rankTargets(shareTargets);
+ List<AppTarget> res = new ArrayList<>();
+ for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(),
+ shareTargets.size()); i++) {
+ res.add(shareTargets.get(i).getAppTarget());
}
- updatePredictions(appTargets);
+ updatePredictions(res);
}
- @VisibleForTesting
- List<ShareTarget> getShareTargets() {
+ /** Provides prediction on app share targets */
+ @WorkerThread
+ @Override
+ void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
+ List<ShareTarget> shareTargets = getAppShareTargets(targets);
+ rankTargets(shareTargets);
+ List<AppTarget> appTargetList = new ArrayList<>();
+ shareTargets.forEach(t -> appTargetList.add(t.getAppTarget()));
+ callback.accept(appTargetList);
+ }
+
+ private void rankTargets(List<ShareTarget> shareTargets) {
+ // Rank targets based on recency of sharing history only for the moment.
+ // TODO: Take more factors into ranking, e.g. frequency, mime type, foreground app.
+ Collections.sort(shareTargets, (t1, t2) -> {
+ if (t1.getEventHistory() == null) {
+ return 1;
+ }
+ if (t2.getEventHistory() == null) {
+ return -1;
+ }
+ Range<Long> timeSlot1 = t1.getEventHistory().getEventIndex(
+ Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
+ Range<Long> timeSlot2 = t2.getEventHistory().getEventIndex(
+ Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
+ if (timeSlot1 == null) {
+ return 1;
+ } else if (timeSlot2 == null) {
+ return -1;
+ } else {
+ return -Long.compare(timeSlot1.getUpper(), timeSlot2.getUpper());
+ }
+ });
+ }
+
+ private List<ShareTarget> getDirectShareTargets() {
List<ShareTarget> shareTargets = new ArrayList<>();
List<ShareShortcutInfo> shareShortcuts =
getDataManager().getShareShortcuts(mIntentFilter, mCallingUserId);
for (ShareShortcutInfo shareShortcut : shareShortcuts) {
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
+ AppTarget appTarget = new AppTarget.Builder(
+ new AppTargetId(shortcutInfo.getId()),
+ shortcutInfo)
+ .setClassName(shareShortcut.getTargetComponent().getClassName())
+ .build();
String packageName = shortcutInfo.getPackage();
int userId = shortcutInfo.getUserId();
PackageData packageData = getDataManager().getPackage(packageName, userId);
- ConversationData conversationData = null;
+ ConversationInfo conversationInfo = null;
+ EventHistory eventHistory = null;
if (packageData != null) {
String shortcutId = shortcutInfo.getId();
- ConversationInfo conversationInfo =
- packageData.getConversationInfo(shortcutId);
-
+ conversationInfo = packageData.getConversationInfo(shortcutId);
if (conversationInfo != null) {
- EventHistory eventHistory = packageData.getEventHistory(shortcutId);
- conversationData = new ConversationData(
- packageName, userId, conversationInfo, eventHistory);
+ eventHistory = packageData.getEventHistory(shortcutId);
}
}
- shareTargets.add(new ShareTarget(shareShortcut, conversationData));
+ shareTargets.add(new ShareTarget(appTarget, eventHistory, conversationInfo));
}
return shareTargets;
}
+ private List<ShareTarget> getAppShareTargets(List<AppTarget> targets) {
+ List<ShareTarget> shareTargets = new ArrayList<>();
+ for (AppTarget target : targets) {
+ PackageData packageData = getDataManager().getPackage(target.getPackageName(),
+ target.getUser().getIdentifier());
+ shareTargets.add(new ShareTarget(target,
+ packageData == null ? null
+ : packageData.getClassLevelEventHistory(target.getClassName()), null));
+ }
+ return shareTargets;
+ }
+
@VisibleForTesting
static class ShareTarget {
@NonNull
- private final ShareShortcutInfo mShareShortcutInfo;
+ private final AppTarget mAppTarget;
@Nullable
- private final ConversationData mConversationData;
-
- private ShareTarget(@NonNull ShareShortcutInfo shareShortcutInfo,
- @Nullable ConversationData conversationData) {
- mShareShortcutInfo = shareShortcutInfo;
- mConversationData = conversationData;
+ private final EventHistory mEventHistory;
+ @Nullable
+ private final ConversationInfo mConversationInfo;
+
+ private ShareTarget(@NonNull AppTarget appTarget,
+ @Nullable EventHistory eventHistory,
+ @Nullable ConversationInfo conversationInfo) {
+ mAppTarget = appTarget;
+ mEventHistory = eventHistory;
+ mConversationInfo = conversationInfo;
}
@NonNull
@VisibleForTesting
- ShareShortcutInfo getShareShortcutInfo() {
- return mShareShortcutInfo;
+ AppTarget getAppTarget() {
+ return mAppTarget;
+ }
+
+ @Nullable
+ @VisibleForTesting
+ EventHistory getEventHistory() {
+ return mEventHistory;
}
@Nullable
@VisibleForTesting
- ConversationData getConversationData() {
- return mConversationData;
+ ConversationInfo getConversationInfo() {
+ return mConversationInfo;
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index f73a4b5776b6..b54317b768c5 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -100,6 +100,7 @@ public final class DataManagerTest {
private static final int USER_ID_PRIMARY_MANAGED = 10;
private static final int USER_ID_SECONDARY = 11;
private static final String TEST_PKG_NAME = "pkg";
+ private static final String TEST_CLASS_NAME = "class";
private static final String TEST_SHORTCUT_ID = "sc";
private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
private static final String PHONE_NUMBER = "+1234567890";
@@ -206,13 +207,13 @@ public final class DataManagerTest {
mDataManager.onUserUnlocked(USER_ID_PRIMARY_MANAGED);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
- mDataManager.onShortcutAddedOrUpdated(
+ mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1",
buildPerson(true, false)));
- mDataManager.onShortcutAddedOrUpdated(
+ mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2",
buildPerson(false, true)));
- mDataManager.onShortcutAddedOrUpdated(
+ mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_3", USER_ID_SECONDARY, "sc_3", buildPerson()));
List<ConversationInfo> conversations = getConversationsInPrimary();
@@ -236,9 +237,9 @@ public final class DataManagerTest {
@Test
public void testAccessConversationForUnlockedUsersOnly() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
- mDataManager.onShortcutAddedOrUpdated(
+ mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson()));
- mDataManager.onShortcutAddedOrUpdated(
+ mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson()));
List<ConversationInfo> conversations = getConversationsInPrimary();
@@ -261,11 +262,12 @@ public final class DataManagerTest {
}
@Test
- public void testReportAppTargetEvent() throws IntentFilter.MalformedMimeTypeException {
+ public void testReportAppTargetEvent_directSharing()
+ throws IntentFilter.MalformedMimeTypeException {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
.build();
@@ -274,7 +276,55 @@ public final class DataManagerTest {
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
.build();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
- mDataManager.reportAppTargetEvent(appTargetEvent, intentFilter);
+ mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
+
+ List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
+ Event.SHARE_EVENT_TYPES);
+ assertEquals(1, activeShareTimeSlots.size());
+ }
+
+ @Test
+ public void testReportAppTargetEvent_directSharing_createConversation()
+ throws IntentFilter.MalformedMimeTypeException {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ null);
+ AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
+ .build();
+ AppTargetEvent appTargetEvent =
+ new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
+ .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
+ .build();
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
+
+ mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
+
+ List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
+ Event.SHARE_EVENT_TYPES);
+ assertEquals(1, activeShareTimeSlots.size());
+ ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+ .getConversationStore()
+ .getConversation(TEST_SHORTCUT_ID);
+ assertNotNull(conversationInfo);
+ assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID);
+ }
+
+ @Test
+ public void testReportAppTargetEvent_appSharing()
+ throws IntentFilter.MalformedMimeTypeException {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ AppTarget appTarget = new AppTarget.Builder(
+ new AppTargetId(TEST_SHORTCUT_ID),
+ TEST_PKG_NAME,
+ UserHandle.of(USER_ID_PRIMARY))
+ .setClassName(TEST_CLASS_NAME)
+ .build();
+ AppTargetEvent appTargetEvent =
+ new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
+ .build();
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
+
+ mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
Event.SHARE_EVENT_TYPES);
@@ -288,7 +338,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
final String newPhoneNumber = "+1000000000";
mInjector.mContactsQueryHelper.mIsStarred = true;
@@ -312,7 +362,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -330,7 +380,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -350,7 +400,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -375,7 +425,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -401,7 +451,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -430,7 +480,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
ContentObserver contentObserver = mDataManager.getCallLogContentObserverForTesting();
contentObserver.onChange(false);
@@ -453,7 +503,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
ContentObserver contentObserver = mDataManager.getMmsSmsContentObserverForTesting();
@@ -476,7 +526,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
PackageMonitor packageMonitor = mDataManager.getPackageMonitorForTesting(USER_ID_PRIMARY);
@@ -493,7 +543,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
doAnswer(ans -> null).when(mPackageManagerInternal)
@@ -508,7 +558,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
long currentTimestamp = System.currentTimeMillis();
mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
@@ -529,7 +579,7 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
long currentTimestamp = System.currentTimeMillis();
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index f498a9450c9e..c6cd34732acf 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -16,16 +16,19 @@
package com.android.server.people.prediction;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -33,22 +36,26 @@ import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.os.Bundle;
import android.os.UserHandle;
+import android.util.Range;
import com.android.server.people.data.ConversationInfo;
import com.android.server.people.data.DataManager;
import com.android.server.people.data.EventHistory;
+import com.android.server.people.data.EventIndex;
import com.android.server.people.data.PackageData;
-import com.android.server.people.prediction.ShareTargetPredictor.ShareTarget;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
@RunWith(JUnit4.class)
public final class ShareTargetPredictorTest {
@@ -57,17 +64,32 @@ public final class ShareTargetPredictorTest {
private static final int NUM_PREDICTED_TARGETS = 5;
private static final int USER_ID = 0;
private static final String PACKAGE_1 = "pkg1";
- private static final String CLASS_1 = "cls1";
private static final String PACKAGE_2 = "pkg2";
+ private static final String PACKAGE_3 = "pkg3";
+ private static final String CLASS_1 = "cls1";
private static final String CLASS_2 = "cls2";
@Mock private Context mContext;
@Mock private DataManager mDataManager;
+ @Mock private Consumer<List<AppTarget>> mUpdatePredictionsMethod;
@Mock private PackageData mPackageData1;
@Mock private PackageData mPackageData2;
+ @Mock private EventHistory mEventHistory1;
+ @Mock private EventHistory mEventHistory2;
+ @Mock private EventHistory mEventHistory3;
+ @Mock private EventHistory mEventHistory4;
+ @Mock private EventHistory mEventHistory5;
+ @Mock private EventHistory mEventHistory6;
- private List<ShareShortcutInfo> mShareShortcuts = new ArrayList<>();
+ @Mock private EventIndex mEventIndex1;
+ @Mock private EventIndex mEventIndex2;
+ @Mock private EventIndex mEventIndex3;
+ @Mock private EventIndex mEventIndex4;
+ @Mock private EventIndex mEventIndex5;
+ @Mock private EventIndex mEventIndex6;
+ @Captor private ArgumentCaptor<List<AppTarget>> mAppTargetCaptor;
+ private List<ShareShortcutInfo> mShareShortcuts = new ArrayList<>();
private ShareTargetPredictor mPredictor;
@Before
@@ -84,11 +106,11 @@ public final class ShareTargetPredictorTest {
.setExtras(new Bundle())
.build();
mPredictor = new ShareTargetPredictor(
- predictionContext, targets -> { }, mDataManager, USER_ID);
+ predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID);
}
@Test
- public void testGetShareTargets() {
+ public void testPredictTargets() {
mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
@@ -99,24 +121,148 @@ public final class ShareTargetPredictorTest {
when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class));
// "sc4" does not have a ConversationInfo.
- when(mPackageData1.getEventHistory(anyString())).thenReturn(mock(EventHistory.class));
- when(mPackageData2.getEventHistory(anyString())).thenReturn(mock(EventHistory.class));
+ when(mPackageData1.getEventHistory("sc1")).thenReturn(mEventHistory1);
+ when(mPackageData1.getEventHistory("sc2")).thenReturn(mEventHistory2);
+ when(mPackageData2.getEventHistory("sc3")).thenReturn(mEventHistory3);
+ when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+ when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+ when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+ when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
+ when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
+ when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
- List<ShareTarget> shareTargets = mPredictor.getShareTargets();
+ mPredictor.predictTargets();
- assertEquals(4, shareTargets.size());
+ verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+ List<AppTarget> res = mAppTargetCaptor.getValue();
+ assertEquals(4, res.size());
+
+ assertEquals("sc3", res.get(0).getId().getId());
+ assertEquals(CLASS_2, res.get(0).getClassName());
+ assertEquals(PACKAGE_2, res.get(0).getPackageName());
+
+ assertEquals("sc2", res.get(1).getId().getId());
+ assertEquals(CLASS_1, res.get(1).getClassName());
+ assertEquals(PACKAGE_1, res.get(1).getPackageName());
+
+ assertEquals("sc1", res.get(2).getId().getId());
+ assertEquals(CLASS_1, res.get(2).getClassName());
+ assertEquals(PACKAGE_1, res.get(2).getPackageName());
+
+ assertEquals("sc4", res.get(3).getId().getId());
+ assertEquals(CLASS_2, res.get(3).getClassName());
+ assertEquals(PACKAGE_2, res.get(3).getPackageName());
+ }
+
+ @Test
+ public void testPredictTargets_reachTargetsLimit() {
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc5"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc6"));
+
+ when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData2.getConversationInfo("sc4")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData1.getConversationInfo("sc5")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData2.getConversationInfo("sc6")).thenReturn(mock(ConversationInfo.class));
+
+ when(mPackageData1.getEventHistory("sc1")).thenReturn(mEventHistory1);
+ when(mPackageData1.getEventHistory("sc2")).thenReturn(mEventHistory2);
+ when(mPackageData2.getEventHistory("sc3")).thenReturn(mEventHistory3);
+ when(mPackageData2.getEventHistory("sc4")).thenReturn(mEventHistory4);
+ when(mPackageData1.getEventHistory("sc5")).thenReturn(mEventHistory5);
+ when(mPackageData2.getEventHistory("sc6")).thenReturn(mEventHistory6);
+
+ when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+ when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+ when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+ when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+ when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+ when(mEventHistory6.getEventIndex(anySet())).thenReturn(mEventIndex6);
+ when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
+ when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
+ when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
+ when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(4L, 5L));
+ when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(5L, 6L));
+ when(mEventIndex6.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(6L, 7L));
+
+ mPredictor.predictTargets();
+
+ verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+ List<AppTarget> res = mAppTargetCaptor.getValue();
+ assertEquals(5, res.size());
+
+ assertEquals("sc6", res.get(0).getId().getId());
+ assertEquals(CLASS_2, res.get(0).getClassName());
+ assertEquals(PACKAGE_2, res.get(0).getPackageName());
+
+ assertEquals("sc5", res.get(1).getId().getId());
+ assertEquals(CLASS_1, res.get(1).getClassName());
+ assertEquals(PACKAGE_1, res.get(1).getPackageName());
+
+ assertEquals("sc4", res.get(2).getId().getId());
+ assertEquals(CLASS_2, res.get(2).getClassName());
+ assertEquals(PACKAGE_2, res.get(2).getPackageName());
+
+ assertEquals("sc3", res.get(3).getId().getId());
+ assertEquals(CLASS_2, res.get(3).getClassName());
+ assertEquals(PACKAGE_2, res.get(3).getPackageName());
+
+ assertEquals("sc2", res.get(4).getId().getId());
+ assertEquals(CLASS_1, res.get(4).getClassName());
+ assertEquals(PACKAGE_1, res.get(4).getPackageName());
+ }
+
+ @Test
+ public void testSortTargets() {
+ AppTarget appTarget1 = new AppTarget.Builder(
+ new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
+ .setClassName(CLASS_1)
+ .build();
+ AppTarget appTarget2 = new AppTarget.Builder(
+ new AppTargetId("cls2#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
+ .setClassName(CLASS_2)
+ .build();
+ AppTarget appTarget3 = new AppTarget.Builder(
+ new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
+ .setClassName(CLASS_1)
+ .build();
+ AppTarget appTarget4 = new AppTarget.Builder(
+ new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
+ .setClassName(CLASS_2)
+ .build();
+ AppTarget appTarget5 = new AppTarget.Builder(
+ new AppTargetId("cls1#pkg3"), PACKAGE_3, UserHandle.of(USER_ID))
+ .setClassName(CLASS_1)
+ .build();
- assertEquals("sc1", shareTargets.get(0).getShareShortcutInfo().getShortcutInfo().getId());
- assertNotNull(shareTargets.get(0).getConversationData());
+ when(mPackageData1.getClassLevelEventHistory(CLASS_1)).thenReturn(mEventHistory1);
+ when(mPackageData1.getClassLevelEventHistory(CLASS_2)).thenReturn(mEventHistory2);
+ when(mPackageData2.getClassLevelEventHistory(CLASS_1)).thenReturn(mEventHistory3);
+ when(mPackageData2.getClassLevelEventHistory(CLASS_2)).thenReturn(mEventHistory4);
+ // PackageData of PACKAGE_3 is empty.
+ when(mDataManager.getPackage(PACKAGE_3, USER_ID)).thenReturn(null);
- assertEquals("sc2", shareTargets.get(1).getShareShortcutInfo().getShortcutInfo().getId());
- assertNotNull(shareTargets.get(1).getConversationData());
+ when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+ when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+ when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+ when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+ when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
+ when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
+ when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
+ when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(4L, 5L));
- assertEquals("sc3", shareTargets.get(2).getShareShortcutInfo().getShortcutInfo().getId());
- assertNotNull(shareTargets.get(2).getConversationData());
+ mPredictor.sortTargets(
+ List.of(appTarget1, appTarget2, appTarget3, appTarget4, appTarget5),
+ mUpdatePredictionsMethod);
- assertEquals("sc4", shareTargets.get(3).getShareShortcutInfo().getShortcutInfo().getId());
- assertNull(shareTargets.get(3).getConversationData());
+ verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+ assertThat(mAppTargetCaptor.getValue()).containsExactly(
+ appTarget4, appTarget3, appTarget2, appTarget1, appTarget5);
}
private ShareShortcutInfo buildShareShortcut(