summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSong Hu <songhu@google.com>2020-09-23 18:12:46 -0700
committerSong Hu <songhu@google.com>2020-09-24 22:18:49 -0700
commiteedf47cbf5836730e7076d920722930a544af846 (patch)
tree97a1b30f5184d7dadd551f696ba44b82e1115da0
parentd6af5b5d1e5986c2d63dfc89d996c899416cf9f8 (diff)
DO NOT MERGE Put parameterized weights on top two sharing shortcuts of each app as per shortcuts native ranking in PeopleService Sharesheet model.
By default weights are 0 which ensures ranking same as what it is now. Bug: 168212835 Test: atest com.android.server.people.prediction.SharesheetModelScorerTest Change-Id: I02eaa5b6a448c33b51e5f4c6acaba93e7a2bd995
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java14
-rw-r--r--services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java4
-rw-r--r--services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java75
-rw-r--r--services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java147
4 files changed, 238 insertions, 2 deletions
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index da26930ca727..b4cd145ca374 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -390,6 +390,20 @@ public final class SystemUiDeviceConfigFlags {
public static final String CHOOSER_TARGET_RANKING_ENABLED = "chooser_target_ranking_enabled";
/**
+ * (float) Weight bonus applied on top sharing shortcuts as per native ranking provided by apps.
+ * Its range need to be 0 ~ 1.
+ */
+ public static final String TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER =
+ "top_native_ranked_sharing_shortcut_booster";
+
+ /**
+ * (float) Weight bonus applied on 2nd top sharing shortcuts as per native ranking provided by
+ * apps. Its range need to be 0 ~ 1.
+ */
+ public static final String NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER =
+ "non_top_native_ranked_sharing_shortcut_booster";
+
+ /**
* (boolean) Whether to enable user-drag resizing for PIP.
*/
public static final String PIP_USER_RESIZE = "pip_user_resize";
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 236ac8407faa..9e6cf845d144 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -86,8 +86,8 @@ class ShareTargetPredictor extends AppTargetPredictor {
return;
}
List<ShareTarget> shareTargets = getDirectShareTargets();
- SharesheetModelScorer.computeScore(shareTargets, getShareEventType(mIntentFilter),
- System.currentTimeMillis());
+ SharesheetModelScorer.computeScoreForDirectShare(shareTargets,
+ getShareEventType(mIntentFilter), System.currentTimeMillis());
Collections.sort(shareTargets,
Comparator.comparing(ShareTarget::getScore, reverseOrder())
.thenComparing(t -> t.getAppTarget().getRank()));
diff --git a/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java
index c77843cfb044..d4a502da56c7 100644
--- a/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java
+++ b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.usage.UsageEvents;
+import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.Pair;
import android.util.Range;
@@ -27,12 +28,14 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ChooserActivity;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.server.people.data.AppUsageStatsData;
import com.android.server.people.data.DataManager;
import com.android.server.people.data.Event;
import java.time.Duration;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -46,6 +49,7 @@ class SharesheetModelScorer {
private static final String TAG = "SharesheetModelScorer";
private static final boolean DEBUG = false;
private static final Integer RECENCY_SCORE_COUNT = 6;
+ private static final Integer NATIVE_RANK_COUNT = 2;
private static final float RECENCY_INITIAL_BASE_SCORE = 0.4F;
private static final float RECENCY_SCORE_INITIAL_DECAY = 0.05F;
private static final float RECENCY_SCORE_SUBSEQUENT_DECAY = 0.02F;
@@ -174,6 +178,77 @@ class SharesheetModelScorer {
postProcess(shareTargets, targetsLimit, dataManager, callingUserId);
}
+ /**
+ * Computes ranking score for direct sharing. Update
+ * {@link ShareTargetPredictor.ShareTargetScore}.
+ */
+ static void computeScoreForDirectShare(List<ShareTargetPredictor.ShareTarget> shareTargets,
+ int shareEventType, long now) {
+ computeScore(shareTargets, shareEventType, now);
+ promoteTopNativeRankedShortcuts(shareTargets);
+ }
+
+ /**
+ * Promotes top (NATIVE_RANK_COUNT) shortcuts for each package and class, as per shortcut native
+ * ranking provided by apps.
+ */
+ private static void promoteTopNativeRankedShortcuts(
+ List<ShareTargetPredictor.ShareTarget> shareTargets) {
+ float topShortcutBonus = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+ 0f);
+ float secondTopShortcutBonus = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+ 0f);
+ // Populates a map which key is a packageName and className pair, value is a max heap
+ // containing top (NATIVE_RANK_COUNT) shortcuts as per shortcut native ranking provided
+ // by apps.
+ Map<Pair<String, String>, PriorityQueue<ShareTargetPredictor.ShareTarget>>
+ topNativeRankedShareTargetMap = new ArrayMap<>();
+ for (ShareTargetPredictor.ShareTarget shareTarget : shareTargets) {
+ Pair<String, String> key = new Pair<>(shareTarget.getAppTarget().getPackageName(),
+ shareTarget.getAppTarget().getClassName());
+ if (!topNativeRankedShareTargetMap.containsKey(key)) {
+ topNativeRankedShareTargetMap.put(key,
+ new PriorityQueue<>(NATIVE_RANK_COUNT,
+ Collections.reverseOrder(Comparator.comparingInt(
+ p -> p.getAppTarget().getRank()))));
+ }
+ PriorityQueue<ShareTargetPredictor.ShareTarget> rankMaxHeap =
+ topNativeRankedShareTargetMap.get(key);
+ if (rankMaxHeap.isEmpty() || shareTarget.getAppTarget().getRank()
+ < rankMaxHeap.peek().getAppTarget().getRank()) {
+ if (rankMaxHeap.size() == NATIVE_RANK_COUNT) {
+ rankMaxHeap.poll();
+ }
+ rankMaxHeap.offer(shareTarget);
+ }
+ }
+ for (PriorityQueue<ShareTargetPredictor.ShareTarget> maxHeap :
+ topNativeRankedShareTargetMap.values()) {
+ while (!maxHeap.isEmpty()) {
+ ShareTargetPredictor.ShareTarget target = maxHeap.poll();
+ float bonus = maxHeap.isEmpty() ? topShortcutBonus : secondTopShortcutBonus;
+ target.setScore(probOR(target.getScore(), bonus));
+
+ if (DEBUG) {
+ Slog.d(TAG, String.format(
+ "SharesheetModel: promote top shortcut as per native ranking,"
+ + "packageName: %s, className: %s, shortcutId: %s, bonus:%.2f,"
+ + "total:%.2f",
+ target.getAppTarget().getPackageName(),
+ target.getAppTarget().getClassName(),
+ target.getAppTarget().getShortcutInfo() != null
+ ? target.getAppTarget().getShortcutInfo().getId() : null,
+ bonus,
+ target.getScore()));
+ }
+ }
+ }
+ }
+
private static void postProcess(List<ShareTargetPredictor.ShareTarget> shareTargets,
int targetsLimit, @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
// Populates a map which key is package name and value is list of shareTargets descended
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java
index 45fff48ade55..605878d85bce 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anySet;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -28,9 +29,13 @@ import static org.mockito.Mockito.when;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetId;
import android.app.usage.UsageEvents;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.util.Range;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.server.people.data.AppUsageStatsData;
import com.android.server.people.data.DataManager;
import com.android.server.people.data.Event;
@@ -121,6 +126,13 @@ public final class SharesheetModelScorerTest {
private ShareTargetPredictor.ShareTarget mShareTarget5;
private ShareTargetPredictor.ShareTarget mShareTarget6;
+ private ShareTargetPredictor.ShareTarget mShareShortcutTarget1;
+ private ShareTargetPredictor.ShareTarget mShareShortcutTarget2;
+ private ShareTargetPredictor.ShareTarget mShareShortcutTarget3;
+ private ShareTargetPredictor.ShareTarget mShareShortcutTarget4;
+ private ShareTargetPredictor.ShareTarget mShareShortcutTarget5;
+ private ShareTargetPredictor.ShareTarget mShareShortcutTarget6;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -154,6 +166,46 @@ public final class SharesheetModelScorerTest {
new AppTargetId("cls2#pkg3"), PACKAGE_3, UserHandle.of(USER_ID))
.setClassName(CLASS_2).build(),
null, null);
+
+ mShareShortcutTarget1 = new ShareTargetPredictor.ShareTarget(
+ new AppTarget.Builder(
+ new AppTargetId("cls1#pkg1#1"), buildShortcutInfo(PACKAGE_1, 0, "1"))
+ .setClassName(CLASS_1).setRank(2).build(),
+ mEventHistory1, null);
+ mShareShortcutTarget2 = new ShareTargetPredictor.ShareTarget(
+ new AppTarget.Builder(
+ new AppTargetId("cls1#pkg1#2"), buildShortcutInfo(PACKAGE_1, 0, "2"))
+ .setClassName(CLASS_1).setRank(1).build(),
+ mEventHistory2, null);
+ mShareShortcutTarget3 = new ShareTargetPredictor.ShareTarget(
+ new AppTarget.Builder(
+ new AppTargetId("cls1#pkg1#3"), buildShortcutInfo(PACKAGE_1, 0, "3"))
+ .setClassName(CLASS_1).setRank(0).build(),
+ mEventHistory3, null);
+ mShareShortcutTarget4 = new ShareTargetPredictor.ShareTarget(
+ new AppTarget.Builder(
+ new AppTargetId("cls1#pkg2#1"), buildShortcutInfo(PACKAGE_2, 0, "1"))
+ .setClassName(CLASS_1).setRank(2).build(),
+ mEventHistory4, null);
+ mShareShortcutTarget5 = new ShareTargetPredictor.ShareTarget(
+ new AppTarget.Builder(
+ new AppTargetId("cls1#pkg2#2"), buildShortcutInfo(PACKAGE_2, 0, "2"))
+ .setClassName(CLASS_1).setRank(1).build(),
+ mEventHistory5, null);
+ mShareShortcutTarget6 = new ShareTargetPredictor.ShareTarget(
+ new AppTarget.Builder(
+ new AppTargetId("cls1#pkg2#3"), buildShortcutInfo(PACKAGE_2, 0, "3"))
+ .setClassName(CLASS_1).setRank(3).build(),
+ null, null);
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+ Float.toString(0f),
+ true /* makeDefault*/);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+ Float.toString(0f),
+ true /* makeDefault*/);
}
@Test
@@ -433,6 +485,101 @@ public final class SharesheetModelScorerTest {
assertEquals(0f, mShareTarget6.getScore(), DELTA);
}
+ @Test
+ public void testComputeScoreForDirectShare() {
+ // Frequency and recency
+ 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(mEventIndex1.getActiveTimeSlots()).thenReturn(
+ List.of(WITHIN_ONE_DAY, TWO_DAYS_AGO, FIVE_DAYS_AGO));
+ when(mEventIndex2.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO));
+ when(mEventIndex3.getActiveTimeSlots()).thenReturn(List.of(FIVE_DAYS_AGO, TWENTY_DAYS_AGO));
+ when(mEventIndex4.getActiveTimeSlots()).thenReturn(
+ List.of(EIGHT_DAYS_AGO, TWELVE_DAYS_AGO, FOUR_WEEKS_AGO));
+ when(mEventIndex5.getActiveTimeSlots()).thenReturn(List.of());
+
+ when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY);
+ when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO);
+ when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO);
+ when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO);
+ when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null);
+
+ // Frequency of the same mime type
+ when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6);
+ when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7);
+ when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8);
+ when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9);
+ when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10);
+
+ when(mEventIndex6.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO));
+ when(mEventIndex7.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO));
+ when(mEventIndex8.getActiveTimeSlots()).thenReturn(List.of());
+ when(mEventIndex9.getActiveTimeSlots()).thenReturn(List.of(EIGHT_DAYS_AGO));
+ when(mEventIndex10.getActiveTimeSlots()).thenReturn(List.of());
+
+ SharesheetModelScorer.computeScore(
+ List.of(mShareShortcutTarget1, mShareShortcutTarget2, mShareShortcutTarget3,
+ mShareShortcutTarget4, mShareShortcutTarget5, mShareShortcutTarget6),
+ Event.TYPE_SHARE_TEXT,
+ NOW);
+
+ // Verification
+ assertEquals(0.514f, mShareShortcutTarget1.getScore(), DELTA);
+ assertEquals(0.475125f, mShareShortcutTarget2.getScore(), DELTA);
+ assertEquals(0.33f, mShareShortcutTarget3.getScore(), DELTA);
+ assertEquals(0.4411f, mShareShortcutTarget4.getScore(), DELTA);
+ assertEquals(0f, mShareShortcutTarget5.getScore(), DELTA);
+ assertEquals(0f, mShareShortcutTarget6.getScore(), DELTA);
+ }
+
+ @Test
+ public void testComputeScoreForDirectShare_promoteTopNativeRankedShortcuts() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+ Float.toString(0.4f),
+ true /* makeDefault*/);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+ Float.toString(0.3f),
+ true /* makeDefault*/);
+
+ 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(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6);
+ when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7);
+ when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8);
+ when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9);
+ when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10);
+
+ SharesheetModelScorer.computeScoreForDirectShare(
+ List.of(mShareShortcutTarget1, mShareShortcutTarget2, mShareShortcutTarget3,
+ mShareShortcutTarget4, mShareShortcutTarget5, mShareShortcutTarget6),
+ Event.TYPE_SHARE_TEXT, 20);
+
+ assertEquals(0f, mShareShortcutTarget1.getScore(), DELTA);
+ assertEquals(0.3f, mShareShortcutTarget2.getScore(), DELTA);
+ assertEquals(0.4f, mShareShortcutTarget3.getScore(), DELTA);
+ assertEquals(0.3f, mShareShortcutTarget4.getScore(), DELTA);
+ assertEquals(0.4f, mShareShortcutTarget5.getScore(), DELTA);
+ assertEquals(0f, mShareShortcutTarget6.getScore(), DELTA);
+ }
+
+ private static ShortcutInfo buildShortcutInfo(String packageName, int userId, String id) {
+ Context mockContext = mock(Context.class);
+ when(mockContext.getPackageName()).thenReturn(packageName);
+ when(mockContext.getUserId()).thenReturn(userId);
+ when(mockContext.getUser()).thenReturn(UserHandle.of(userId));
+ ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, id).setShortLabel(id);
+ return builder.build();
+ }
+
private static UsageEvents.Event createUsageEvent(String packageName) {
UsageEvents.Event e = new UsageEvents.Event();
e.mPackage = packageName;