ResolverComparator changes to rank targets across profiles.
Adds methodology to rank targets across profiles, when ResolverRankerService (AOSP) is used for comparison. APSResolver and other files are touched only due to underlying interface changes. There is no potential logic change there. Bug: 240100378 Test: Manually verified by triggering sharesheet. Change-Id: I7d27868d5a1012373d29824fef33688c00e8aeff
This commit is contained in:
@@ -30,11 +30,16 @@ import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
|
||||
import com.android.internal.app.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}.
|
||||
@@ -48,8 +53,8 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
|
||||
private static final String TAG = "AbstractResolverComp";
|
||||
|
||||
protected AfterCompute 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;
|
||||
|
||||
@@ -98,14 +103,28 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
|
||||
}
|
||||
};
|
||||
|
||||
public AbstractResolverComparator(Context context, Intent intent) {
|
||||
// context here refers to the activity calling this comparator.
|
||||
// targetUserSpace refers to the userSpace in which the targets to be ranked lie.
|
||||
public AbstractResolverComparator(Context launchedFromContext, Intent intent,
|
||||
UserHandle targetUserSpace) {
|
||||
this(launchedFromContext, intent, Lists.newArrayList(targetUserSpace));
|
||||
}
|
||||
|
||||
// context here refers to the activity calling this comparator.
|
||||
// targetUserSpaceList refers to the userSpace(s) in which the targets to be ranked lie.
|
||||
public AbstractResolverComparator(Context launchedFromContext, Intent intent,
|
||||
List<UserHandle> targetUserSpaceList) {
|
||||
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 : targetUserSpaceList) {
|
||||
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.
|
||||
@@ -208,8 +227,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.
|
||||
@@ -226,7 +245,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.
|
||||
*/
|
||||
abstract float getScore(ComponentName name);
|
||||
abstract float getScore(TargetInfo targetInfo);
|
||||
|
||||
/** Handles result message sent to mHandler. */
|
||||
abstract void handleResultMessage(Message message);
|
||||
@@ -234,9 +253,11 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
|
||||
/**
|
||||
* Reports to UsageStats what was chosen.
|
||||
*/
|
||||
final void updateChooserCounts(String packageName, int userId, String action) {
|
||||
if (mUsm != null) {
|
||||
mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action);
|
||||
final void updateChooserCounts(String packageName, UserHandle user, String action) {
|
||||
if (mUsmMap.containsKey(user)) {
|
||||
mUsmMap.get(user)
|
||||
.reportChooserSelection(packageName, user.getIdentifier(), mContentType,
|
||||
mAnnotations, action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,9 +267,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.
|
||||
*/
|
||||
void updateModel(ComponentName componentName) {
|
||||
void updateModel(TargetInfo targetInfo) {
|
||||
}
|
||||
|
||||
/** Called before {@link #doCompute(List)}. Sets up 500ms timeout. */
|
||||
|
||||
@@ -31,6 +31,9 @@ import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
|
||||
import com.android.internal.app.chooser.TargetInfo;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -70,7 +73,7 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
|
||||
AppPredictor appPredictor,
|
||||
UserHandle user,
|
||||
ChooserActivityLogger chooserActivityLogger) {
|
||||
super(context, intent);
|
||||
super(context, intent, Lists.newArrayList(user));
|
||||
mContext = context;
|
||||
mIntent = intent;
|
||||
mAppPredictor = appPredictor;
|
||||
@@ -99,13 +102,13 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
|
||||
}
|
||||
|
||||
@Override
|
||||
float getScore(ComponentName name) {
|
||||
return mComparatorModel.getScore(name);
|
||||
float getScore(TargetInfo targetInfo) {
|
||||
return mComparatorModel.getScore(targetInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateModel(ComponentName componentName) {
|
||||
mComparatorModel.notifyOnTargetSelected(componentName);
|
||||
void updateModel(TargetInfo targetInfo) {
|
||||
mComparatorModel.notifyOnTargetSelected(targetInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,9 +161,12 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
|
||||
private void setupFallbackModel(List<ResolvedComponentInfo> targets) {
|
||||
mResolverRankerService =
|
||||
new ResolverRankerServiceResolverComparator(
|
||||
mContext, mIntent, mReferrerPackage,
|
||||
mContext,
|
||||
mIntent,
|
||||
mReferrerPackage,
|
||||
() -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT),
|
||||
getChooserActivityLogger());
|
||||
getChooserActivityLogger(),
|
||||
mUser);
|
||||
mComparatorModel = mModelBuilder.buildFallbackModel(mResolverRankerService);
|
||||
mResolverRankerService.compute(targets);
|
||||
}
|
||||
@@ -224,13 +230,13 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getScore(ComponentName componentName) {
|
||||
return comparator.getScore(componentName);
|
||||
public float getScore(TargetInfo targetInfo) {
|
||||
return comparator.getScore(targetInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyOnTargetSelected(ComponentName componentName) {
|
||||
comparator.updateModel(componentName);
|
||||
public void notifyOnTargetSelected(TargetInfo targetInfo) {
|
||||
comparator.updateModel(targetInfo);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -271,8 +277,8 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getScore(ComponentName name) {
|
||||
Integer rank = mTargetRanks.get(name);
|
||||
public float getScore(TargetInfo targetInfo) {
|
||||
Integer rank = mTargetRanks.get(targetInfo.getResolvedComponentName());
|
||||
if (rank == null) {
|
||||
Log.w(TAG, "Score requested for unknown component. Did you call compute yet?");
|
||||
return 0f;
|
||||
@@ -282,13 +288,14 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyOnTargetSelected(ComponentName componentName) {
|
||||
public void notifyOnTargetSelected(TargetInfo targetInfo) {
|
||||
mAppPredictor.notifyAppTargetEvent(
|
||||
new AppTargetEvent.Builder(
|
||||
new AppTarget.Builder(
|
||||
new AppTargetId(componentName.toString()),
|
||||
componentName.getPackageName(), mUser)
|
||||
.setClassName(componentName.getClassName()).build(),
|
||||
new AppTargetId(targetInfo.getResolvedComponentName().toString()),
|
||||
targetInfo.getResolvedComponentName().getPackageName(), mUser)
|
||||
.setClassName(targetInfo.getResolvedComponentName()
|
||||
.getClassName()).build(),
|
||||
ACTION_LAUNCH).build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2268,9 +2268,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);
|
||||
@@ -2395,7 +2397,10 @@ public class ChooserActivity extends ResolverActivity implements
|
||||
*/
|
||||
@Nullable
|
||||
private AppPredictor getAppPredictorForShareActivitiesIfEnabled(UserHandle userHandle) {
|
||||
return USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES ? createAppPredictor(userHandle) : null;
|
||||
// We cannot use APS service when clone profile is present as APS service cannot sort
|
||||
// cross profile targets as of now.
|
||||
return USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES && getCloneProfileUserHandle() == null
|
||||
? createAppPredictor(userHandle) : null;
|
||||
}
|
||||
|
||||
void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
|
||||
@@ -2548,8 +2553,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));
|
||||
}
|
||||
|
||||
UserHandle queryIntentsUser = getQueryIntentsUser(userHandle);
|
||||
|
||||
@@ -1617,6 +1617,14 @@ public class ResolverActivity extends Activity implements
|
||||
@VisibleForTesting
|
||||
protected ResolverListController createListController(UserHandle userHandle) {
|
||||
UserHandle queryIntentsUser = getQueryIntentsUser(userHandle);
|
||||
ResolverRankerServiceResolverComparator resolverComparator =
|
||||
new ResolverRankerServiceResolverComparator(
|
||||
this,
|
||||
getTargetIntent(),
|
||||
getReferrerPackageName(),
|
||||
null,
|
||||
null,
|
||||
getResolverRankerServiceUserHandleList(userHandle));
|
||||
return new ResolverListController(
|
||||
this,
|
||||
mPm,
|
||||
@@ -1624,6 +1632,7 @@ public class ResolverActivity extends Activity implements
|
||||
getReferrerPackageName(),
|
||||
mLaunchedFromUid,
|
||||
userHandle,
|
||||
resolverComparator,
|
||||
queryIntentsUser);
|
||||
}
|
||||
|
||||
@@ -2534,4 +2543,21 @@ public class ResolverActivity extends Activity implements
|
||||
UserHandle predictedHandle) {
|
||||
return resolveInfo.userHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link List} of {@link UserHandle} to pass on to the
|
||||
* {@link ResolverRankerServiceResolverComparator} as per the provided {@code userHandle}.
|
||||
*/
|
||||
protected final List<UserHandle> getResolverRankerServiceUserHandleList(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
package com.android.internal.app;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ResolveInfo;
|
||||
|
||||
import com.android.internal.app.chooser.TargetInfo;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A ranking model for resolver targets, providing ordering and (optionally) numerical scoring.
|
||||
@@ -45,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
|
||||
@@ -53,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);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import static android.content.Context.ACTIVITY_SERVICE;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.PermissionChecker;
|
||||
@@ -157,17 +156,17 @@ public class ResolverListAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Returns the app share score of the given {@code componentName}.
|
||||
*/
|
||||
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);
|
||||
public void updateModel(TargetInfo targetInfo) {
|
||||
mResolverListController.updateModel(targetInfo);
|
||||
}
|
||||
|
||||
public void updateChooserCounts(String packageName, String action) {
|
||||
public void updateChooserCounts(String packageName, String action, UserHandle userHandle) {
|
||||
mResolverListController.updateChooserCounts(
|
||||
packageName, getUserHandle().getIdentifier(), action);
|
||||
packageName, userHandle, action);
|
||||
}
|
||||
|
||||
List<ResolvedComponentInfo> getUnfilteredResolveList() {
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.chooser.DisplayResolveInfo;
|
||||
import com.android.internal.app.chooser.TargetInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -72,7 +73,13 @@ public class ResolverListController {
|
||||
UserHandle queryIntentsAsUser) {
|
||||
this(context, pm, targetIntent, referrerPackage, launchedFromUid, userHandle,
|
||||
new ResolverRankerServiceResolverComparator(
|
||||
context, targetIntent, referrerPackage, null, null), queryIntentsAsUser);
|
||||
context,
|
||||
targetIntent,
|
||||
referrerPackage,
|
||||
null,
|
||||
null,
|
||||
userHandle),
|
||||
queryIntentsAsUser);
|
||||
}
|
||||
|
||||
public ResolverListController(
|
||||
@@ -397,22 +404,22 @@ 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);
|
||||
public void updateModel(TargetInfo targetInfo) {
|
||||
mResolverComparator.updateModel(targetInfo);
|
||||
}
|
||||
|
||||
public void updateChooserCounts(String packageName, int userId, String action) {
|
||||
mResolverComparator.updateChooserCounts(packageName, userId, action);
|
||||
public void updateChooserCounts(String packageName, UserHandle user, String action) {
|
||||
mResolverComparator.updateChooserCounts(packageName, user, action);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
|
||||
package com.android.internal.app;
|
||||
|
||||
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;
|
||||
@@ -38,12 +40,16 @@ import android.service.resolver.ResolverTarget;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
|
||||
import com.android.internal.app.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;
|
||||
@@ -69,10 +75,10 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
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, LinkedHashMap<ComponentName, ResolverTarget>> mTargetsDictPerUser;
|
||||
private final String mReferrerPackage;
|
||||
private final Object mLock = new Object();
|
||||
private ArrayList<ResolverTarget> mTargets;
|
||||
@@ -85,17 +91,34 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
private CountDownLatch mConnectSignal;
|
||||
private ResolverRankerServiceComparatorModel mComparatorModel;
|
||||
|
||||
public ResolverRankerServiceResolverComparator(Context context, Intent intent,
|
||||
// context here refers to the activity calling this comparator.
|
||||
// targetUserSpace refers to the userSpace in which the targets to be ranked lie.
|
||||
public ResolverRankerServiceResolverComparator(Context launchedFromContext, Intent intent,
|
||||
String referrerPackage, AfterCompute afterCompute,
|
||||
ChooserActivityLogger chooserActivityLogger) {
|
||||
super(context, intent);
|
||||
mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
|
||||
mReferrerPackage = referrerPackage;
|
||||
mContext = context;
|
||||
ChooserActivityLogger chooserActivityLogger, UserHandle targetUserSpace) {
|
||||
this(launchedFromContext, intent, referrerPackage, afterCompute, chooserActivityLogger,
|
||||
Lists.newArrayList(targetUserSpace));
|
||||
}
|
||||
|
||||
// context here refers to the activity calling this comparator.
|
||||
// targetUserSpaceList refers to the userSpace(s) in which the targets to be ranked lie.
|
||||
public ResolverRankerServiceResolverComparator(Context launchedFromContext, Intent intent,
|
||||
String referrerPackage, AfterCompute afterCompute,
|
||||
ChooserActivityLogger chooserActivityLogger, List<UserHandle> targetUserSpaceList) {
|
||||
super(launchedFromContext, intent, targetUserSpaceList);
|
||||
mCollator = Collator.getInstance(launchedFromContext
|
||||
.getResources().getConfiguration().locale);
|
||||
mReferrerPackage = referrerPackage;
|
||||
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,57 +170,63 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
|
||||
for (ResolvedComponentInfo target : targets) {
|
||||
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 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);
|
||||
final LinkedHashMap<ComponentName, ResolverTarget> targetsDict = mTargetsDictPerUser
|
||||
.get(target.getResolveInfoAt(0).userHandle);
|
||||
final Map<String, UsageStats> stats = mStatsPerUser
|
||||
.get(target.getResolveInfoAt(0).userHandle);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,7 +238,11 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
+ " 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,
|
||||
@@ -232,15 +265,15 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +314,8 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
// 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) {
|
||||
@@ -294,7 +328,8 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
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
|
||||
@@ -305,9 +340,9 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
+ " 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 "
|
||||
@@ -385,7 +420,9 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
@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();
|
||||
@@ -465,14 +502,14 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -481,35 +518,36 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
* 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.
|
||||
private final Map<UserHandle, LinkedHashMap<ComponentName,
|
||||
ResolverTarget>> mTargetsDictPerUser; // Treat as immutable.
|
||||
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, LinkedHashMap<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
|
||||
@@ -518,25 +556,29 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
// 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));
|
||||
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 (lhsTarget != null && rhsTarget != null) {
|
||||
final int selectProbabilityDiff = Float.compare(
|
||||
rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability());
|
||||
|
||||
if (selectProbabilityDiff != 0) {
|
||||
return selectProbabilityDiff > 0 ? 1 : -1;
|
||||
}
|
||||
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());
|
||||
@@ -544,22 +586,28 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getScore(ComponentName name) {
|
||||
final ResolverTarget target = mTargetsDict.get(name);
|
||||
if (target != null) {
|
||||
return target.getSelectProbability();
|
||||
public float getScore(TargetInfo targetInfo) {
|
||||
if (mTargetsDictPerUser.containsKey(targetInfo.getResolveInfo().userHandle)
|
||||
&& mTargetsDictPerUser.get(targetInfo.getResolveInfo().userHandle)
|
||||
.get(targetInfo.getResolvedComponentName()) != null) {
|
||||
return mTargetsDictPerUser.get(targetInfo.getResolveInfo().userHandle)
|
||||
.get(targetInfo.getResolvedComponentName()).getSelectProbability();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@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) {
|
||||
@@ -570,7 +618,8 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
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) {
|
||||
@@ -594,5 +643,16 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
|
||||
metricsLogger.write(log);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ResolverTarget getActivityResolverTargetForUser(
|
||||
ActivityInfo activity, UserHandle user) {
|
||||
if ((mStatsPerUser == null) || !mTargetsDictPerUser.containsKey(user)) {
|
||||
return null;
|
||||
}
|
||||
return mTargetsDictPerUser
|
||||
.get(user)
|
||||
.get(new ComponentName(activity.packageName, activity.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.internal.app;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -27,6 +28,10 @@ import android.os.Message;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import com.android.internal.app.chooser.TargetInfo;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
@@ -96,7 +101,8 @@ public class AbstractResolverComparatorTest {
|
||||
Intent intent = new Intent();
|
||||
|
||||
AbstractResolverComparator testComparator =
|
||||
new AbstractResolverComparator(context, intent) {
|
||||
new AbstractResolverComparator(context, intent,
|
||||
Lists.newArrayList(context.getUser())) {
|
||||
|
||||
@Override
|
||||
int compare(ResolveInfo lhs, ResolveInfo rhs) {
|
||||
@@ -109,7 +115,7 @@ public class AbstractResolverComparatorTest {
|
||||
void doCompute(List<ResolverActivity.ResolvedComponentInfo> targets) {}
|
||||
|
||||
@Override
|
||||
float getScore(ComponentName name) {
|
||||
float getScore(TargetInfo targetInfo) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -545,13 +545,17 @@ public class ChooserActivityTest {
|
||||
return true;
|
||||
};
|
||||
ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
|
||||
DisplayResolveInfo testDri =
|
||||
activity.createTestDisplayResolveInfo(sendIntent, toChoose, "testLabel", "testInfo",
|
||||
sendIntent,/* resolveInfoPresentationGetter */ null);
|
||||
onView(withText(toChoose.activityInfo.name))
|
||||
.perform(click());
|
||||
waitForIdle();
|
||||
verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
|
||||
.updateChooserCounts(Mockito.anyString(), anyInt(), Mockito.anyString());
|
||||
.updateChooserCounts(Mockito.anyString(), any(UserHandle.class),
|
||||
Mockito.anyString());
|
||||
verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
|
||||
.updateModel(toChoose.activityInfo.getComponentName());
|
||||
.updateModel(testDri);
|
||||
assertThat(activity.getIsSelected(), is(true));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.internal.app;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ResolveInfo;
|
||||
|
||||
import com.android.internal.app.chooser.TargetInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
@@ -45,14 +47,15 @@ public class FakeResolverComparatorModel implements ResolverComparatorModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getScore(ComponentName name) {
|
||||
public float getScore(TargetInfo targetInfo) {
|
||||
return 0.0f; // Models are not required to provide numerical scores.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyOnTargetSelected(ComponentName componentName) {
|
||||
public void notifyOnTargetSelected(TargetInfo targetInfo) {
|
||||
System.out.println(
|
||||
"User selected " + componentName + " under model " + System.identityHashCode(this));
|
||||
"User selected " + targetInfo.getResolvedComponentName() + " under model "
|
||||
+ System.identityHashCode(this));
|
||||
}
|
||||
|
||||
private FakeResolverComparatorModel(Comparator<ResolveInfo> comparator) {
|
||||
|
||||
@@ -86,6 +86,7 @@ public class ResolverListControllerTest {
|
||||
config.locale = Locale.getDefault();
|
||||
List<ResolveInfo> services = new ArrayList<>();
|
||||
mUsm = new UsageStatsManager(mMockContext, mMockService);
|
||||
when(mMockContext.createContextAsUser(any(), anyInt())).thenReturn(mMockContext);
|
||||
when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
|
||||
when(mMockPackageManager.queryIntentServices(any(), anyInt())).thenReturn(services);
|
||||
when(mMockResources.getConfiguration()).thenReturn(config);
|
||||
@@ -126,7 +127,7 @@ public class ResolverListControllerTest {
|
||||
UserHandle.SYSTEM);
|
||||
mController.sort(new ArrayList<ResolvedComponentInfo>());
|
||||
long beforeReport = getCount(mUsm, packageName, action, annotation);
|
||||
mController.updateChooserCounts(packageName, UserHandle.USER_CURRENT, action);
|
||||
mController.updateChooserCounts(packageName, UserHandle.SYSTEM, action);
|
||||
long afterReport = getCount(mUsm, packageName, action, annotation);
|
||||
assertThat(afterReport, is(beforeReport + 1l));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user