blob: 37a5a63265e8d1336edd665280f810ac9d0a5225 [file] [log] [blame]
George Hodulikc681ce42019-04-12 17:10:31 -07001/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.app;
18
19import static android.app.prediction.AppTargetEvent.ACTION_LAUNCH;
20
21import android.app.prediction.AppPredictor;
22import android.app.prediction.AppTarget;
23import android.app.prediction.AppTargetEvent;
24import android.app.prediction.AppTargetId;
25import android.content.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.content.pm.ResolveInfo;
George Hodulik1f5d9bf2019-04-29 14:55:48 -070029import android.os.Message;
George Hodulikc681ce42019-04-12 17:10:31 -070030import android.os.UserHandle;
Song Hu5aab2d32020-04-30 10:07:15 -070031import android.provider.DeviceConfig;
George Hodulik3f399f22019-04-26 16:17:54 -070032import android.util.Log;
George Hodulikc681ce42019-04-12 17:10:31 -070033
34import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
Song Hu5aab2d32020-04-30 10:07:15 -070035import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
George Hodulikc681ce42019-04-12 17:10:31 -070036
37import java.util.ArrayList;
38import java.util.HashMap;
39import java.util.List;
40import java.util.Map;
Song Hue2deffd2020-03-09 15:22:29 -070041import java.util.Map.Entry;
George Hodulikccb0cbc2019-05-02 12:23:44 -070042import java.util.concurrent.Executors;
Song Hue2deffd2020-03-09 15:22:29 -070043import java.util.stream.Collectors;
George Hodulikc681ce42019-04-12 17:10:31 -070044
45/**
George Hodulik3f399f22019-04-26 16:17:54 -070046 * Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be
47 * disabled by returning an empty sorted target list, {@link AppPredictionServiceResolverComparator}
48 * will fallback to using a {@link ResolverRankerServiceResolverComparator}.
George Hodulikc681ce42019-04-12 17:10:31 -070049 */
50class AppPredictionServiceResolverComparator extends AbstractResolverComparator {
51
52 private static final String TAG = "APSResolverComparator";
53
54 private final AppPredictor mAppPredictor;
55 private final Context mContext;
56 private final Map<ComponentName, Integer> mTargetRanks = new HashMap<>();
Song Hu6f422812020-04-26 21:51:31 -070057 private final Map<ComponentName, Integer> mTargetScores = new HashMap<>();
George Hodulikc681ce42019-04-12 17:10:31 -070058 private final UserHandle mUser;
George Hodulik3f399f22019-04-26 16:17:54 -070059 private final Intent mIntent;
60 private final String mReferrerPackage;
61 // If this is non-null (and this is not destroyed), it means APS is disabled and we should fall
62 // back to using the ResolverRankerService.
63 private ResolverRankerServiceResolverComparator mResolverRankerService;
George Hodulikc681ce42019-04-12 17:10:31 -070064
Song Hu5aab2d32020-04-30 10:07:15 -070065 private boolean mAppendDirectShareEnabled = DeviceConfig.getBoolean(
66 DeviceConfig.NAMESPACE_SYSTEMUI,
67 SystemUiDeviceConfigFlags.APPEND_DIRECT_SHARE_ENABLED,
68 true);
69
George Hodulikc681ce42019-04-12 17:10:31 -070070 AppPredictionServiceResolverComparator(
George Hodulik3f399f22019-04-26 16:17:54 -070071 Context context,
72 Intent intent,
73 String referrerPackage,
74 AppPredictor appPredictor,
75 UserHandle user) {
George Hodulikc681ce42019-04-12 17:10:31 -070076 super(context, intent);
77 mContext = context;
George Hodulik3f399f22019-04-26 16:17:54 -070078 mIntent = intent;
George Hodulikc681ce42019-04-12 17:10:31 -070079 mAppPredictor = appPredictor;
80 mUser = user;
George Hodulik3f399f22019-04-26 16:17:54 -070081 mReferrerPackage = referrerPackage;
George Hodulikc681ce42019-04-12 17:10:31 -070082 }
83
84 @Override
85 int compare(ResolveInfo lhs, ResolveInfo rhs) {
George Hodulik3f399f22019-04-26 16:17:54 -070086 if (mResolverRankerService != null) {
87 return mResolverRankerService.compare(lhs, rhs);
88 }
George Hodulikc681ce42019-04-12 17:10:31 -070089 Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName,
90 lhs.activityInfo.name));
91 Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName,
92 rhs.activityInfo.name));
93 if (lhsRank == null && rhsRank == null) {
94 return 0;
95 } else if (lhsRank == null) {
96 return -1;
97 } else if (rhsRank == null) {
98 return 1;
99 }
100 return lhsRank - rhsRank;
101 }
102
103 @Override
George Hodulik1f5d9bf2019-04-29 14:55:48 -0700104 void doCompute(List<ResolvedComponentInfo> targets) {
George Hodulik3f399f22019-04-26 16:17:54 -0700105 if (targets.isEmpty()) {
106 mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT);
107 return;
108 }
George Hodulikc681ce42019-04-12 17:10:31 -0700109 List<AppTarget> appTargets = new ArrayList<>();
110 for (ResolvedComponentInfo target : targets) {
George Hodulik9f572f42019-05-16 15:23:12 -0700111 appTargets.add(
112 new AppTarget.Builder(
113 new AppTargetId(target.name.flattenToString()),
114 target.name.getPackageName(),
115 mUser)
116 .setClassName(target.name.getClassName())
117 .build());
George Hodulikc681ce42019-04-12 17:10:31 -0700118 }
George Hodulikccb0cbc2019-05-02 12:23:44 -0700119 mAppPredictor.sortTargets(appTargets, Executors.newSingleThreadExecutor(),
George Hodulikc681ce42019-04-12 17:10:31 -0700120 sortedAppTargets -> {
George Hodulik3f399f22019-04-26 16:17:54 -0700121 if (sortedAppTargets.isEmpty()) {
Song Hu5aab2d32020-04-30 10:07:15 -0700122 Log.i(TAG, "AppPredictionService disabled. Using resolver.");
George Hodulik3f399f22019-04-26 16:17:54 -0700123 // APS for chooser is disabled. Fallback to resolver.
124 mResolverRankerService =
125 new ResolverRankerServiceResolverComparator(
126 mContext, mIntent, mReferrerPackage,
127 () -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT));
George Hodulikccb0cbc2019-05-02 12:23:44 -0700128 mResolverRankerService.compute(targets);
George Hodulik3f399f22019-04-26 16:17:54 -0700129 } else {
Song Hu5aab2d32020-04-30 10:07:15 -0700130 Log.i(TAG, "AppPredictionService response received");
George Hodulik3f399f22019-04-26 16:17:54 -0700131 Message msg =
George Hodulik1f5d9bf2019-04-29 14:55:48 -0700132 Message.obtain(mHandler, RANKER_SERVICE_RESULT, sortedAppTargets);
George Hodulik3f399f22019-04-26 16:17:54 -0700133 msg.sendToTarget();
134 }
135 }
136 );
George Hodulikc681ce42019-04-12 17:10:31 -0700137 }
138
139 @Override
George Hodulik1f5d9bf2019-04-29 14:55:48 -0700140 void handleResultMessage(Message msg) {
George Hodulik3f399f22019-04-26 16:17:54 -0700141 // Null value is okay if we have defaulted to the ResolverRankerService.
142 if (msg.what == RANKER_SERVICE_RESULT && msg.obj != null) {
George Hodulik1f5d9bf2019-04-29 14:55:48 -0700143 final List<AppTarget> sortedAppTargets = (List<AppTarget>) msg.obj;
Song Hu6f422812020-04-26 21:51:31 -0700144 if (checkAppTargetRankValid(sortedAppTargets)) {
145 sortedAppTargets.forEach(target -> mTargetScores.put(
146 new ComponentName(target.getPackageName(), target.getClassName()),
147 target.getRank()));
148 }
George Hodulik1f5d9bf2019-04-29 14:55:48 -0700149 for (int i = 0; i < sortedAppTargets.size(); i++) {
Song Hu5aab2d32020-04-30 10:07:15 -0700150 ComponentName componentName = new ComponentName(
151 sortedAppTargets.get(i).getPackageName(),
152 sortedAppTargets.get(i).getClassName());
153 mTargetRanks.put(componentName, i);
154 Log.i(TAG, "handleResultMessage, sortedAppTargets #" + i + ": " + componentName);
George Hodulik1f5d9bf2019-04-29 14:55:48 -0700155 }
George Hodulik3f399f22019-04-26 16:17:54 -0700156 } else if (msg.obj == null && mResolverRankerService == null) {
157 Log.e(TAG, "Unexpected null result");
George Hodulik1f5d9bf2019-04-29 14:55:48 -0700158 }
159 }
160
Song Hu6f422812020-04-26 21:51:31 -0700161 private boolean checkAppTargetRankValid(List<AppTarget> sortedAppTargets) {
162 for (AppTarget target : sortedAppTargets) {
163 if (target.getRank() != 0) {
164 return true;
165 }
166 }
167 return false;
168 }
169
George Hodulik1f5d9bf2019-04-29 14:55:48 -0700170 @Override
George Hodulikc681ce42019-04-12 17:10:31 -0700171 float getScore(ComponentName name) {
George Hodulik3f399f22019-04-26 16:17:54 -0700172 if (mResolverRankerService != null) {
173 return mResolverRankerService.getScore(name);
174 }
Song Hu5aab2d32020-04-30 10:07:15 -0700175 if (mAppendDirectShareEnabled && !mTargetScores.isEmpty()) {
Song Hu6f422812020-04-26 21:51:31 -0700176 return mTargetScores.get(name);
177 }
George Hodulikc681ce42019-04-12 17:10:31 -0700178 Integer rank = mTargetRanks.get(name);
179 if (rank == null) {
180 Log.w(TAG, "Score requested for unknown component.");
181 return 0f;
182 }
183 int consecutiveSumOfRanks = (mTargetRanks.size() - 1) * (mTargetRanks.size()) / 2;
184 return 1.0f - (((float) rank) / consecutiveSumOfRanks);
185 }
186
187 @Override
Song Hue2deffd2020-03-09 15:22:29 -0700188 List<ComponentName> getTopComponentNames(int topK) {
Song Hu36ba1b12020-04-14 15:36:17 -0700189 if (mResolverRankerService != null) {
190 return mResolverRankerService.getTopComponentNames(topK);
191 }
Song Hue2deffd2020-03-09 15:22:29 -0700192 return mTargetRanks.entrySet().stream()
193 .sorted(Entry.comparingByValue())
194 .limit(topK)
195 .map(Entry::getKey)
196 .collect(Collectors.toList());
197 }
198
199 @Override
George Hodulikc681ce42019-04-12 17:10:31 -0700200 void updateModel(ComponentName componentName) {
George Hodulik3f399f22019-04-26 16:17:54 -0700201 if (mResolverRankerService != null) {
202 mResolverRankerService.updateModel(componentName);
203 return;
204 }
George Hodulikc681ce42019-04-12 17:10:31 -0700205 mAppPredictor.notifyAppTargetEvent(
206 new AppTargetEvent.Builder(
207 new AppTarget.Builder(
208 new AppTargetId(componentName.toString()),
209 componentName.getPackageName(), mUser)
210 .setClassName(componentName.getClassName()).build(),
211 ACTION_LAUNCH).build());
212 }
George Hodulik3f399f22019-04-26 16:17:54 -0700213
214 @Override
215 void destroy() {
216 if (mResolverRankerService != null) {
217 mResolverRankerService.destroy();
218 mResolverRankerService = null;
219 }
220 }
George Hodulikc681ce42019-04-12 17:10:31 -0700221}