summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
author Himanshu Gupta <himanshuz@google.com> 2023-02-25 14:39:01 +0000
committer Himanshu Gupta <himanshuz@google.com> 2023-03-22 13:19:04 +0000
commit10f22425705485803cc723bcdc61623926a402c9 (patch)
tree3305b325f8822a7f73041cc60e43a203b11eb536 /java/src
parent961d141bc72743ee8c42ce21bb6b0e5e01b30c4c (diff)
Migrating AppCloning code to unbundled sharesheet.
This CL encapsulates the work done in previous CLs for the frameworks sharesheet: 1. ag/20982903 2. ag/20980382 3. ag/20480246 4. ag/21072117 5. ag/21223364 6. ag/21117396 7. ag/21465730 With this CL, Cloned Apps can be shown in unbundled sharesheet, at par with frameworks. Bug: 273294251 Test: atest com.android.intentresolver Change-Id: Ic01a93f7279c8beb998b3e98f53c459c5ed2b1bf
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java9
-rw-r--r--java/src/com/android/intentresolver/AnnotatedUserHandles.java42
-rw-r--r--java/src/com/android/intentresolver/ChooserActivity.java85
-rw-r--r--java/src/com/android/intentresolver/ChooserListAdapter.java9
-rw-r--r--java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java6
-rw-r--r--java/src/com/android/intentresolver/GenericMultiProfilePagerAdapter.java24
-rw-r--r--java/src/com/android/intentresolver/NoAppsAvailableEmptyStateProvider.java12
-rw-r--r--java/src/com/android/intentresolver/NoCrossProfileEmptyStateProvider.java11
-rw-r--r--java/src/com/android/intentresolver/ResolverActivity.java184
-rw-r--r--java/src/com/android/intentresolver/ResolverListAdapter.java33
-rw-r--r--java/src/com/android/intentresolver/ResolverListController.java45
-rw-r--r--java/src/com/android/intentresolver/ResolverMultiProfilePagerAdapter.java10
-rw-r--r--java/src/com/android/intentresolver/model/AbstractResolverComparator.java72
-rw-r--r--java/src/com/android/intentresolver/model/AppPredictionServiceResolverComparator.java43
-rw-r--r--java/src/com/android/intentresolver/model/ResolverComparatorModel.java7
-rw-r--r--java/src/com/android/intentresolver/model/ResolverRankerServiceResolverComparator.java281
16 files changed, 608 insertions, 265 deletions
diff --git a/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java
index e3f1b233..4b06db3b 100644
--- a/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java
+++ b/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java
@@ -62,6 +62,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
private Set<Integer> mLoadedPages;
private final EmptyStateProvider mEmptyStateProvider;
private final UserHandle mWorkProfileUserHandle;
+ private final UserHandle mCloneProfileUserHandle;
private final Supplier<Boolean> mWorkProfileQuietModeChecker; // True when work is quiet.
AbstractMultiProfilePagerAdapter(
@@ -69,11 +70,13 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
int currentPage,
EmptyStateProvider emptyStateProvider,
Supplier<Boolean> workProfileQuietModeChecker,
- UserHandle workProfileUserHandle) {
+ UserHandle workProfileUserHandle,
+ UserHandle cloneProfileUserHandle) {
mContext = Objects.requireNonNull(context);
mCurrentPage = currentPage;
mLoadedPages = new HashSet<>();
mWorkProfileUserHandle = workProfileUserHandle;
+ mCloneProfileUserHandle = cloneProfileUserHandle;
mEmptyStateProvider = emptyStateProvider;
mWorkProfileQuietModeChecker = workProfileQuietModeChecker;
}
@@ -160,6 +163,10 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
return null;
}
+ public UserHandle getCloneUserHandle() {
+ return mCloneProfileUserHandle;
+ }
+
/**
* Returns the {@link ProfileDescriptor} relevant to the given <code>pageIndex</code>.
* <ul>
diff --git a/java/src/com/android/intentresolver/AnnotatedUserHandles.java b/java/src/com/android/intentresolver/AnnotatedUserHandles.java
index b4365b84..769195ed 100644
--- a/java/src/com/android/intentresolver/AnnotatedUserHandles.java
+++ b/java/src/com/android/intentresolver/AnnotatedUserHandles.java
@@ -87,6 +87,11 @@ public final class AnnotatedUserHandles {
// TODO: integrate logic for `ResolverActivity.EXTRA_CALLING_USER`.
userHandleSharesheetLaunchedAs = UserHandle.of(UserHandle.myUserId());
+ // ActivityManager.getCurrentUser() refers to the current Foreground user. When clone/work
+ // profile is active, we always make the personal tab from the foreground user.
+ // Outside profiles, current foreground user is potentially the same as the sharesheet
+ // process's user (UserHandle.myUserId()), so we continue to create personal tab with the
+ // current foreground user.
personalProfileUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
UserManager userManager = forShareActivity.getSystemService(UserManager.class);
@@ -100,14 +105,43 @@ public final class AnnotatedUserHandles {
@Nullable
private static UserHandle getWorkProfileForUser(
UserManager userManager, UserHandle profileOwnerUserHandle) {
- return userManager.getProfiles(profileOwnerUserHandle.getIdentifier()).stream()
- .filter(info -> info.isManagedProfile()).findFirst()
- .map(info -> info.getUserHandle()).orElse(null);
+ return userManager.getProfiles(profileOwnerUserHandle.getIdentifier())
+ .stream()
+ .filter(info -> info.isManagedProfile())
+ .findFirst()
+ .map(info -> info.getUserHandle())
+ .orElse(null);
}
@Nullable
private static UserHandle getCloneProfileForUser(
UserManager userManager, UserHandle profileOwnerUserHandle) {
- return null; // Not yet supported in framework.
+ return userManager.getProfiles(profileOwnerUserHandle.getIdentifier())
+ .stream()
+ .filter(info -> info.isCloneProfile())
+ .findFirst()
+ .map(info -> info.getUserHandle())
+ .orElse(null);
+ }
+
+ /**
+ * Returns the {@link UserHandle} to use when querying resolutions for intents in a
+ * {@link ResolverListController} configured for the provided {@code userHandle}.
+ */
+ public UserHandle getQueryIntentsUser(UserHandle userHandle) {
+ // In case launching app is in clonedProfile, and we are building the personal tab, intent
+ // resolution will be attempted as clonedUser instead of user 0. This is because intent
+ // resolution from user 0 and clonedUser is not guaranteed to return same results.
+ // We do not care about the case when personal adapter is started with non-root user
+ // (secondary user case), as clone profile is guaranteed to be non-active in that case.
+ UserHandle queryIntentsUser = userHandle;
+ if (isLaunchedAsCloneProfile() && userHandle.equals(personalProfileUserHandle)) {
+ queryIntentsUser = cloneProfileUserHandle;
+ }
+ return queryIntentsUser;
+ }
+
+ private Boolean isLaunchedAsCloneProfile() {
+ return userHandleSharesheetLaunchedAs.equals(cloneProfileUserHandle);
}
}
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java
index ae5be26d..bae1feb2 100644
--- a/java/src/com/android/intentresolver/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/ChooserActivity.java
@@ -495,7 +495,7 @@ public class ChooserActivity extends ResolverActivity implements
return new NoCrossProfileEmptyStateProvider(getPersonalProfileUserHandle(),
noWorkToPersonalEmptyState, noPersonalToWorkEmptyState,
- createCrossProfileIntentsChecker(), createMyUserIdProvider());
+ createCrossProfileIntentsChecker(), getTabOwnerUserHandleForLaunch());
}
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForOneProfile(
@@ -508,13 +508,14 @@ public class ChooserActivity extends ResolverActivity implements
initialIntents,
rList,
filterLastUsed,
- /* userHandle */ UserHandle.of(UserHandle.myUserId()));
+ /* userHandle */ getPersonalProfileUserHandle());
return new ChooserMultiProfilePagerAdapter(
/* context */ this,
adapter,
createEmptyStateProvider(/* workProfileUserHandle= */ null),
/* workProfileQuietModeChecker= */ () -> false,
/* workProfileUserHandle= */ null,
+ getCloneProfileUserHandle(),
mMaxTargetsPerRow);
}
@@ -545,13 +546,14 @@ public class ChooserActivity extends ResolverActivity implements
() -> mWorkProfileAvailability.isQuietModeEnabled(),
selectedProfile,
getWorkProfileUserHandle(),
+ getCloneProfileUserHandle(),
mMaxTargetsPerRow);
}
private int findSelectedProfile() {
int selectedProfile = getSelectedProfileExtra();
if (selectedProfile == -1) {
- selectedProfile = getProfileForUser(getUser());
+ selectedProfile = getProfileForUser(getTabOwnerUserHandleForLaunch());
}
return selectedProfile;
}
@@ -860,7 +862,11 @@ public class ChooserActivity extends ResolverActivity implements
ChooserTargetActionsDialogFragment.show(
getSupportFragmentManager(),
targetList,
- mChooserMultiProfilePagerAdapter.getCurrentUserHandle(),
+ // Adding userHandle from ResolveInfo allows the app icon in Dialog Box to be
+ // resolved correctly within the same tab.
+ getResolveInfoUserHandle(
+ targetInfo.getResolveInfo(),
+ mChooserMultiProfilePagerAdapter.getCurrentUserHandle()),
shortcutIdKey,
shortcutTitle,
isShortcutPinned,
@@ -892,11 +898,14 @@ public class ChooserActivity extends ResolverActivity implements
if (targetInfo.isMultiDisplayResolveInfo()) {
MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
if (!mti.hasSelected()) {
+ // Add userHandle based badge to the stackedAppDialogBox.
ChooserStackedAppDialogFragment.show(
getSupportFragmentManager(),
mti,
which,
- mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
+ getResolveInfoUserHandle(
+ targetInfo.getResolveInfo(),
+ mChooserMultiProfilePagerAdapter.getCurrentUserHandle()));
return;
}
}
@@ -1008,9 +1017,11 @@ public class ChooserActivity extends ResolverActivity implements
mChooserMultiProfilePagerAdapter.getActiveListAdapter();
if (currentListAdapter != null) {
sendImpressionToAppPredictor(info, currentListAdapter);
- currentListAdapter.updateModel(info.getResolvedComponentName());
- currentListAdapter.updateChooserCounts(ri.activityInfo.packageName,
- targetIntent.getAction());
+ currentListAdapter.updateModel(info);
+ currentListAdapter.updateChooserCounts(
+ ri.activityInfo.packageName,
+ targetIntent.getAction(),
+ ri.userHandle);
}
if (DEBUG) {
Log.d(TAG, "ResolveInfo Package is " + ri.activityInfo.packageName);
@@ -1096,22 +1107,33 @@ public class ChooserActivity extends ResolverActivity implements
@Nullable
private AppPredictor getAppPredictor(UserHandle userHandle) {
ProfileRecord record = getProfileRecord(userHandle);
- return (record == null) ? null : record.appPredictor;
+ // We cannot use APS service when clone profile is present as APS service cannot sort
+ // cross profile targets as of now.
+ return (record == null || getCloneProfileUserHandle() != null) ? null : record.appPredictor;
}
/**
* Sort intents alphabetically based on display label.
*/
static class AzInfoComparator implements Comparator<DisplayResolveInfo> {
- Collator mCollator;
+ Comparator<DisplayResolveInfo> mComparator;
AzInfoComparator(Context context) {
- mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
+ Collator collator = Collator
+ .getInstance(context.getResources().getConfiguration().locale);
+ // Adding two stage comparator, first stage compares using displayLabel, next stage
+ // compares using resolveInfo.userHandle
+ mComparator = Comparator.comparing(DisplayResolveInfo::getDisplayLabel, collator)
+ .thenComparingInt(displayResolveInfo ->
+ getResolveInfoUserHandle(
+ displayResolveInfo.getResolveInfo(),
+ // TODO: User resolveInfo.userHandle, once its available.
+ UserHandle.SYSTEM).getIdentifier());
}
@Override
public int compare(
DisplayResolveInfo lhsp, DisplayResolveInfo rhsp) {
- return mCollator.compare(lhsp.getDisplayLabel(), rhsp.getDisplayLabel());
+ return mComparator.compare(lhsp, rhsp);
}
}
@@ -1129,14 +1151,16 @@ public class ChooserActivity extends ResolverActivity implements
Intent targetIntent,
String referrerPackageName,
int launchedFromUid,
- AbstractResolverComparator resolverComparator) {
+ AbstractResolverComparator resolverComparator,
+ UserHandle queryIntentsAsUser) {
super(
context,
pm,
targetIntent,
referrerPackageName,
launchedFromUid,
- resolverComparator);
+ resolverComparator,
+ queryIntentsAsUser);
}
@Override
@@ -1255,20 +1279,24 @@ public class ChooserActivity extends ResolverActivity implements
Intent targetIntent,
ChooserRequestParameters chooserRequest,
int maxTargetsPerRow) {
+ UserHandle initialIntentsUserSpace = isLaunchedAsCloneProfile()
+ && userHandle.equals(getPersonalProfileUserHandle())
+ ? getCloneProfileUserHandle() : userHandle;
return new ChooserListAdapter(
context,
payloadIntents,
initialIntents,
rList,
filterLastUsed,
- resolverListController,
+ createListController(userHandle),
userHandle,
targetIntent,
this,
context.getPackageManager(),
getChooserActivityLogger(),
chooserRequest,
- maxTargetsPerRow);
+ maxTargetsPerRow,
+ initialIntentsUserSpace);
}
@Override
@@ -1281,8 +1309,13 @@ public class ChooserActivity extends ResolverActivity implements
getReferrerPackageName(), appPredictor, userHandle, getChooserActivityLogger());
} else {
resolverComparator =
- new ResolverRankerServiceResolverComparator(this, getTargetIntent(),
- getReferrerPackageName(), null, getChooserActivityLogger());
+ new ResolverRankerServiceResolverComparator(
+ this,
+ getTargetIntent(),
+ getReferrerPackageName(),
+ null,
+ getChooserActivityLogger(),
+ getResolverRankerServiceUserHandleList(userHandle));
}
return new ChooserListController(
@@ -1291,7 +1324,8 @@ public class ChooserActivity extends ResolverActivity implements
getTargetIntent(),
getReferrerPackageName(),
getAnnotatedUserHandles().userIdOfCallingApp,
- resolverComparator);
+ resolverComparator,
+ getQueryIntentsUser(userHandle));
}
@VisibleForTesting
@@ -1508,17 +1542,16 @@ public class ChooserActivity extends ResolverActivity implements
}
/**
- * Returns {@link #PROFILE_PERSONAL}, {@link #PROFILE_WORK}, or -1 if the given user handle
- * does not match either the personal or work user handle.
+ * Returns {@link #PROFILE_WORK}, if the given user handle matches work user handle.
+ * Returns {@link #PROFILE_PERSONAL}, otherwise.
**/
private int getProfileForUser(UserHandle currentUserHandle) {
- if (currentUserHandle.equals(getPersonalProfileUserHandle())) {
- return PROFILE_PERSONAL;
- } else if (currentUserHandle.equals(getWorkProfileUserHandle())) {
+ if (currentUserHandle.equals(getWorkProfileUserHandle())) {
return PROFILE_WORK;
}
- Log.e(TAG, "User " + currentUserHandle + " does not belong to a personal or work profile.");
- return -1;
+ // We return personal profile, as it is the default when there is no work profile, personal
+ // profile represents rootUser, clonedUser & secondaryUser, covering all use cases.
+ return PROFILE_PERSONAL;
}
private ViewGroup getActiveEmptyStateView() {
diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java
index f0651360..dab44577 100644
--- a/java/src/com/android/intentresolver/ChooserListAdapter.java
+++ b/java/src/com/android/intentresolver/ChooserListAdapter.java
@@ -142,7 +142,8 @@ public class ChooserListAdapter extends ResolverListAdapter {
PackageManager packageManager,
ChooserActivityLogger chooserActivityLogger,
ChooserRequestParameters chooserRequest,
- int maxRankedTargets) {
+ int maxRankedTargets,
+ UserHandle initialIntentsUserSpace) {
// Don't send the initial intents through the shared ResolverActivity path,
// we want to separate them into a different section.
super(
@@ -155,7 +156,8 @@ public class ChooserListAdapter extends ResolverListAdapter {
userHandle,
targetIntent,
resolverListCommunicator,
- false);
+ false,
+ initialIntentsUserSpace);
mChooserRequest = chooserRequest;
mMaxRankedTargets = maxRankedTargets;
@@ -222,6 +224,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
ri.noResourceId = true;
ri.icon = 0;
}
+ ri.userHandle = initialIntentsUserSpace;
DisplayResolveInfo displayResolveInfo = DisplayResolveInfo.newDisplayResolveInfo(
ii, ri, ii, mPresentationFactory.makePresentationGetter(ri));
mCallerTargets.add(displayResolveInfo);
@@ -351,6 +354,8 @@ public class ChooserListAdapter extends ResolverListAdapter {
.collect(Collectors.groupingBy(target ->
target.getResolvedComponentName().getPackageName()
+ "#" + target.getDisplayLabel()
+ + '#' + ResolverActivity.getResolveInfoUserHandle(
+ target.getResolveInfo(), getUserHandle()).getIdentifier()
))
.values()
.stream()
diff --git a/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java
index 3e2ea473..9c096fd2 100644
--- a/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java
+++ b/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java
@@ -50,6 +50,7 @@ public class ChooserMultiProfilePagerAdapter extends GenericMultiProfilePagerAda
EmptyStateProvider emptyStateProvider,
Supplier<Boolean> workProfileQuietModeChecker,
UserHandle workProfileUserHandle,
+ UserHandle cloneProfileUserHandle,
int maxTargetsPerRow) {
this(
context,
@@ -59,6 +60,7 @@ public class ChooserMultiProfilePagerAdapter extends GenericMultiProfilePagerAda
workProfileQuietModeChecker,
/* defaultProfile= */ 0,
workProfileUserHandle,
+ cloneProfileUserHandle,
new BottomPaddingOverrideSupplier(context));
}
@@ -70,6 +72,7 @@ public class ChooserMultiProfilePagerAdapter extends GenericMultiProfilePagerAda
Supplier<Boolean> workProfileQuietModeChecker,
@Profile int defaultProfile,
UserHandle workProfileUserHandle,
+ UserHandle cloneProfileUserHandle,
int maxTargetsPerRow) {
this(
context,
@@ -79,6 +82,7 @@ public class ChooserMultiProfilePagerAdapter extends GenericMultiProfilePagerAda
workProfileQuietModeChecker,
defaultProfile,
workProfileUserHandle,
+ cloneProfileUserHandle,
new BottomPaddingOverrideSupplier(context));
}
@@ -90,6 +94,7 @@ public class ChooserMultiProfilePagerAdapter extends GenericMultiProfilePagerAda
Supplier<Boolean> workProfileQuietModeChecker,
@Profile int defaultProfile,
UserHandle workProfileUserHandle,
+ UserHandle cloneProfileUserHandle,
BottomPaddingOverrideSupplier bottomPaddingOverrideSupplier) {
super(
context,
@@ -100,6 +105,7 @@ public class ChooserMultiProfilePagerAdapter extends GenericMultiProfilePagerAda
workProfileQuietModeChecker,
defaultProfile,
workProfileUserHandle,
+ cloneProfileUserHandle,
() -> makeProfileView(context),
bottomPaddingOverrideSupplier);
mAdapterBinder = adapterBinder;
diff --git a/java/src/com/android/intentresolver/GenericMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/GenericMultiProfilePagerAdapter.java
index 7613f35f..a1c53402 100644
--- a/java/src/com/android/intentresolver/GenericMultiProfilePagerAdapter.java
+++ b/java/src/com/android/intentresolver/GenericMultiProfilePagerAdapter.java
@@ -19,6 +19,7 @@ package com.android.intentresolver;
import android.annotation.Nullable;
import android.content.Context;
import android.os.UserHandle;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -84,6 +85,7 @@ class GenericMultiProfilePagerAdapter<
Supplier<Boolean> workProfileQuietModeChecker,
@Profile int defaultProfile,
UserHandle workProfileUserHandle,
+ UserHandle cloneProfileUserHandle,
Supplier<ViewGroup> pageViewInflater,
Supplier<Optional<Integer>> containerBottomPaddingOverrideSupplier) {
super(
@@ -91,7 +93,8 @@ class GenericMultiProfilePagerAdapter<
/* currentPage= */ defaultProfile,
emptyStateProvider,
workProfileQuietModeChecker,
- workProfileUserHandle);
+ workProfileUserHandle,
+ cloneProfileUserHandle);
mListAdapterExtractor = listAdapterExtractor;
mAdapterBinder = adapterBinder;
@@ -145,12 +148,12 @@ class GenericMultiProfilePagerAdapter<
@Override
@Nullable
protected ListAdapterT getListAdapterForUserHandle(UserHandle userHandle) {
- if (getActiveListAdapter().getUserHandle().equals(userHandle)) {
- return getActiveListAdapter();
- }
- if ((getInactiveListAdapter() != null) && getInactiveListAdapter().getUserHandle().equals(
- userHandle)) {
- return getInactiveListAdapter();
+ if (getPersonalListAdapter().getUserHandle().equals(userHandle)
+ || userHandle.equals(getCloneUserHandle())) {
+ return getPersonalListAdapter();
+ } else if (getWorkListAdapter() != null
+ && getWorkListAdapter().getUserHandle().equals(userHandle)) {
+ return getWorkListAdapter();
}
return null;
}
@@ -177,6 +180,9 @@ class GenericMultiProfilePagerAdapter<
@Override
public ListAdapterT getWorkListAdapter() {
+ if (!hasAdapterForIndex(PROFILE_WORK)) {
+ return null;
+ }
return mListAdapterExtractor.apply(getAdapterForIndex(PROFILE_WORK));
}
@@ -209,6 +215,10 @@ class GenericMultiProfilePagerAdapter<
paddingBottom));
}
+ private boolean hasAdapterForIndex(int pageIndex) {
+ return (pageIndex < getCount());
+ }
+
// TODO: `ChooserActivity` also has a per-profile record type. Maybe the "multi-profile pager"
// should be the owner of all per-profile data (especially now that the API is generic)?
private static class GenericProfileDescriptor<PageViewT, SinglePageAdapterT> extends
diff --git a/java/src/com/android/intentresolver/NoAppsAvailableEmptyStateProvider.java b/java/src/com/android/intentresolver/NoAppsAvailableEmptyStateProvider.java
index d424f295..a7b50f38 100644
--- a/java/src/com/android/intentresolver/NoAppsAvailableEmptyStateProvider.java
+++ b/java/src/com/android/intentresolver/NoAppsAvailableEmptyStateProvider.java
@@ -30,7 +30,7 @@ import android.stats.devicepolicy.nano.DevicePolicyEnums;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.EmptyState;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
-import com.android.intentresolver.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
+import com.android.internal.R;
import java.util.List;
@@ -49,16 +49,16 @@ public class NoAppsAvailableEmptyStateProvider implements EmptyStateProvider {
@NonNull
private final String mMetricsCategory;
@NonNull
- private final MyUserIdProvider mMyUserIdProvider;
+ private final UserHandle mTabOwnerUserHandleForLaunch;
public NoAppsAvailableEmptyStateProvider(Context context, UserHandle workProfileUserHandle,
UserHandle personalProfileUserHandle, String metricsCategory,
- MyUserIdProvider myUserIdProvider) {
+ UserHandle tabOwnerUserHandleForLaunch) {
mContext = context;
mWorkProfileUserHandle = workProfileUserHandle;
mPersonalProfileUserHandle = personalProfileUserHandle;
mMetricsCategory = metricsCategory;
- mMyUserIdProvider = myUserIdProvider;
+ mTabOwnerUserHandleForLaunch = tabOwnerUserHandleForLaunch;
}
@Nullable
@@ -68,7 +68,7 @@ public class NoAppsAvailableEmptyStateProvider implements EmptyStateProvider {
UserHandle listUserHandle = resolverListAdapter.getUserHandle();
if (mWorkProfileUserHandle != null
- && (mMyUserIdProvider.getMyUserId() == listUserHandle.getIdentifier()
+ && (mTabOwnerUserHandleForLaunch.equals(listUserHandle)
|| !hasAppsInOtherProfile(resolverListAdapter))) {
String title;
@@ -101,7 +101,7 @@ public class NoAppsAvailableEmptyStateProvider implements EmptyStateProvider {
return false;
}
List<ResolvedComponentInfo> resolversForIntent =
- adapter.getResolversForUser(UserHandle.of(mMyUserIdProvider.getMyUserId()));
+ adapter.getResolversForUser(mTabOwnerUserHandleForLaunch);
for (ResolvedComponentInfo info : resolversForIntent) {
ResolveInfo resolveInfo = info.getResolveInfoAt(0);
if (resolveInfo.targetUserId != UserHandle.USER_CURRENT) {
diff --git a/java/src/com/android/intentresolver/NoCrossProfileEmptyStateProvider.java b/java/src/com/android/intentresolver/NoCrossProfileEmptyStateProvider.java
index 420d26c5..6f72bb00 100644
--- a/java/src/com/android/intentresolver/NoCrossProfileEmptyStateProvider.java
+++ b/java/src/com/android/intentresolver/NoCrossProfileEmptyStateProvider.java
@@ -27,7 +27,6 @@ import android.os.UserHandle;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.EmptyState;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
-import com.android.intentresolver.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
/**
* Empty state provider that does not allow cross profile sharing, it will return a blocker
@@ -39,28 +38,28 @@ public class NoCrossProfileEmptyStateProvider implements EmptyStateProvider {
private final EmptyState mNoWorkToPersonalEmptyState;
private final EmptyState mNoPersonalToWorkEmptyState;
private final CrossProfileIntentsChecker mCrossProfileIntentsChecker;
- private final MyUserIdProvider mUserIdProvider;
+ private final UserHandle mTabOwnerUserHandleForLaunch;
public NoCrossProfileEmptyStateProvider(UserHandle personalUserHandle,
EmptyState noWorkToPersonalEmptyState,
EmptyState noPersonalToWorkEmptyState,
CrossProfileIntentsChecker crossProfileIntentsChecker,
- MyUserIdProvider myUserIdProvider) {
+ UserHandle tabOwnerUserHandleForLaunch) {
mPersonalProfileUserHandle = personalUserHandle;
mNoWorkToPersonalEmptyState = noWorkToPersonalEmptyState;
mNoPersonalToWorkEmptyState = noPersonalToWorkEmptyState;
mCrossProfileIntentsChecker = crossProfileIntentsChecker;
- mUserIdProvider = myUserIdProvider;
+ mTabOwnerUserHandleForLaunch = tabOwnerUserHandleForLaunch;
}
@Nullable
@Override
public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
boolean shouldShowBlocker =
- mUserIdProvider.getMyUserId() != resolverListAdapter.getUserHandle().getIdentifier()
+ !mTabOwnerUserHandleForLaunch.equals(resolverListAdapter.getUserHandle())
&& !mCrossProfileIntentsChecker
.hasCrossProfileIntents(resolverListAdapter.getIntents(),
- mUserIdProvider.getMyUserId(),
+ mTabOwnerUserHandleForLaunch.getIdentifier(),
resolverListAdapter.getUserHandle().getIdentifier());
if (!shouldShowBlocker) {
diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java
index a240968b..3b9d2a53 100644
--- a/java/src/com/android/intentresolver/ResolverActivity.java
+++ b/java/src/com/android/intentresolver/ResolverActivity.java
@@ -33,6 +33,8 @@ import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_S
import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
+
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UiThread;
@@ -106,6 +108,7 @@ import com.android.intentresolver.AbstractMultiProfilePagerAdapter.Profile;
import com.android.intentresolver.NoCrossProfileEmptyStateProvider.DevicePolicyBlockerEmptyState;
import com.android.intentresolver.chooser.DisplayResolveInfo;
import com.android.intentresolver.chooser.TargetInfo;
+import com.android.intentresolver.model.ResolverRankerServiceResolverComparator;
import com.android.intentresolver.widget.ResolverDrawerLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
@@ -375,8 +378,11 @@ public class ResolverActivity extends FragmentActivity implements
// turn this off when running under voice interaction, since it results in
// a more complicated UI that the current voice interaction flow is not able
// to handle. We also turn it off when the work tab is shown to simplify the UX.
+ // We also turn it off when clonedProfile is present on the device, because we might have
+ // different "last chosen" activities in the different profiles, and PackageManager doesn't
+ // provide any more information to help us select between them.
boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction()
- && !shouldShowTabs();
+ && !shouldShowTabs() && !hasCloneProfile();
mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
if (configureContentView()) {
return;
@@ -480,7 +486,7 @@ public class ResolverActivity extends FragmentActivity implements
return new NoCrossProfileEmptyStateProvider(getPersonalProfileUserHandle(),
noWorkToPersonalEmptyState, noPersonalToWorkEmptyState,
- createCrossProfileIntentsChecker(), createMyUserIdProvider());
+ createCrossProfileIntentsChecker(), getTabOwnerUserHandleForLaunch());
}
protected int appliedThemeResId() {
@@ -857,13 +863,23 @@ public class ResolverActivity extends FragmentActivity implements
// the future if resolver *were* to make any (non-overridden) calls to a version that used a
// different signature (and thus didn't return the subclass type).
@VisibleForTesting
- protected ResolverListController createListController(UserHandle unused) {
+ protected ResolverListController createListController(UserHandle userHandle) {
+ ResolverRankerServiceResolverComparator resolverComparator =
+ new ResolverRankerServiceResolverComparator(
+ this,
+ getTargetIntent(),
+ getReferrerPackageName(),
+ null,
+ null,
+ getResolverRankerServiceUserHandleList(userHandle));
return new ResolverListController(
this,
mPm,
getTargetIntent(),
getReferrerPackageName(),
- getAnnotatedUserHandles().userIdOfCallingApp);
+ getAnnotatedUserHandles().userIdOfCallingApp,
+ resolverComparator,
+ getQueryIntentsUser(userHandle));
}
/**
@@ -1003,27 +1019,6 @@ public class ResolverActivity extends FragmentActivity implements
});
}
- // TODO: have tests override `getAnnotatedUserHandles()`, and make this method `final`.
- // @NonFinalForTesting
- @Nullable
- protected UserHandle getWorkProfileUserHandle() {
- return getAnnotatedUserHandles().workProfileUserHandle;
- }
-
- // @NonFinalForTesting
- @VisibleForTesting
- public void safelyStartActivity(TargetInfo cti) {
- // We're dispatching intents that might be coming from legacy apps, so
- // don't kill ourselves.
- StrictMode.disableDeathOnFileUriExposure();
- try {
- UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
- safelyStartActivityInternal(cti, currentUserHandle, null);
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
- }
- }
-
// @NonFinalForTesting
@VisibleForTesting
protected ResolverListAdapter createResolverListAdapter(Context context,
@@ -1032,6 +1027,9 @@ public class ResolverActivity extends FragmentActivity implements
Intent startIntent = getIntent();
boolean isAudioCaptureDevice =
startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
+ UserHandle initialIntentsUserSpace = isLaunchedAsCloneProfile()
+ && userHandle.equals(getPersonalProfileUserHandle())
+ ? getCloneProfileUserHandle() : userHandle;
return new ResolverListAdapter(
context,
payloadIntents,
@@ -1042,7 +1040,8 @@ public class ResolverActivity extends FragmentActivity implements
userHandle,
getTargetIntent(),
this,
- isAudioCaptureDevice);
+ isAudioCaptureDevice,
+ initialIntentsUserSpace);
}
private LatencyTracker getLatencyTracker() {
@@ -1081,7 +1080,7 @@ public class ResolverActivity extends FragmentActivity implements
workProfileUserHandle,
getPersonalProfileUserHandle(),
getMetricsCategory(),
- createMyUserIdProvider()
+ getTabOwnerUserHandleForLaunch()
);
// Return composite provider, the order matters (the higher, the more priority)
@@ -1124,19 +1123,20 @@ public class ResolverActivity extends FragmentActivity implements
initialIntents,
rList,
filterLastUsed,
- /* userHandle */ UserHandle.of(UserHandle.myUserId()));
+ /* userHandle */ getPersonalProfileUserHandle());
return new ResolverMultiProfilePagerAdapter(
/* context */ this,
adapter,
createEmptyStateProvider(/* workProfileUserHandle= */ null),
/* workProfileQuietModeChecker= */ () -> false,
- /* workProfileUserHandle= */ null);
+ /* workProfileUserHandle= */ null,
+ getCloneProfileUserHandle());
}
private UserHandle getIntentUser() {
return getIntent().hasExtra(EXTRA_CALLING_USER)
? getIntent().getParcelableExtra(EXTRA_CALLING_USER)
- : getUser();
+ : getTabOwnerUserHandleForLaunch();
}
private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles(
@@ -1148,7 +1148,7 @@ public class ResolverActivity extends FragmentActivity implements
// this happens, we check for it here and set the current profile's tab.
int selectedProfile = getCurrentProfile();
UserHandle intentUser = getIntentUser();
- if (!getUser().equals(intentUser)) {
+ if (!getTabOwnerUserHandleForLaunch().equals(intentUser)) {
if (getPersonalProfileUserHandle().equals(intentUser)) {
selectedProfile = PROFILE_PERSONAL;
} else if (getWorkProfileUserHandle().equals(intentUser)) {
@@ -1187,7 +1187,8 @@ public class ResolverActivity extends FragmentActivity implements
createEmptyStateProvider(getWorkProfileUserHandle()),
() -> mWorkProfileAvailability.isQuietModeEnabled(),
selectedProfile,
- getWorkProfileUserHandle());
+ getWorkProfileUserHandle(),
+ getCloneProfileUserHandle());
}
/**
@@ -1210,7 +1211,8 @@ public class ResolverActivity extends FragmentActivity implements
}
protected final @Profile int getCurrentProfile() {
- return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK);
+ return (getTabOwnerUserHandleForLaunch().equals(getPersonalProfileUserHandle())
+ ? PROFILE_PERSONAL : PROFILE_WORK);
}
protected final AnnotatedUserHandles getAnnotatedUserHandles() {
@@ -1221,10 +1223,43 @@ public class ResolverActivity extends FragmentActivity implements
return getAnnotatedUserHandles().personalProfileUserHandle;
}
+ // TODO: have tests override `getAnnotatedUserHandles()`, and make this method `final`.
+ // @NonFinalForTesting
+ @Nullable
+ protected UserHandle getWorkProfileUserHandle() {
+ return getAnnotatedUserHandles().workProfileUserHandle;
+ }
+
+ // TODO: have tests override `getAnnotatedUserHandles()`, and make this method `final`.
+ @Nullable
+ protected UserHandle getCloneProfileUserHandle() {
+ return getAnnotatedUserHandles().cloneProfileUserHandle;
+ }
+
+ // TODO: have tests override `getAnnotatedUserHandles()`, and make this method `final`.
+ protected UserHandle getTabOwnerUserHandleForLaunch() {
+ return getAnnotatedUserHandles().tabOwnerUserHandleForLaunch;
+ }
+
+ protected UserHandle getUserHandleSharesheetLaunchedAs() {
+ return getAnnotatedUserHandles().userHandleSharesheetLaunchedAs;
+ }
+
+
private boolean hasWorkProfile() {
return getWorkProfileUserHandle() != null;
}
+ private boolean hasCloneProfile() {
+ return getCloneProfileUserHandle() != null;
+ }
+
+ protected final boolean isLaunchedAsCloneProfile() {
+ return hasCloneProfile()
+ && getUserHandleSharesheetLaunchedAs().equals(getCloneProfileUserHandle());
+ }
+
+
protected final boolean shouldShowTabs() {
return hasWorkProfile();
}
@@ -1498,6 +1533,13 @@ public class ResolverActivity extends FragmentActivity implements
mAlwaysButton.setEnabled(false);
return;
}
+ // In case of clonedProfile being active, we do not allow the 'Always' option in the
+ // disambiguation dialog of Personal Profile as the package manager cannot distinguish
+ // between cross-profile preferred activities.
+ if (hasCloneProfile() && (mMultiProfilePagerAdapter.getCurrentPage() == PROFILE_PERSONAL)) {
+ mAlwaysButton.setEnabled(false);
+ return;
+ }
boolean enabled = false;
ResolveInfo ri = null;
if (hasValidSelection) {
@@ -1572,6 +1614,16 @@ public class ResolverActivity extends FragmentActivity implements
}
}
+ /** Start the activity specified by the {@link TargetInfo}.*/
+ public final void safelyStartActivity(TargetInfo cti) {
+ // In case cloned apps are present, we would want to start those apps in cloned user
+ // space, which will not be same as adaptor's userHandle. resolveInfo.userHandle
+ // identifies the correct user space in such cases.
+ UserHandle activityUserHandle = getResolveInfoUserHandle(
+ cti.getResolveInfo(), mMultiProfilePagerAdapter.getCurrentUserHandle());
+ safelyStartActivityAsUser(cti, activityUserHandle, null);
+ }
+
/**
* Start activity as a fixed user handle.
* @param cti TargetInfo to be launched.
@@ -1594,7 +1646,8 @@ public class ResolverActivity extends FragmentActivity implements
}
}
- private void safelyStartActivityInternal(
+ @VisibleForTesting
+ protected void safelyStartActivityInternal(
TargetInfo cti, UserHandle user, @Nullable Bundle options) {
// If the target is suspended, the activity will not be successfully launched.
// Do not unregister from package manager updates in this case
@@ -2172,16 +2225,10 @@ public class ResolverActivity extends FragmentActivity implements
public final boolean useLayoutWithDefault() {
// We only use the default app layout when the profile of the active user has a
// filtered item. We always show the same default app even in the inactive user profile.
- boolean currentUserAdapterHasFilteredItem;
- if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
- == UserHandle.myUserId()) {
- currentUserAdapterHasFilteredItem =
- mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem();
- } else {
- currentUserAdapterHasFilteredItem =
- mMultiProfilePagerAdapter.getInactiveListAdapter().hasFilteredItem();
- }
- return mSupportsAlwaysUseOption && currentUserAdapterHasFilteredItem;
+ boolean adapterForCurrentUserHasFilteredItem =
+ mMultiProfilePagerAdapter.getListAdapterForUserHandle(
+ getTabOwnerUserHandleForLaunch()).hasFilteredItem();
+ return mSupportsAlwaysUseOption && adapterForCurrentUserHasFilteredItem;
}
/**
@@ -2200,7 +2247,14 @@ public class ResolverActivity extends FragmentActivity implements
return lhs == null ? rhs == null
: lhs.activityInfo == null ? rhs.activityInfo == null
: Objects.equals(lhs.activityInfo.name, rhs.activityInfo.name)
- && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName);
+ && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName)
+ // Comparing against resolveInfo.userHandle in case cloned apps are present,
+ // as they will have the same activityInfo.
+ && Objects.equals(
+ getResolveInfoUserHandle(lhs,
+ mMultiProfilePagerAdapter.getActiveListAdapter().getUserHandle()),
+ getResolveInfoUserHandle(rhs,
+ mMultiProfilePagerAdapter.getActiveListAdapter().getUserHandle()));
}
private boolean inactiveListAdapterHasItems() {
@@ -2307,4 +2361,44 @@ public class ResolverActivity extends FragmentActivity implements
}
}
}
+ /**
+ * Returns the {@link UserHandle} to use when querying resolutions for intents in a
+ * {@link ResolverListController} configured for the provided {@code userHandle}.
+ */
+ protected final UserHandle getQueryIntentsUser(UserHandle userHandle) {
+ return mLazyAnnotatedUserHandles.get().getQueryIntentsUser(userHandle);
+ }
+
+ /**
+ * Returns the {@link List} of {@link UserHandle} to pass on to the
+ * {@link ResolverRankerServiceResolverComparator} as per the provided {@code userHandle}.
+ */
+ @VisibleForTesting(visibility = PROTECTED)
+ public final List<UserHandle> getResolverRankerServiceUserHandleList(UserHandle userHandle) {
+ return getResolverRankerServiceUserHandleListInternal(userHandle);
+ }
+
+ @VisibleForTesting
+ protected List<UserHandle> getResolverRankerServiceUserHandleListInternal(
+ UserHandle userHandle) {
+ List<UserHandle> userList = new ArrayList<>();
+ userList.add(userHandle);
+ // Add clonedProfileUserHandle to the list only if we are:
+ // a. Building the Personal Tab.
+ // b. CloneProfile exists on the device.
+ if (userHandle.equals(getPersonalProfileUserHandle())
+ && getCloneProfileUserHandle() != null) {
+ userList.add(getCloneProfileUserHandle());
+ }
+ return userList;
+ }
+
+ /**
+ * This function is temporary in nature, and its usages will be replaced with just
+ * resolveInfo.userHandle, once it is available, once sharesheet is stable.
+ */
+ public static UserHandle getResolveInfoUserHandle(ResolveInfo resolveInfo,
+ UserHandle predictedHandle) {
+ return resolveInfo.userHandle;
+ }
}
diff --git a/java/src/com/android/intentresolver/ResolverListAdapter.java b/java/src/com/android/intentresolver/ResolverListAdapter.java
index eac275cc..b0586f2d 100644
--- a/java/src/com/android/intentresolver/ResolverListAdapter.java
+++ b/java/src/com/android/intentresolver/ResolverListAdapter.java
@@ -97,6 +97,8 @@ public class ResolverListAdapter extends BaseAdapter {
private boolean mFilterLastUsed;
private Runnable mPostListReadyRunnable;
private boolean mIsTabLoaded;
+ // Represents the UserSpace in which the Initial Intents should be resolved.
+ private final UserHandle mInitialIntentsUserSpace;
public ResolverListAdapter(
Context context,
@@ -108,7 +110,8 @@ public class ResolverListAdapter extends BaseAdapter {
UserHandle userHandle,
Intent targetIntent,
ResolverListCommunicator resolverListCommunicator,
- boolean isAudioCaptureDevice) {
+ boolean isAudioCaptureDevice,
+ UserHandle initialIntentsUserSpace) {
mContext = context;
mIntents = payloadIntents;
mInitialIntents = initialIntents;
@@ -125,6 +128,7 @@ public class ResolverListAdapter extends BaseAdapter {
final ActivityManager am = (ActivityManager) mContext.getSystemService(ACTIVITY_SERVICE);
mIconDpi = am.getLauncherLargeIconDensity();
mPresentationFactory = new TargetPresentationGetter.Factory(mContext, mIconDpi);
+ mInitialIntentsUserSpace = initialIntentsUserSpace;
}
public final DisplayResolveInfo getFirstDisplayResolveInfo() {
@@ -176,19 +180,25 @@ public class ResolverListAdapter extends BaseAdapter {
}
/**
- * Returns the app share score of the given {@code componentName}.
+ * Returns the app share score of the given {@code targetInfo}.
*/
- public float getScore(ComponentName componentName) {
- return mResolverListController.getScore(componentName);
+ public float getScore(TargetInfo targetInfo) {
+ return mResolverListController.getScore(targetInfo);
}
- public void updateModel(ComponentName componentName) {
- mResolverListController.updateModel(componentName);
+ /**
+ * Updates the model about the chosen {@code targetInfo}.
+ */
+ public void updateModel(TargetInfo targetInfo) {
+ mResolverListController.updateModel(targetInfo);
}
- public void updateChooserCounts(String packageName, String action) {
+ /**
+ * Updates the model about Chooser Activity selection.
+ */
+ public void updateChooserCounts(String packageName, String action, UserHandle userHandle) {
mResolverListController.updateChooserCounts(
- packageName, getUserHandle().getIdentifier(), action);
+ packageName, userHandle, action);
}
List<ResolvedComponentInfo> getUnfilteredResolveList() {
@@ -468,6 +478,7 @@ public class ResolverListAdapter extends BaseAdapter {
ri.icon = 0;
}
+ ri.userHandle = mInitialIntentsUserSpace;
addResolveInfo(DisplayResolveInfo.newDisplayResolveInfo(
ii,
ri,
@@ -761,8 +772,10 @@ public class ResolverListAdapter extends BaseAdapter {
}
Drawable loadIconForResolveInfo(ResolveInfo ri) {
- // Load icons based on the current process. If in work profile icons should be badged.
- return mPresentationFactory.makePresentationGetter(ri).getIcon(getUserHandle());
+ // Load icons based on userHandle from ResolveInfo. If in work profile/clone profile, icons
+ // should be badged.
+ return mPresentationFactory.makePresentationGetter(ri)
+ .getIcon(ResolverActivity.getResolveInfoUserHandle(ri, getUserHandle()));
}
protected final Drawable loadIconPlaceholder() {
diff --git a/java/src/com/android/intentresolver/ResolverListController.java b/java/src/com/android/intentresolver/ResolverListController.java
index b4544c43..d5a5fedf 100644
--- a/java/src/com/android/intentresolver/ResolverListController.java
+++ b/java/src/com/android/intentresolver/ResolverListController.java
@@ -32,8 +32,8 @@ import android.os.UserHandle;
import android.util.Log;
import com.android.intentresolver.chooser.DisplayResolveInfo;
+import com.android.intentresolver.chooser.TargetInfo;
import com.android.intentresolver.model.AbstractResolverComparator;
-import com.android.intentresolver.model.ResolverRankerServiceResolverComparator;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
@@ -58,6 +58,7 @@ public class ResolverListController {
private static final String TAG = "ResolverListController";
private static final boolean DEBUG = false;
+ private final UserHandle mQueryIntentsAsUser;
private AbstractResolverComparator mResolverComparator;
private boolean isComputed = false;
@@ -67,25 +68,16 @@ public class ResolverListController {
PackageManager pm,
Intent targetIntent,
String referrerPackage,
- int launchedFromUid) {
- this(context, pm, targetIntent, referrerPackage, launchedFromUid,
- new ResolverRankerServiceResolverComparator(
- context, targetIntent, referrerPackage, null, null));
- }
-
- public ResolverListController(
- Context context,
- PackageManager pm,
- Intent targetIntent,
- String referrerPackage,
int launchedFromUid,
- AbstractResolverComparator resolverComparator) {
+ AbstractResolverComparator resolverComparator,
+ UserHandle queryIntentsAsUser) {
mContext = context;
mpm = pm;
mLaunchedFromUid = launchedFromUid;
mTargetIntent = targetIntent;
mReferrerPackage = referrerPackage;
mResolverComparator = resolverComparator;
+ mQueryIntentsAsUser = queryIntentsAsUser;
}
@VisibleForTesting
@@ -118,7 +110,8 @@ public class ResolverListController {
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
- | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0);
+ | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0)
+ | PackageManager.MATCH_CLONE_PROFILE;
return getResolversForIntentAsUserInternal(intents, userHandle, baseFlags);
}
@@ -154,6 +147,10 @@ public class ResolverListController {
final int intoCount = into.size();
for (int i = 0; i < fromCount; i++) {
final ResolveInfo newInfo = from.get(i);
+ if (newInfo.userHandle == null) {
+ Log.w(TAG, "Skipping ResolveInfo with no userHandle: " + newInfo);
+ continue;
+ }
boolean found = false;
// Only loop to the end of into as it was before we started; no dupes in from.
for (int j = 0; j < intoCount; j++) {
@@ -344,22 +341,28 @@ public class ResolverListController {
@VisibleForTesting
public float getScore(DisplayResolveInfo target) {
- return mResolverComparator.getScore(target.getResolvedComponentName());
+ return mResolverComparator.getScore(target);
}
/**
* Returns the app share score of the given {@code componentName}.
*/
- public float getScore(ComponentName componentName) {
- return mResolverComparator.getScore(componentName);
+ public float getScore(TargetInfo targetInfo) {
+ return mResolverComparator.getScore(targetInfo);
}
- public void updateModel(ComponentName componentName) {
- mResolverComparator.updateModel(componentName);
+ /**
+ * Updates the model about the chosen {@code targetInfo}.
+ */
+ public void updateModel(TargetInfo targetInfo) {
+ mResolverComparator.updateModel(targetInfo);
}
- public void updateChooserCounts(String packageName, int userId, String action) {
- mResolverComparator.updateChooserCounts(packageName, userId, action);
+ /**
+ * Updates the model about Chooser Activity selection.
+ */
+ public void updateChooserCounts(String packageName, UserHandle user, String action) {
+ mResolverComparator.updateChooserCounts(packageName, user, action);
}
public void destroy() {
diff --git a/java/src/com/android/intentresolver/ResolverMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/ResolverMultiProfilePagerAdapter.java
index 48e3b62d..85d97ad5 100644
--- a/java/src/com/android/intentresolver/ResolverMultiProfilePagerAdapter.java
+++ b/java/src/com/android/intentresolver/ResolverMultiProfilePagerAdapter.java
@@ -44,7 +44,8 @@ public class ResolverMultiProfilePagerAdapter extends
ResolverListAdapter adapter,
EmptyStateProvider emptyStateProvider,
Supplier<Boolean> workProfileQuietModeChecker,
- UserHandle workProfileUserHandle) {
+ UserHandle workProfileUserHandle,
+ UserHandle cloneProfileUserHandle) {
this(
context,
ImmutableList.of(adapter),
@@ -52,6 +53,7 @@ public class ResolverMultiProfilePagerAdapter extends
workProfileQuietModeChecker,
/* defaultProfile= */ 0,
workProfileUserHandle,
+ cloneProfileUserHandle,
new BottomPaddingOverrideSupplier());
}
@@ -61,7 +63,8 @@ public class ResolverMultiProfilePagerAdapter extends
EmptyStateProvider emptyStateProvider,
Supplier<Boolean> workProfileQuietModeChecker,
@Profile int defaultProfile,
- UserHandle workProfileUserHandle) {
+ UserHandle workProfileUserHandle,
+ UserHandle cloneProfileUserHandle) {
this(
context,
ImmutableList.of(personalAdapter, workAdapter),
@@ -69,6 +72,7 @@ public class ResolverMultiProfilePagerAdapter extends
workProfileQuietModeChecker,
defaultProfile,
workProfileUserHandle,
+ cloneProfileUserHandle,
new BottomPaddingOverrideSupplier());
}
@@ -79,6 +83,7 @@ public class ResolverMultiProfilePagerAdapter extends
Supplier<Boolean> workProfileQuietModeChecker,
@Profile int defaultProfile,
UserHandle workProfileUserHandle,
+ UserHandle cloneProfileUserHandle,
BottomPaddingOverrideSupplier bottomPaddingOverrideSupplier) {
super(
context,
@@ -89,6 +94,7 @@ public class ResolverMultiProfilePagerAdapter extends
workProfileQuietModeChecker,
defaultProfile,
workProfileUserHandle,
+ cloneProfileUserHandle,
() -> (ViewGroup) LayoutInflater.from(context).inflate(
R.layout.resolver_list_per_profile, null, false),
bottomPaddingOverrideSupplier);
diff --git a/java/src/com/android/intentresolver/model/AbstractResolverComparator.java b/java/src/com/android/intentresolver/model/AbstractResolverComparator.java
index ea767568..7357fde9 100644
--- a/java/src/com/android/intentresolver/model/AbstractResolverComparator.java
+++ b/java/src/com/android/intentresolver/model/AbstractResolverComparator.java
@@ -32,11 +32,16 @@ import android.util.Log;
import com.android.intentresolver.ChooserActivityLogger;
import com.android.intentresolver.ResolvedComponentInfo;
import com.android.intentresolver.ResolverActivity;
+import com.android.intentresolver.chooser.TargetInfo;
+
+import com.google.android.collect.Lists;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Used to sort resolved activities in {@link ResolverListController}.
@@ -50,8 +55,8 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
private static final String TAG = "AbstractResolverComp";
protected Runnable mAfterCompute;
- protected final PackageManager mPm;
- protected final UsageStatsManager mUsm;
+ protected final Map<UserHandle, PackageManager> mPmMap = new HashMap<>();
+ protected final Map<UserHandle, UsageStatsManager> mUsmMap = new HashMap<>();
protected String[] mAnnotations;
protected String mContentType;
@@ -100,14 +105,48 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
}
};
- public AbstractResolverComparator(Context context, Intent intent) {
+ /**
+ * Constructor to initialize the comparator.
+ * @param launchedFromContext the activity calling this comparator
+ * @param intent original intent
+ * @param resolvedActivityUserSpace refers to the userSpace used by the comparator for
+ * fetching activity stats and recording activity selection.
+ * The latter could be different from the userSpace provided by
+ * context.
+ */
+ public AbstractResolverComparator(
+ Context launchedFromContext,
+ Intent intent,
+ UserHandle resolvedActivityUserSpace) {
+ this(launchedFromContext, intent, Lists.newArrayList(resolvedActivityUserSpace));
+ }
+
+
+ /**
+ * Constructor to initialize the comparator.
+ * @param launchedFromContext the activity calling this comparator
+ * @param intent original intent
+ * @param resolvedActivityUserSpaceList refers to the userSpace(s) used by the comparator for
+ * fetching activity stats and recording activity
+ * selection. The latter could be different from the
+ * userSpace provided by context.
+ */
+ public AbstractResolverComparator(
+ Context launchedFromContext,
+ Intent intent,
+ List<UserHandle> resolvedActivityUserSpaceList) {
String scheme = intent.getScheme();
mHttp = "http".equals(scheme) || "https".equals(scheme);
mContentType = intent.getType();
getContentAnnotations(intent);
- mPm = context.getPackageManager();
- mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
- mAzComparator = new AzInfoComparator(context);
+ for (UserHandle user : resolvedActivityUserSpaceList) {
+ Context userContext = launchedFromContext.createContextAsUser(user, 0);
+ mPmMap.put(user, userContext.getPackageManager());
+ mUsmMap.put(
+ user,
+ (UsageStatsManager) userContext.getSystemService(Context.USAGE_STATS_SERVICE));
+ }
+ mAzComparator = new AzInfoComparator(launchedFromContext);
}
// get annotations of content from intent.
@@ -197,8 +236,8 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
/**
* Computes features for each target. This will be called before calls to {@link
- * #getScore(ComponentName)} or {@link #compare(Object, Object)}, in order to prepare the
- * comparator for those calls. Note that {@link #getScore(ComponentName)} uses {@link
+ * #getScore(TargetInfo)} or {@link #compare(ResolveInfo, ResolveInfo)}, in order to prepare the
+ * comparator for those calls. Note that {@link #getScore(TargetInfo)} uses {@link
* ComponentName}, so the implementation will have to be prepared to identify a {@link
* ResolvedComponentInfo} by {@link ComponentName}. {@link #beforeCompute()} will be called
* before doing any computing.
@@ -215,7 +254,7 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
* Returns the score that was calculated for the corresponding {@link ResolvedComponentInfo}
* when {@link #compute(List)} was called before this.
*/
- public abstract float getScore(ComponentName name);
+ public abstract float getScore(TargetInfo targetInfo);
/** Handles result message sent to mHandler. */
abstract void handleResultMessage(Message message);
@@ -223,9 +262,14 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
/**
* Reports to UsageStats what was chosen.
*/
- public final void updateChooserCounts(String packageName, int userId, String action) {
- if (mUsm != null) {
- mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action);
+ public final void updateChooserCounts(String packageName, UserHandle user, String action) {
+ if (mUsmMap.containsKey(user)) {
+ mUsmMap.get(user).reportChooserSelection(
+ packageName,
+ user.getIdentifier(),
+ mContentType,
+ mAnnotations,
+ action);
}
}
@@ -235,9 +279,9 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
* <p>Default implementation does nothing, as we could have simple model that does not train
* online.
*
- * @param componentName the component that the user clicked
+ * * @param targetInfo the target that the user clicked.
*/
- public void updateModel(ComponentName componentName) {
+ public void updateModel(TargetInfo targetInfo) {
}
/** Called before {@link #doCompute(List)}. Sets up 500ms timeout. */
diff --git a/java/src/com/android/intentresolver/model/AppPredictionServiceResolverComparator.java b/java/src/com/android/intentresolver/model/AppPredictionServiceResolverComparator.java
index c986ef15..84dca3ff 100644
--- a/java/src/com/android/intentresolver/model/AppPredictionServiceResolverComparator.java
+++ b/java/src/com/android/intentresolver/model/AppPredictionServiceResolverComparator.java
@@ -33,6 +33,9 @@ import android.util.Log;
import com.android.intentresolver.ChooserActivityLogger;
import com.android.intentresolver.ResolvedComponentInfo;
+import com.android.intentresolver.chooser.TargetInfo;
+
+import com.google.android.collect.Lists;
import java.util.ArrayList;
import java.util.Comparator;
@@ -70,7 +73,7 @@ public class AppPredictionServiceResolverComparator extends AbstractResolverComp
AppPredictor appPredictor,
UserHandle user,
ChooserActivityLogger chooserActivityLogger) {
- super(context, intent);
+ super(context, intent, Lists.newArrayList(user));
mContext = context;
mIntent = intent;
mAppPredictor = appPredictor;
@@ -108,9 +111,12 @@ public class AppPredictionServiceResolverComparator extends AbstractResolverComp
// APS for chooser is disabled. Fallback to resolver.
mResolverRankerService =
new ResolverRankerServiceResolverComparator(
- mContext, mIntent, mReferrerPackage,
+ mContext,
+ mIntent,
+ mReferrerPackage,
() -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT),
- getChooserActivityLogger());
+ getChooserActivityLogger(),
+ mUser);
mComparatorModel = buildUpdatedModel();
mResolverRankerService.compute(targets);
} else {
@@ -167,13 +173,13 @@ public class AppPredictionServiceResolverComparator extends AbstractResolverComp
}
@Override
- public float getScore(ComponentName name) {
- return mComparatorModel.getScore(name);
+ public float getScore(TargetInfo targetInfo) {
+ return mComparatorModel.getScore(targetInfo);
}
@Override
- public void updateModel(ComponentName componentName) {
- mComparatorModel.notifyOnTargetSelected(componentName);
+ public void updateModel(TargetInfo targetInfo) {
+ mComparatorModel.notifyOnTargetSelected(targetInfo);
}
@Override
@@ -246,11 +252,11 @@ public class AppPredictionServiceResolverComparator extends AbstractResolverComp
}
@Override
- public float getScore(ComponentName name) {
+ public float getScore(TargetInfo targetInfo) {
if (mResolverRankerService != null) {
- return mResolverRankerService.getScore(name);
+ return mResolverRankerService.getScore(targetInfo);
}
- Integer rank = mTargetRanks.get(name);
+ Integer rank = mTargetRanks.get(targetInfo.getResolvedComponentName());
if (rank == null) {
Log.w(TAG, "Score requested for unknown component. Did you call compute yet?");
return 0f;
@@ -260,18 +266,19 @@ public class AppPredictionServiceResolverComparator extends AbstractResolverComp
}
@Override
- public void notifyOnTargetSelected(ComponentName componentName) {
+ public void notifyOnTargetSelected(TargetInfo targetInfo) {
if (mResolverRankerService != null) {
- mResolverRankerService.updateModel(componentName);
+ mResolverRankerService.updateModel(targetInfo);
return;
}
+ ComponentName targetComponent = targetInfo.getResolvedComponentName();
+ AppTargetId targetId = new AppTargetId(targetComponent.toString());
+ AppTarget appTarget =
+ new AppTarget.Builder(targetId, targetComponent.getPackageName(), mUser)
+ .setClassName(targetComponent.getClassName())
+ .build();
mAppPredictor.notifyAppTargetEvent(
- new AppTargetEvent.Builder(
- new AppTarget.Builder(
- new AppTargetId(componentName.toString()),
- componentName.getPackageName(), mUser)
- .setClassName(componentName.getClassName()).build(),
- ACTION_LAUNCH).build());
+ new AppTargetEvent.Builder(appTarget, ACTION_LAUNCH).build());
}
}
}
diff --git a/java/src/com/android/intentresolver/model/ResolverComparatorModel.java b/java/src/com/android/intentresolver/model/ResolverComparatorModel.java
index 3616a853..4835ea17 100644
--- a/java/src/com/android/intentresolver/model/ResolverComparatorModel.java
+++ b/java/src/com/android/intentresolver/model/ResolverComparatorModel.java
@@ -16,9 +16,10 @@
package com.android.intentresolver.model;
-import android.content.ComponentName;
import android.content.pm.ResolveInfo;
+import com.android.intentresolver.chooser.TargetInfo;
+
import java.util.Comparator;
/**
@@ -44,7 +45,7 @@ interface ResolverComparatorModel {
* likelihood that the user will select that component as the target. Implementations that don't
* assign numerical scores are <em>recommended</em> to return a value of 0 for all components.
*/
- float getScore(ComponentName name);
+ float getScore(TargetInfo targetInfo);
/**
* Notify the model that the user selected a target. (Models may log this information, use it as
@@ -52,5 +53,5 @@ interface ResolverComparatorModel {
* {@code ResolverComparatorModel} instance is immutable, clients will need to get an up-to-date
* instance in order to see any changes in the ranking that might result from this feedback.
*/
- void notifyOnTargetSelected(ComponentName componentName);
+ void notifyOnTargetSelected(TargetInfo targetInfo);
}
diff --git a/java/src/com/android/intentresolver/model/ResolverRankerServiceResolverComparator.java b/java/src/com/android/intentresolver/model/ResolverRankerServiceResolverComparator.java
index 0431078c..725212e4 100644
--- a/java/src/com/android/intentresolver/model/ResolverRankerServiceResolverComparator.java
+++ b/java/src/com/android/intentresolver/model/ResolverRankerServiceResolverComparator.java
@@ -17,11 +17,13 @@
package com.android.intentresolver.model;
+import android.annotation.Nullable;
import android.app.usage.UsageStats;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -39,12 +41,16 @@ import android.util.Log;
import com.android.intentresolver.ChooserActivityLogger;
import com.android.intentresolver.ResolvedComponentInfo;
+import com.android.intentresolver.chooser.TargetInfo;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.google.android.collect.Lists;
+
import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -70,10 +76,10 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200;
private final Collator mCollator;
- private final Map<String, UsageStats> mStats;
+ private final Map<UserHandle, Map<String, UsageStats>> mStatsPerUser;
private final long mCurrentTime;
private final long mSinceTime;
- private final LinkedHashMap<ComponentName, ResolverTarget> mTargetsDict = new LinkedHashMap<>();
+ private final Map<UserHandle, Map<ComponentName, ResolverTarget>> mTargetsDictPerUser;
private final String mReferrerPackage;
private final Object mLock = new Object();
private ArrayList<ResolverTarget> mTargets;
@@ -86,17 +92,48 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
private CountDownLatch mConnectSignal;
private ResolverRankerServiceComparatorModel mComparatorModel;
- public ResolverRankerServiceResolverComparator(Context context, Intent intent,
- String referrerPackage, Runnable afterCompute,
- ChooserActivityLogger chooserActivityLogger) {
- super(context, intent);
- mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
+ /**
+ * Constructor to initialize the comparator.
+ * @param launchedFromContext the activity calling this comparator
+ * @param intent original intent
+ * @param targetUserSpace the userSpace(s) used by the comparator for fetching activity stats
+ * and recording activity selection. The latter could be different from
+ * the userSpace provided by context.
+ */
+ public ResolverRankerServiceResolverComparator(Context launchedFromContext, Intent intent,
+ String referrerPackage, Runnable afterCompute,
+ ChooserActivityLogger chooserActivityLogger, UserHandle targetUserSpace) {
+ this(launchedFromContext, intent, referrerPackage, afterCompute, chooserActivityLogger,
+ Lists.newArrayList(targetUserSpace));
+ }
+
+ /**
+ * Constructor to initialize the comparator.
+ * @param launchedFromContext the activity calling this comparator
+ * @param intent original intent
+ * @param targetUserSpaceList the userSpace(s) used by the comparator for fetching activity
+ * stats and recording activity selection. The latter could be
+ * different from the userSpace provided by context.
+ */
+ public ResolverRankerServiceResolverComparator(Context launchedFromContext, Intent intent,
+ String referrerPackage, Runnable afterCompute,
+ ChooserActivityLogger chooserActivityLogger, List<UserHandle> targetUserSpaceList) {
+ super(launchedFromContext, intent, targetUserSpaceList);
+ mCollator = Collator.getInstance(
+ launchedFromContext.getResources().getConfiguration().locale);
mReferrerPackage = referrerPackage;
- mContext = context;
+ mContext = launchedFromContext;
mCurrentTime = System.currentTimeMillis();
mSinceTime = mCurrentTime - USAGE_STATS_PERIOD;
- mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
+ mStatsPerUser = new HashMap<>();
+ mTargetsDictPerUser = new HashMap<>();
+ for (UserHandle user : targetUserSpaceList) {
+ mStatsPerUser.put(
+ user,
+ mUsmMap.get(user).queryAndAggregateUsageStats(mSinceTime, mCurrentTime));
+ mTargetsDictPerUser.put(user, new LinkedHashMap<>());
+ }
mAction = intent.getAction();
mRankerServiceName = new ComponentName(mContext, this.getClass());
setCallBack(afterCompute);
@@ -147,58 +184,68 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
float mostChooserScore = 1.0f;
for (ResolvedComponentInfo target : targets) {
+ if (target.getResolveInfoAt(0) == null) {
+ continue;
+ }
final ResolverTarget resolverTarget = new ResolverTarget();
- mTargetsDict.put(target.name, resolverTarget);
- final UsageStats pkStats = mStats.get(target.name.getPackageName());
- if (pkStats != null) {
- // Only count recency for apps that weren't the caller
- // since the caller is always the most recent.
- // Persistent processes muck this up, so omit them too.
- if (!target.name.getPackageName().equals(mReferrerPackage)
- && !isPersistentProcess(target)) {
- final float recencyScore =
- (float) Math.max(pkStats.getLastTimeUsed() - recentSinceTime, 0);
- resolverTarget.setRecencyScore(recencyScore);
- if (recencyScore > mostRecencyScore) {
- mostRecencyScore = recencyScore;
+ final UserHandle resolvedComponentUserSpace =
+ target.getResolveInfoAt(0).userHandle;
+ final Map<ComponentName, ResolverTarget> targetsDict =
+ mTargetsDictPerUser.get(resolvedComponentUserSpace);
+ final Map<String, UsageStats> stats = mStatsPerUser.get(resolvedComponentUserSpace);
+ if (targetsDict != null && stats != null) {
+ targetsDict.put(target.name, resolverTarget);
+ final UsageStats pkStats = stats.get(target.name.getPackageName());
+ if (pkStats != null) {
+ // Only count recency for apps that weren't the caller
+ // since the caller is always the most recent.
+ // Persistent processes muck this up, so omit them too.
+ if (!target.name.getPackageName().equals(mReferrerPackage)
+ && !isPersistentProcess(target)) {
+ final float recencyScore =
+ (float) Math.max(pkStats.getLastTimeUsed() - recentSinceTime, 0);
+ resolverTarget.setRecencyScore(recencyScore);
+ if (recencyScore > mostRecencyScore) {
+ mostRecencyScore = recencyScore;
+ }
+ }
+ final float timeSpentScore = (float) pkStats.getTotalTimeInForeground();
+ resolverTarget.setTimeSpentScore(timeSpentScore);
+ if (timeSpentScore > mostTimeSpentScore) {
+ mostTimeSpentScore = timeSpentScore;
+ }
+ final float launchScore = (float) pkStats.mLaunchCount;
+ resolverTarget.setLaunchScore(launchScore);
+ if (launchScore > mostLaunchScore) {
+ mostLaunchScore = launchScore;
}
- }
- final float timeSpentScore = (float) pkStats.getTotalTimeInForeground();
- resolverTarget.setTimeSpentScore(timeSpentScore);
- if (timeSpentScore > mostTimeSpentScore) {
- mostTimeSpentScore = timeSpentScore;
- }
- final float launchScore = (float) pkStats.mLaunchCount;
- resolverTarget.setLaunchScore(launchScore);
- if (launchScore > mostLaunchScore) {
- mostLaunchScore = launchScore;
- }
- float chooserScore = 0.0f;
- if (pkStats.mChooserCounts != null && mAction != null
- && pkStats.mChooserCounts.get(mAction) != null) {
- chooserScore = (float) pkStats.mChooserCounts.get(mAction)
- .getOrDefault(mContentType, 0);
- if (mAnnotations != null) {
- final int size = mAnnotations.length;
- for (int i = 0; i < size; i++) {
- chooserScore += (float) pkStats.mChooserCounts.get(mAction)
- .getOrDefault(mAnnotations[i], 0);
+ float chooserScore = 0.0f;
+ if (pkStats.mChooserCounts != null && mAction != null
+ && pkStats.mChooserCounts.get(mAction) != null) {
+ chooserScore = (float) pkStats.mChooserCounts.get(mAction)
+ .getOrDefault(mContentType, 0);
+ if (mAnnotations != null) {
+ final int size = mAnnotations.length;
+ for (int i = 0; i < size; i++) {
+ chooserScore += (float) pkStats.mChooserCounts.get(mAction)
+ .getOrDefault(mAnnotations[i], 0);
+ }
}
}
- }
- if (DEBUG) {
- if (mAction == null) {
- Log.d(TAG, "Action type is null");
- } else {
- Log.d(TAG, "Chooser Count of " + mAction + ":"
- + target.name.getPackageName() + " is "
- + Float.toString(chooserScore));
+ if (DEBUG) {
+ if (mAction == null) {
+ Log.d(TAG, "Action type is null");
+ } else {
+ Log.d(TAG, "Chooser Count of " + mAction + ":"
+ + target.name.getPackageName() + " is "
+ + Float.toString(chooserScore));
+ }
+ }
+ resolverTarget.setChooserScore(chooserScore);
+ if (chooserScore > mostChooserScore) {
+ mostChooserScore = chooserScore;
}
- }
- resolverTarget.setChooserScore(chooserScore);
- if (chooserScore > mostChooserScore) {
- mostChooserScore = chooserScore;
}
}
}
@@ -210,7 +257,10 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
+ " mostChooserScore: " + mostChooserScore);
}
- mTargets = new ArrayList<>(mTargetsDict.values());
+ mTargets = new ArrayList<>();
+ for (UserHandle u : mTargetsDictPerUser.keySet()) {
+ mTargets.addAll(mTargetsDictPerUser.get(u).values());
+ }
for (ResolverTarget target : mTargets) {
final float recency = target.getRecencyScore() / mostRecencyScore;
setFeatures(target, recency * recency * RECENCY_MULTIPLIER,
@@ -233,15 +283,15 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
}
@Override
- public float getScore(ComponentName name) {
- return mComparatorModel.getScore(name);
+ public float getScore(TargetInfo targetInfo) {
+ return mComparatorModel.getScore(targetInfo);
}
// update ranking model when the connection to it is valid.
@Override
- public void updateModel(ComponentName componentName) {
+ public void updateModel(TargetInfo targetInfo) {
synchronized (mLock) {
- mComparatorModel.notifyOnTargetSelected(componentName);
+ mComparatorModel.notifyOnTargetSelected(targetInfo);
}
}
@@ -282,7 +332,8 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
// resolve the service for ranking.
private Intent resolveRankerService() {
Intent intent = new Intent(ResolverRankerService.SERVICE_INTERFACE);
- final List<ResolveInfo> resolveInfos = mPm.queryIntentServices(intent, 0);
+ final List<ResolveInfo> resolveInfos = mContext.getPackageManager()
+ .queryIntentServices(intent, 0);
for (ResolveInfo resolveInfo : resolveInfos) {
if (resolveInfo == null || resolveInfo.serviceInfo == null
|| resolveInfo.serviceInfo.applicationInfo == null) {
@@ -295,7 +346,8 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
resolveInfo.serviceInfo.applicationInfo.packageName,
resolveInfo.serviceInfo.name);
try {
- final String perm = mPm.getServiceInfo(componentName, 0).permission;
+ final String perm =
+ mContext.getPackageManager().getServiceInfo(componentName, 0).permission;
if (!ResolverRankerService.BIND_PERMISSION.equals(perm)) {
Log.w(TAG, "ResolverRankerService " + componentName + " does not require"
+ " permission " + ResolverRankerService.BIND_PERMISSION
@@ -306,9 +358,9 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
+ " in the manifest.");
continue;
}
- if (PackageManager.PERMISSION_GRANTED != mPm.checkPermission(
- ResolverRankerService.HOLD_PERMISSION,
- resolveInfo.serviceInfo.packageName)) {
+ if (PackageManager.PERMISSION_GRANTED != mContext.getPackageManager()
+ .checkPermission(ResolverRankerService.HOLD_PERMISSION,
+ resolveInfo.serviceInfo.packageName)) {
Log.w(TAG, "ResolverRankerService " + componentName + " does not hold"
+ " permission " + ResolverRankerService.HOLD_PERMISSION
+ " - this service will not be queried for "
@@ -386,7 +438,9 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
@Override
void beforeCompute() {
super.beforeCompute();
- mTargetsDict.clear();
+ for (UserHandle userHandle : mTargetsDictPerUser.keySet()) {
+ mTargetsDictPerUser.get(userHandle).clear();
+ }
mTargets = null;
mRankerServiceName = new ComponentName(mContext, this.getClass());
mComparatorModel = buildUpdatedModel();
@@ -468,14 +522,14 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
// so the ResolverComparatorModel may provide inconsistent results. We should make immutable
// copies of the data (waiting for any necessary remaining data before creating the model).
return new ResolverRankerServiceComparatorModel(
- mStats,
- mTargetsDict,
+ mStatsPerUser,
+ mTargetsDictPerUser,
mTargets,
mCollator,
mRanker,
mRankerServiceName,
(mAnnotations != null),
- mPm);
+ mPmMap);
}
/**
@@ -484,35 +538,36 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
* removing the complex legacy API.
*/
static class ResolverRankerServiceComparatorModel implements ResolverComparatorModel {
- private final Map<String, UsageStats> mStats; // Treat as immutable.
- private final Map<ComponentName, ResolverTarget> mTargetsDict; // Treat as immutable.
+ private final Map<UserHandle, Map<String, UsageStats>> mStatsPerUser; // Treat as immutable.
+ // Treat as immutable.
+ private final Map<UserHandle, Map<ComponentName, ResolverTarget>> mTargetsDictPerUser;
private final List<ResolverTarget> mTargets; // Treat as immutable.
private final Collator mCollator;
private final IResolverRankerService mRanker;
private final ComponentName mRankerServiceName;
private final boolean mAnnotationsUsed;
- private final PackageManager mPm;
+ private final Map<UserHandle, PackageManager> mPmMap;
// TODO: it doesn't look like we should have to pass both targets and targetsDict, but it's
// not written in a way that makes it clear whether we can derive one from the other (at
// least in this constructor).
ResolverRankerServiceComparatorModel(
- Map<String, UsageStats> stats,
- Map<ComponentName, ResolverTarget> targetsDict,
+ Map<UserHandle, Map<String, UsageStats>> statsPerUser,
+ Map<UserHandle, Map<ComponentName, ResolverTarget>> targetsDictPerUser,
List<ResolverTarget> targets,
Collator collator,
IResolverRankerService ranker,
ComponentName rankerServiceName,
boolean annotationsUsed,
- PackageManager pm) {
- mStats = stats;
- mTargetsDict = targetsDict;
+ Map<UserHandle, PackageManager> pmMap) {
+ mStatsPerUser = statsPerUser;
+ mTargetsDictPerUser = targetsDictPerUser;
mTargets = targets;
mCollator = collator;
mRanker = ranker;
mRankerServiceName = rankerServiceName;
mAnnotationsUsed = annotationsUsed;
- mPm = pm;
+ mPmMap = pmMap;
}
@Override
@@ -521,25 +576,29 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
// a bug there, or do we have a way of knowing it will be non-null under certain
// conditions?
return (lhs, rhs) -> {
- if (mStats != null) {
- final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName(
- lhs.activityInfo.packageName, lhs.activityInfo.name));
- final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName(
- rhs.activityInfo.packageName, rhs.activityInfo.name));
-
- if (lhsTarget != null && rhsTarget != null) {
- final int selectProbabilityDiff = Float.compare(
- rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability());
-
- if (selectProbabilityDiff != 0) {
- return selectProbabilityDiff > 0 ? 1 : -1;
- }
+ final ResolverTarget lhsTarget =
+ getActivityResolverTargetForUser(lhs.activityInfo, lhs.userHandle);
+ final ResolverTarget rhsTarget =
+ getActivityResolverTargetForUser(rhs.activityInfo, rhs.userHandle);
+
+ if (lhsTarget != null && rhsTarget != null) {
+ final int selectProbabilityDiff = Float.compare(
+ rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability());
+
+ if (selectProbabilityDiff != 0) {
+ return selectProbabilityDiff > 0 ? 1 : -1;
}
}
- CharSequence sa = lhs.loadLabel(mPm);
+ CharSequence sa = null;
+ if (mPmMap.containsKey(lhs.userHandle)) {
+ sa = lhs.loadLabel(mPmMap.get(lhs.userHandle));
+ }
if (sa == null) sa = lhs.activityInfo.name;
- CharSequence sb = rhs.loadLabel(mPm);
+ CharSequence sb = null;
+ if (mPmMap.containsKey(rhs.userHandle)) {
+ sb = rhs.loadLabel(mPmMap.get(rhs.userHandle));
+ }
if (sb == null) sb = rhs.activityInfo.name;
return mCollator.compare(sa.toString().trim(), sb.toString().trim());
@@ -547,8 +606,9 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
}
@Override
- public float getScore(ComponentName name) {
- final ResolverTarget target = mTargetsDict.get(name);
+ public float getScore(TargetInfo targetInfo) {
+ ResolverTarget target = getResolverTargetForUserAndComponent(
+ targetInfo.getResolvedComponentName(), targetInfo.getResolveInfo().userHandle);
if (target != null) {
return target.getSelectProbability();
}
@@ -556,13 +616,17 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
}
@Override
- public void notifyOnTargetSelected(ComponentName componentName) {
+ public void notifyOnTargetSelected(TargetInfo targetInfo) {
if (mRanker != null) {
try {
- int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet())
- .indexOf(componentName);
+ int selectedPos = -1;
+ if (mTargetsDictPerUser.containsKey(targetInfo.getResolveInfo().userHandle)) {
+ selectedPos = new ArrayList<>(mTargetsDictPerUser
+ .get(targetInfo.getResolveInfo().userHandle).keySet())
+ .indexOf(targetInfo.getResolvedComponentName());
+ }
if (selectedPos >= 0 && mTargets != null) {
- final float selectedProbability = getScore(componentName);
+ final float selectedProbability = getScore(targetInfo);
int order = 0;
for (ResolverTarget target : mTargets) {
if (target.getSelectProbability() > selectedProbability) {
@@ -573,7 +637,8 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
mRanker.train(mTargets, selectedPos);
} else {
if (DEBUG) {
- Log.d(TAG, "Selected a unknown component: " + componentName);
+ Log.d(TAG, "Selected a unknown component: " + targetInfo
+ .getResolvedComponentName());
}
}
} catch (RemoteException e) {
@@ -597,5 +662,21 @@ public class ResolverRankerServiceResolverComparator extends AbstractResolverCom
metricsLogger.write(log);
}
}
+
+ @Nullable
+ private ResolverTarget getActivityResolverTargetForUser(
+ ActivityInfo activity, UserHandle user) {
+ return getResolverTargetForUserAndComponent(
+ new ComponentName(activity.packageName, activity.name), user);
+ }
+
+ @Nullable
+ private ResolverTarget getResolverTargetForUserAndComponent(
+ ComponentName targetComponentName, UserHandle user) {
+ if ((mStatsPerUser == null) || !mTargetsDictPerUser.containsKey(user)) {
+ return null;
+ }
+ return mTargetsDictPerUser.get(user).get(targetComponentName);
+ }
}
}