summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/people/java/com/android/server/people/SessionInfo.java10
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java45
-rw-r--r--services/people/java/com/android/server/people/data/PackageData.java10
-rw-r--r--services/people/java/com/android/server/people/prediction/AppTargetPredictor.java128
-rw-r--r--services/people/java/com/android/server/people/prediction/ConversationPredictor.java142
-rw-r--r--services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java139
-rw-r--r--services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java137
9 files changed, 435 insertions, 205 deletions
diff --git a/services/people/java/com/android/server/people/SessionInfo.java b/services/people/java/com/android/server/people/SessionInfo.java
index eb08e03c14de..eaa0781f12ef 100644
--- a/services/people/java/com/android/server/people/SessionInfo.java
+++ b/services/people/java/com/android/server/people/SessionInfo.java
@@ -25,7 +25,7 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.people.data.DataManager;
-import com.android.server.people.prediction.ConversationPredictor;
+import com.android.server.people.prediction.AppTargetPredictor;
import java.util.List;
@@ -34,12 +34,12 @@ class SessionInfo {
private static final String TAG = "SessionInfo";
- private final ConversationPredictor mConversationPredictor;
+ private final AppTargetPredictor mAppTargetPredictor;
private final RemoteCallbackList<IPredictionCallback> mCallbacks =
new RemoteCallbackList<>();
SessionInfo(AppPredictionContext predictionContext, DataManager dataManager) {
- mConversationPredictor = new ConversationPredictor(predictionContext,
+ mAppTargetPredictor = AppTargetPredictor.create(predictionContext,
this::updatePredictions, dataManager);
}
@@ -51,8 +51,8 @@ class SessionInfo {
mCallbacks.unregister(callback);
}
- ConversationPredictor getPredictor() {
- return mConversationPredictor;
+ AppTargetPredictor getPredictor() {
+ return mAppTargetPredictor;
}
void onDestroy() {
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 13cce414ea7c..8a2a0c77a4dc 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -59,7 +59,6 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.SmsApplication;
import com.android.server.LocalServices;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
@@ -199,6 +198,13 @@ public class DataManager {
}
}
+ /** Gets the {@link PackageData} for the given package and user. */
+ @Nullable
+ public PackageData getPackage(@NonNull String packageName, @UserIdInt int userId) {
+ UserData userData = getUnlockedUserData(userId);
+ return userData != null ? userData.getPackageData(packageName) : null;
+ }
+
/** Gets the {@link ShortcutInfo} for the given shortcut ID. */
@Nullable
public ShortcutInfo getShortcut(@NonNull String packageName, @UserIdInt int userId,
@@ -212,20 +218,11 @@ public class DataManager {
}
/**
- * Gets the conversation {@link ShareShortcutInfo}s from all packages owned by the calling user
- * that match the specified {@link IntentFilter}.
+ * Gets the {@link ShareShortcutInfo}s from all packages owned by the calling user that match
+ * the specified {@link IntentFilter}.
*/
- public List<ShareShortcutInfo> getConversationShareTargets(
- @NonNull IntentFilter intentFilter) {
- List<ShareShortcutInfo> shareShortcuts = mShortcutManager.getShareTargets(intentFilter);
- List<ShareShortcutInfo> result = new ArrayList<>();
- for (ShareShortcutInfo shareShortcut : shareShortcuts) {
- ShortcutInfo si = shareShortcut.getShortcutInfo();
- if (getConversationInfo(si.getPackage(), si.getUserId(), si.getId()) != null) {
- result.add(shareShortcut);
- }
- }
- return result;
+ public List<ShareShortcutInfo> getShareShortcuts(@NonNull IntentFilter intentFilter) {
+ return mShortcutManager.getShareTargets(intentFilter);
}
/** Reports the {@link AppTargetEvent} from App Prediction Manager. */
@@ -236,7 +233,7 @@ public class DataManager {
if (shortcutInfo == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
return;
}
- PackageData packageData = getPackageData(appTarget.getPackageName(),
+ PackageData packageData = getPackage(appTarget.getPackageName(),
appTarget.getUser().getIdentifier());
if (packageData == null) {
return;
@@ -283,20 +280,6 @@ public class DataManager {
return userData != null && userData.isUnlocked() ? userData : null;
}
- @Nullable
- private PackageData getPackageData(@NonNull String packageName, int userId) {
- UserData userData = getUnlockedUserData(userId);
- return userData != null ? userData.getPackageData(packageName) : null;
- }
-
- @Nullable
- private ConversationInfo getConversationInfo(@NonNull String packageName, @UserIdInt int userId,
- @NonNull String shortcutId) {
- PackageData packageData = getPackageData(packageName, userId);
- return packageData != null ? packageData.getConversationStore().getConversation(shortcutId)
- : null;
- }
-
private void updateDefaultDialer(@NonNull UserData userData) {
TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
String defaultDialer = telecomManager != null
@@ -318,7 +301,7 @@ public class DataManager {
if (shortcutId == null) {
return null;
}
- PackageData packageData = getPackageData(sbn.getPackageName(),
+ PackageData packageData = getPackage(sbn.getPackageName(),
sbn.getUser().getIdentifier());
if (packageData == null
|| packageData.getConversationStore().getConversation(shortcutId) == null) {
@@ -382,7 +365,7 @@ public class DataManager {
usageEvents.getNextEvent(e);
String packageName = e.getPackageName();
- PackageData packageData = getPackageData(packageName, userId);
+ PackageData packageData = getPackage(packageName, userId);
if (packageData == null) {
continue;
}
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
index 9c22a7f1c484..35b65ecb0ebc 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -17,6 +17,7 @@
package com.android.server.people.data;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.LocusId;
import android.text.TextUtils;
@@ -68,6 +69,15 @@ public class PackageData {
}
/**
+ * Gets the {@link ConversationInfo} for a given shortcut ID. Returns null if such as {@link
+ * ConversationInfo} does not exist.
+ */
+ @Nullable
+ public ConversationInfo getConversationInfo(@NonNull String shortcutId) {
+ return getConversationStore().getConversation(shortcutId);
+ }
+
+ /**
* Gets the combined {@link EventHistory} for a given shortcut ID. This returned {@link
* EventHistory} has events of all types, no matter whether they're annotated with shortcut ID,
* Locus ID, or phone number etc.
diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
new file mode 100644
index 000000000000..44f3e35833d9
--- /dev/null
+++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 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.server.people.prediction;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.people.data.DataManager;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+/**
+ * Predictor that predicts the {@link AppTarget} the user is most likely to open.
+ */
+public class AppTargetPredictor {
+
+ private static final String UI_SURFACE_SHARE = "share";
+
+ /** Creates a {@link AppTargetPredictor} instance based on the prediction context. */
+ public static AppTargetPredictor create(@NonNull AppPredictionContext predictionContext,
+ @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
+ @NonNull DataManager dataManager) {
+ if (UI_SURFACE_SHARE.equals(predictionContext.getUiSurface())) {
+ return new ShareTargetPredictor(
+ predictionContext, updatePredictionsMethod, dataManager);
+ }
+ return new AppTargetPredictor(predictionContext, updatePredictionsMethod, dataManager);
+ }
+
+ private final AppPredictionContext mPredictionContext;
+ private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
+ private final DataManager mDataManager;
+ private final ExecutorService mCallbackExecutor;
+
+ AppTargetPredictor(@NonNull AppPredictionContext predictionContext,
+ @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
+ @NonNull DataManager dataManager) {
+ mPredictionContext = predictionContext;
+ mUpdatePredictionsMethod = updatePredictionsMethod;
+ mDataManager = dataManager;
+ mCallbackExecutor = Executors.newSingleThreadExecutor();
+ }
+
+ /**
+ * Called by the client app to indicate a target launch.
+ */
+ @MainThread
+ public void onAppTargetEvent(AppTargetEvent event) {
+ }
+
+ /**
+ * Called by the client app to indicate a particular location has been shown to the user.
+ */
+ @MainThread
+ public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {
+ }
+
+ /**
+ * Called by the client app to request sorting of the provided targets based on the prediction
+ * ranking.
+ */
+ @MainThread
+ public void onSortAppTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
+ mCallbackExecutor.execute(() -> sortTargets(targets, callback));
+ }
+
+ /**
+ * Called by the client app to request target predictions.
+ */
+ @MainThread
+ public void onRequestPredictionUpdate() {
+ mCallbackExecutor.execute(this::predictTargets);
+ }
+
+ @VisibleForTesting
+ public Consumer<List<AppTarget>> getUpdatePredictionsMethod() {
+ return mUpdatePredictionsMethod;
+ }
+
+ /** To be overridden by the subclass to predict the targets. */
+ @WorkerThread
+ void predictTargets() {
+ }
+
+ /**
+ * To be overridden by the subclass to sort the provided targets based on the prediction
+ * ranking.
+ */
+ @WorkerThread
+ void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
+ callback.accept(targets);
+ }
+
+ AppPredictionContext getPredictionContext() {
+ return mPredictionContext;
+ }
+
+ DataManager getDataManager() {
+ return mDataManager;
+ }
+
+ void updatePredictions(List<AppTarget> targets) {
+ mUpdatePredictionsMethod.accept(targets);
+ }
+}
diff --git a/services/people/java/com/android/server/people/prediction/ConversationPredictor.java b/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
deleted file mode 100644
index ed8a56bb6435..000000000000
--- a/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2019 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.server.people.prediction;
-
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppTarget;
-import android.app.prediction.AppTargetEvent;
-import android.app.prediction.AppTargetId;
-import android.content.IntentFilter;
-import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager.ShareShortcutInfo;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.ChooserActivity;
-import com.android.server.people.data.DataManager;
-import com.android.server.people.data.EventHistory;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.function.Consumer;
-
-/**
- * Predictor that predicts the conversations or apps the user is most likely to open.
- */
-public class ConversationPredictor {
-
- private static final String UI_SURFACE_SHARE = "share";
-
- private final AppPredictionContext mPredictionContext;
- private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
- private final DataManager mDataManager;
- private final ExecutorService mCallbackExecutor;
- @Nullable
- private final IntentFilter mIntentFilter;
-
- public ConversationPredictor(@NonNull AppPredictionContext predictionContext,
- @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
- @NonNull DataManager dataManager) {
- mPredictionContext = predictionContext;
- mUpdatePredictionsMethod = updatePredictionsMethod;
- mDataManager = dataManager;
- mCallbackExecutor = Executors.newSingleThreadExecutor();
- if (UI_SURFACE_SHARE.equals(mPredictionContext.getUiSurface())) {
- mIntentFilter = mPredictionContext.getExtras().getParcelable(
- ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
- } else {
- mIntentFilter = null;
- }
- }
-
- /**
- * Called by the client app to indicate a target launch.
- */
- @MainThread
- public void onAppTargetEvent(AppTargetEvent event) {
- mDataManager.reportAppTargetEvent(event, mIntentFilter);
- }
-
- /**
- * Called by the client app to indicate a particular location has been shown to the user.
- */
- @MainThread
- public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {}
-
- /**
- * Called by the client app to request sorting of the provided targets based on the prediction
- * ranking.
- */
- @MainThread
- public void onSortAppTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
- mCallbackExecutor.execute(() -> callback.accept(targets));
- }
-
- /**
- * Called by the client app to request target predictions.
- */
- @MainThread
- public void onRequestPredictionUpdate() {
- // TODO: Re-route the call to different ranking classes for different surfaces.
- mCallbackExecutor.execute(() -> {
- List<AppTarget> targets = new ArrayList<>();
- if (mIntentFilter != null) {
- List<ShareShortcutInfo> shareShortcuts =
- mDataManager.getConversationShareTargets(mIntentFilter);
- for (ShareShortcutInfo shareShortcut : shareShortcuts) {
- ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
- AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
- String shareTargetClass = shareShortcut.getTargetComponent().getClassName();
- targets.add(new AppTarget.Builder(appTargetId, shortcutInfo)
- .setClassName(shareTargetClass)
- .build());
- }
- } else {
- List<ConversationData> conversationDataList = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- packageData.forAllConversations(conversationInfo -> {
- EventHistory eventHistory = packageData.getEventHistory(
- conversationInfo.getShortcutId());
- ConversationData conversationData = new ConversationData(
- packageData.getPackageName(), packageData.getUserId(),
- conversationInfo, eventHistory);
- conversationDataList.add(conversationData);
- }));
- for (ConversationData conversationData : conversationDataList) {
- String shortcutId = conversationData.getConversationInfo().getShortcutId();
- ShortcutInfo shortcut = mDataManager.getShortcut(
- conversationData.getPackageName(), conversationData.getUserId(),
- shortcutId);
- if (shortcut != null) {
- AppTargetId appTargetId = new AppTargetId(shortcut.getId());
- targets.add(new AppTarget.Builder(appTargetId, shortcut).build());
- }
- }
- }
- mUpdatePredictionsMethod.accept(targets);
- });
- }
-
- @VisibleForTesting
- public Consumer<List<AppTarget>> getUpdatePredictionsMethod() {
- return mUpdatePredictionsMethod;
- }
-}
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
new file mode 100644
index 000000000000..280ced3a07c5
--- /dev/null
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 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.server.people.prediction;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.content.IntentFilter;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager.ShareShortcutInfo;
+
+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.EventHistory;
+import com.android.server.people.data.PackageData;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+class ShareTargetPredictor extends AppTargetPredictor {
+
+ private final IntentFilter mIntentFilter;
+
+ ShareTargetPredictor(@NonNull AppPredictionContext predictionContext,
+ @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
+ @NonNull DataManager dataManager) {
+ super(predictionContext, updatePredictionsMethod, dataManager);
+ mIntentFilter = predictionContext.getExtras().getParcelable(
+ ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
+ }
+
+ @MainThread
+ @Override
+ public void onAppTargetEvent(AppTargetEvent event) {
+ getDataManager().reportAppTargetEvent(event, mIntentFilter);
+ }
+
+ @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;
+ }
+ }
+ updatePredictions(appTargets);
+ }
+
+ @VisibleForTesting
+ List<ShareTarget> getShareTargets() {
+ List<ShareTarget> shareTargets = new ArrayList<>();
+ List<ShareShortcutInfo> shareShortcuts =
+ getDataManager().getShareShortcuts(mIntentFilter);
+
+ for (ShareShortcutInfo shareShortcut : shareShortcuts) {
+ ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
+ String packageName = shortcutInfo.getPackage();
+ int userId = shortcutInfo.getUserId();
+ PackageData packageData = getDataManager().getPackage(packageName, userId);
+
+ ConversationData conversationData = null;
+ if (packageData != null) {
+ String shortcutId = shortcutInfo.getId();
+ ConversationInfo conversationInfo =
+ packageData.getConversationInfo(shortcutId);
+
+ if (conversationInfo != null) {
+ EventHistory eventHistory = packageData.getEventHistory(shortcutId);
+ conversationData = new ConversationData(
+ packageName, userId, conversationInfo, eventHistory);
+ }
+ }
+ shareTargets.add(new ShareTarget(shareShortcut, conversationData));
+ }
+
+ return shareTargets;
+ }
+
+ @VisibleForTesting
+ static class ShareTarget {
+
+ @NonNull
+ private final ShareShortcutInfo mShareShortcutInfo;
+ @Nullable
+ private final ConversationData mConversationData;
+
+ private ShareTarget(@NonNull ShareShortcutInfo shareShortcutInfo,
+ @Nullable ConversationData conversationData) {
+ mShareShortcutInfo = shareShortcutInfo;
+ mConversationData = conversationData;
+ }
+
+ @NonNull
+ @VisibleForTesting
+ ShareShortcutInfo getShareShortcutInfo() {
+ return mShareShortcutInfo;
+ }
+
+ @Nullable
+ @VisibleForTesting
+ ConversationData getConversationData() {
+ return mConversationData;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
index d3166b91dc9e..4ae374abb7c2 100644
--- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
@@ -28,6 +28,7 @@ import android.app.prediction.IPredictionCallback;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
+import android.os.Bundle;
import android.os.RemoteException;
import com.android.server.LocalServices;
@@ -76,6 +77,7 @@ public final class PeopleServiceTest {
mPredictionContext = new AppPredictionContext.Builder(mContext)
.setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
.setPredictedTargetCount(APP_PREDICTION_TARGET_COUNT)
+ .setExtras(new Bundle())
.build();
}
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 9f3d656188e1..4008cdc596dc 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
@@ -40,14 +40,12 @@ import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
-import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
@@ -224,31 +222,6 @@ public final class DataManagerTest {
}
@Test
- public void testGetShareTargets() {
- mDataManager.onUserUnlocked(USER_ID_PRIMARY);
-
- ShortcutInfo shortcut1 =
- buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson());
- ShareShortcutInfo shareShortcut1 =
- new ShareShortcutInfo(shortcut1, new ComponentName("pkg_1", "activity"));
-
- ShortcutInfo shortcut2 =
- buildShortcutInfo("pkg_2", USER_ID_PRIMARY, "sc_2", buildPerson());
- ShareShortcutInfo shareShortcut2 =
- new ShareShortcutInfo(shortcut2, new ComponentName("pkg_2", "activity"));
- mDataManager.onShortcutAddedOrUpdated(shortcut2);
-
- when(mShortcutManager.getShareTargets(any(IntentFilter.class)))
- .thenReturn(Arrays.asList(shareShortcut1, shareShortcut2));
-
- List<ShareShortcutInfo> shareShortcuts =
- mDataManager.getConversationShareTargets(new IntentFilter());
- // Only "sc_2" is stored as a conversation.
- assertEquals(1, shareShortcuts.size());
- assertEquals("sc_2", shareShortcuts.get(0).getShortcutInfo().getId());
- }
-
- @Test
public void testReportAppTargetEvent() throws IntentFilter.MalformedMimeTypeException {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
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
new file mode 100644
index 000000000000..808906e3a06a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2020 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.server.people.prediction;
+
+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.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.prediction.AppPredictionContext;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager.ShareShortcutInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+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.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.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public final class ShareTargetPredictorTest {
+
+ private static final String UI_SURFACE_SHARE = "share";
+ 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 CLASS_2 = "cls2";
+
+ @Mock private Context mContext;
+ @Mock private DataManager mDataManager;
+ @Mock private PackageData mPackageData1;
+ @Mock private PackageData mPackageData2;
+
+ private List<ShareShortcutInfo> mShareShortcuts = new ArrayList<>();
+
+ private ShareTargetPredictor mPredictor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mDataManager.getShareShortcuts(any())).thenReturn(mShareShortcuts);
+ when(mDataManager.getPackage(PACKAGE_1, USER_ID)).thenReturn(mPackageData1);
+ when(mDataManager.getPackage(PACKAGE_2, USER_ID)).thenReturn(mPackageData2);
+
+ AppPredictionContext predictionContext = new AppPredictionContext.Builder(mContext)
+ .setUiSurface(UI_SURFACE_SHARE)
+ .setPredictedTargetCount(NUM_PREDICTED_TARGETS)
+ .setExtras(new Bundle())
+ .build();
+ mPredictor = new ShareTargetPredictor(predictionContext, targets -> { }, mDataManager);
+ }
+
+ @Test
+ public void testGetShareTargets() {
+ 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"));
+
+ when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
+ 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));
+
+ List<ShareTarget> shareTargets = mPredictor.getShareTargets();
+
+ assertEquals(4, shareTargets.size());
+
+ assertEquals("sc1", shareTargets.get(0).getShareShortcutInfo().getShortcutInfo().getId());
+ assertNotNull(shareTargets.get(0).getConversationData());
+
+ assertEquals("sc2", shareTargets.get(1).getShareShortcutInfo().getShortcutInfo().getId());
+ assertNotNull(shareTargets.get(1).getConversationData());
+
+ assertEquals("sc3", shareTargets.get(2).getShareShortcutInfo().getShortcutInfo().getId());
+ assertNotNull(shareTargets.get(2).getConversationData());
+
+ assertEquals("sc4", shareTargets.get(3).getShareShortcutInfo().getShortcutInfo().getId());
+ assertNull(shareTargets.get(3).getConversationData());
+ }
+
+ private ShareShortcutInfo buildShareShortcut(
+ String packageName, String className, String shortcutId) {
+ ShortcutInfo shortcutInfo = buildShortcut(packageName, shortcutId);
+ ComponentName componentName = new ComponentName(packageName, className);
+ return new ShareShortcutInfo(shortcutInfo, componentName);
+ }
+
+ private ShortcutInfo buildShortcut(String packageName, String shortcutId) {
+ Context mockContext = mock(Context.class);
+ when(mockContext.getPackageName()).thenReturn(packageName);
+ when(mockContext.getUserId()).thenReturn(USER_ID);
+ when(mockContext.getUser()).thenReturn(UserHandle.of(USER_ID));
+ ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, shortcutId)
+ .setShortLabel(shortcutId)
+ .setIntent(new Intent("TestIntent"));
+ return builder.build();
+ }
+}