Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 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 | |
| 17 | |
| 18 | package com.android.internal.app; |
| 19 | |
| 20 | import android.app.usage.UsageStats; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 21 | import android.content.ComponentName; |
| 22 | import android.content.Context; |
| 23 | import android.content.Intent; |
shafik | 6cbef19 | 2018-12-27 14:14:58 +0000 | [diff] [blame] | 24 | import android.content.ServiceConnection; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 25 | import android.content.pm.ApplicationInfo; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 26 | import android.content.pm.PackageManager; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 27 | import android.content.pm.PackageManager.NameNotFoundException; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 28 | import android.content.pm.ResolveInfo; |
Kang Li | 2c57189 | 2017-07-05 14:47:32 -0700 | [diff] [blame] | 29 | import android.metrics.LogMaker; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 30 | import android.os.IBinder; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 31 | import android.os.Message; |
| 32 | import android.os.RemoteException; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 33 | import android.os.UserHandle; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 34 | import android.service.resolver.IResolverRankerResult; |
shafik | 6cbef19 | 2018-12-27 14:14:58 +0000 | [diff] [blame] | 35 | import android.service.resolver.IResolverRankerService; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 36 | import android.service.resolver.ResolverRankerService; |
| 37 | import android.service.resolver.ResolverTarget; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 38 | import android.util.Log; |
shafik | 6cbef19 | 2018-12-27 14:14:58 +0000 | [diff] [blame] | 39 | |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 40 | import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; |
Kang Li | 2c57189 | 2017-07-05 14:47:32 -0700 | [diff] [blame] | 41 | import com.android.internal.logging.MetricsLogger; |
| 42 | import com.android.internal.logging.nano.MetricsProto.MetricsEvent; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 43 | |
| 44 | import java.text.Collator; |
| 45 | import java.util.ArrayList; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 46 | import java.util.LinkedHashMap; |
| 47 | import java.util.List; |
| 48 | import java.util.Map; |
shafik | 6cbef19 | 2018-12-27 14:14:58 +0000 | [diff] [blame] | 49 | import java.util.concurrent.CountDownLatch; |
| 50 | import java.util.concurrent.TimeUnit; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 51 | |
| 52 | /** |
George Hodulik | 30f0c6f | 2019-03-20 18:23:23 -0700 | [diff] [blame] | 53 | * Ranks and compares packages based on usage stats and uses the {@link ResolverRankerService}. |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 54 | */ |
George Hodulik | 30f0c6f | 2019-03-20 18:23:23 -0700 | [diff] [blame] | 55 | class ResolverRankerServiceResolverComparator extends AbstractResolverComparator { |
| 56 | private static final String TAG = "RRSResolverComparator"; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 57 | |
Adam Powell | 0641394 | 2015-06-11 10:08:03 -0700 | [diff] [blame] | 58 | private static final boolean DEBUG = false; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 59 | |
Adam Powell | 2388251 | 2016-01-29 10:21:00 -0800 | [diff] [blame] | 60 | // One week |
| 61 | private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 7; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 62 | |
| 63 | private static final long RECENCY_TIME_PERIOD = 1000 * 60 * 60 * 12; |
| 64 | |
Adam Powell | bba0030 | 2016-02-03 16:01:09 -0800 | [diff] [blame] | 65 | private static final float RECENCY_MULTIPLIER = 2.f; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 66 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 67 | // timeout for establishing connections with a ResolverRankerService. |
| 68 | private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200; |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 69 | |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 70 | private final Collator mCollator; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 71 | private final Map<String, UsageStats> mStats; |
| 72 | private final long mCurrentTime; |
| 73 | private final long mSinceTime; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 74 | private final LinkedHashMap<ComponentName, ResolverTarget> mTargetsDict = new LinkedHashMap<>(); |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 75 | private final String mReferrerPackage; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 76 | private final Object mLock = new Object(); |
| 77 | private ArrayList<ResolverTarget> mTargets; |
Kang Li | 53b4314 | 2016-11-14 14:38:25 -0800 | [diff] [blame] | 78 | private String mAction; |
Kang Li | 2c57189 | 2017-07-05 14:47:32 -0700 | [diff] [blame] | 79 | private ComponentName mResolvedRankerName; |
| 80 | private ComponentName mRankerServiceName; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 81 | private IResolverRankerService mRanker; |
| 82 | private ResolverRankerServiceConnection mConnection; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 83 | private Context mContext; |
| 84 | private CountDownLatch mConnectSignal; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 85 | |
George Hodulik | 30f0c6f | 2019-03-20 18:23:23 -0700 | [diff] [blame] | 86 | public ResolverRankerServiceResolverComparator(Context context, Intent intent, |
| 87 | String referrerPackage, AfterCompute afterCompute) { |
George Hodulik | 9356244 | 2019-03-21 17:35:23 -0700 | [diff] [blame] | 88 | super(context, intent); |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 89 | mCollator = Collator.getInstance(context.getResources().getConfiguration().locale); |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 90 | mReferrerPackage = referrerPackage; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 91 | mContext = context; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 92 | |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 93 | mCurrentTime = System.currentTimeMillis(); |
| 94 | mSinceTime = mCurrentTime - USAGE_STATS_PERIOD; |
| 95 | mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime); |
Kang Li | 53b4314 | 2016-11-14 14:38:25 -0800 | [diff] [blame] | 96 | mAction = intent.getAction(); |
Kang Li | 2c57189 | 2017-07-05 14:47:32 -0700 | [diff] [blame] | 97 | mRankerServiceName = new ComponentName(mContext, this.getClass()); |
George Hodulik | c681ce4 | 2019-04-12 17:10:31 -0700 | [diff] [blame] | 98 | setCallBack(afterCompute); |
Kang Li | 9fa2a2c | 2017-01-06 13:33:24 -0800 | [diff] [blame] | 99 | } |
| 100 | |
George Hodulik | 1f5d9bf | 2019-04-29 14:55:48 -0700 | [diff] [blame] | 101 | @Override |
| 102 | public void handleResultMessage(Message msg) { |
| 103 | if (msg.what != RANKER_SERVICE_RESULT) { |
| 104 | return; |
| 105 | } |
George Hodulik | 3f399f2 | 2019-04-26 16:17:54 -0700 | [diff] [blame] | 106 | if (msg.obj == null) { |
| 107 | Log.e(TAG, "Receiving null prediction results."); |
| 108 | return; |
| 109 | } |
George Hodulik | 1f5d9bf | 2019-04-29 14:55:48 -0700 | [diff] [blame] | 110 | final List<ResolverTarget> receivedTargets = (List<ResolverTarget>) msg.obj; |
| 111 | if (receivedTargets != null && mTargets != null |
| 112 | && receivedTargets.size() == mTargets.size()) { |
| 113 | final int size = mTargets.size(); |
| 114 | boolean isUpdated = false; |
| 115 | for (int i = 0; i < size; ++i) { |
| 116 | final float predictedProb = |
| 117 | receivedTargets.get(i).getSelectProbability(); |
| 118 | if (predictedProb != mTargets.get(i).getSelectProbability()) { |
| 119 | mTargets.get(i).setSelectProbability(predictedProb); |
| 120 | isUpdated = true; |
| 121 | } |
| 122 | } |
| 123 | if (isUpdated) { |
| 124 | mRankerServiceName = mResolvedRankerName; |
| 125 | } |
| 126 | } else { |
| 127 | Log.e(TAG, "Sizes of sent and received ResolverTargets diff."); |
| 128 | } |
| 129 | } |
| 130 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 131 | // compute features for each target according to usage stats of targets. |
George Hodulik | 30f0c6f | 2019-03-20 18:23:23 -0700 | [diff] [blame] | 132 | @Override |
George Hodulik | 1f5d9bf | 2019-04-29 14:55:48 -0700 | [diff] [blame] | 133 | public void doCompute(List<ResolvedComponentInfo> targets) { |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 134 | final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD; |
| 135 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 136 | float mostRecencyScore = 1.0f; |
| 137 | float mostTimeSpentScore = 1.0f; |
| 138 | float mostLaunchScore = 1.0f; |
| 139 | float mostChooserScore = 1.0f; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 140 | |
| 141 | for (ResolvedComponentInfo target : targets) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 142 | final ResolverTarget resolverTarget = new ResolverTarget(); |
| 143 | mTargetsDict.put(target.name, resolverTarget); |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 144 | final UsageStats pkStats = mStats.get(target.name.getPackageName()); |
| 145 | if (pkStats != null) { |
| 146 | // Only count recency for apps that weren't the caller |
| 147 | // since the caller is always the most recent. |
| 148 | // Persistent processes muck this up, so omit them too. |
| 149 | if (!target.name.getPackageName().equals(mReferrerPackage) |
| 150 | && !isPersistentProcess(target)) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 151 | final float recencyScore = |
| 152 | (float) Math.max(pkStats.getLastTimeUsed() - recentSinceTime, 0); |
| 153 | resolverTarget.setRecencyScore(recencyScore); |
| 154 | if (recencyScore > mostRecencyScore) { |
| 155 | mostRecencyScore = recencyScore; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 156 | } |
| 157 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 158 | final float timeSpentScore = (float) pkStats.getTotalTimeInForeground(); |
| 159 | resolverTarget.setTimeSpentScore(timeSpentScore); |
| 160 | if (timeSpentScore > mostTimeSpentScore) { |
| 161 | mostTimeSpentScore = timeSpentScore; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 162 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 163 | final float launchScore = (float) pkStats.mLaunchCount; |
| 164 | resolverTarget.setLaunchScore(launchScore); |
| 165 | if (launchScore > mostLaunchScore) { |
| 166 | mostLaunchScore = launchScore; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 167 | } |
Kang Li | 53b4314 | 2016-11-14 14:38:25 -0800 | [diff] [blame] | 168 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 169 | float chooserScore = 0.0f; |
Kang Li | 53b4314 | 2016-11-14 14:38:25 -0800 | [diff] [blame] | 170 | if (pkStats.mChooserCounts != null && mAction != null |
| 171 | && pkStats.mChooserCounts.get(mAction) != null) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 172 | chooserScore = (float) pkStats.mChooserCounts.get(mAction) |
| 173 | .getOrDefault(mContentType, 0); |
Kang Li | 9fa2a2c | 2017-01-06 13:33:24 -0800 | [diff] [blame] | 174 | if (mAnnotations != null) { |
| 175 | final int size = mAnnotations.length; |
| 176 | for (int i = 0; i < size; i++) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 177 | chooserScore += (float) pkStats.mChooserCounts.get(mAction) |
Kang Li | 9fa2a2c | 2017-01-06 13:33:24 -0800 | [diff] [blame] | 178 | .getOrDefault(mAnnotations[i], 0); |
| 179 | } |
| 180 | } |
Kang Li | 53b4314 | 2016-11-14 14:38:25 -0800 | [diff] [blame] | 181 | } |
| 182 | if (DEBUG) { |
| 183 | if (mAction == null) { |
| 184 | Log.d(TAG, "Action type is null"); |
| 185 | } else { |
| 186 | Log.d(TAG, "Chooser Count of " + mAction + ":" + |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 187 | target.name.getPackageName() + " is " + |
| 188 | Float.toString(chooserScore)); |
Kang Li | 53b4314 | 2016-11-14 14:38:25 -0800 | [diff] [blame] | 189 | } |
| 190 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 191 | resolverTarget.setChooserScore(chooserScore); |
| 192 | if (chooserScore > mostChooserScore) { |
| 193 | mostChooserScore = chooserScore; |
Kang Li | 53b4314 | 2016-11-14 14:38:25 -0800 | [diff] [blame] | 194 | } |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 195 | } |
| 196 | } |
| 197 | |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 198 | if (DEBUG) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 199 | Log.d(TAG, "compute - mostRecencyScore: " + mostRecencyScore |
| 200 | + " mostTimeSpentScore: " + mostTimeSpentScore |
| 201 | + " mostLaunchScore: " + mostLaunchScore |
| 202 | + " mostChooserScore: " + mostChooserScore); |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 203 | } |
| 204 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 205 | mTargets = new ArrayList<>(mTargetsDict.values()); |
| 206 | for (ResolverTarget target : mTargets) { |
| 207 | final float recency = target.getRecencyScore() / mostRecencyScore; |
| 208 | setFeatures(target, recency * recency * RECENCY_MULTIPLIER, |
| 209 | target.getLaunchScore() / mostLaunchScore, |
| 210 | target.getTimeSpentScore() / mostTimeSpentScore, |
| 211 | target.getChooserScore() / mostChooserScore); |
| 212 | addDefaultSelectProbability(target); |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 213 | if (DEBUG) { |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 214 | Log.d(TAG, "Scores: " + target); |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 215 | } |
| 216 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 217 | predictSelectProbabilities(mTargets); |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 218 | } |
| 219 | |
| 220 | @Override |
George Hodulik | 9356244 | 2019-03-21 17:35:23 -0700 | [diff] [blame] | 221 | public int compare(ResolveInfo lhs, ResolveInfo rhs) { |
Matt Pietal | df634cc | 2019-03-13 09:55:28 -0400 | [diff] [blame] | 222 | if (mStats != null) { |
| 223 | final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName( |
| 224 | lhs.activityInfo.packageName, lhs.activityInfo.name)); |
| 225 | final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName( |
| 226 | rhs.activityInfo.packageName, rhs.activityInfo.name)); |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 227 | |
Matt Pietal | df634cc | 2019-03-13 09:55:28 -0400 | [diff] [blame] | 228 | if (lhsTarget != null && rhsTarget != null) { |
| 229 | final int selectProbabilityDiff = Float.compare( |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 230 | rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability()); |
Kang Li | 53b4314 | 2016-11-14 14:38:25 -0800 | [diff] [blame] | 231 | |
Matt Pietal | df634cc | 2019-03-13 09:55:28 -0400 | [diff] [blame] | 232 | if (selectProbabilityDiff != 0) { |
| 233 | return selectProbabilityDiff > 0 ? 1 : -1; |
Adam Powell | 2388251 | 2016-01-29 10:21:00 -0800 | [diff] [blame] | 234 | } |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 235 | } |
| 236 | } |
| 237 | |
| 238 | CharSequence sa = lhs.loadLabel(mPm); |
| 239 | if (sa == null) sa = lhs.activityInfo.name; |
| 240 | CharSequence sb = rhs.loadLabel(mPm); |
| 241 | if (sb == null) sb = rhs.activityInfo.name; |
| 242 | |
| 243 | return mCollator.compare(sa.toString().trim(), sb.toString().trim()); |
| 244 | } |
| 245 | |
George Hodulik | 30f0c6f | 2019-03-20 18:23:23 -0700 | [diff] [blame] | 246 | @Override |
Adam Powell | a182e45 | 2015-07-06 16:57:56 -0700 | [diff] [blame] | 247 | public float getScore(ComponentName name) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 248 | final ResolverTarget target = mTargetsDict.get(name); |
Adam Powell | a182e45 | 2015-07-06 16:57:56 -0700 | [diff] [blame] | 249 | if (target != null) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 250 | return target.getSelectProbability(); |
Adam Powell | a182e45 | 2015-07-06 16:57:56 -0700 | [diff] [blame] | 251 | } |
| 252 | return 0; |
| 253 | } |
| 254 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 255 | // update ranking model when the connection to it is valid. |
George Hodulik | 30f0c6f | 2019-03-20 18:23:23 -0700 | [diff] [blame] | 256 | @Override |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 257 | public void updateModel(ComponentName componentName) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 258 | synchronized (mLock) { |
| 259 | if (mRanker != null) { |
| 260 | try { |
| 261 | int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet()) |
| 262 | .indexOf(componentName); |
Kang Li | 78c6efc | 2017-07-13 16:00:42 -0700 | [diff] [blame] | 263 | if (selectedPos >= 0 && mTargets != null) { |
| 264 | final float selectedProbability = getScore(componentName); |
| 265 | int order = 0; |
| 266 | for (ResolverTarget target : mTargets) { |
| 267 | if (target.getSelectProbability() > selectedProbability) { |
| 268 | order++; |
| 269 | } |
| 270 | } |
| 271 | logMetrics(order); |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 272 | mRanker.train(mTargets, selectedPos); |
| 273 | } else { |
| 274 | if (DEBUG) { |
| 275 | Log.d(TAG, "Selected a unknown component: " + componentName); |
| 276 | } |
| 277 | } |
| 278 | } catch (RemoteException e) { |
| 279 | Log.e(TAG, "Error in Train: " + e); |
| 280 | } |
| 281 | } else { |
| 282 | if (DEBUG) { |
| 283 | Log.d(TAG, "Ranker is null; skip updateModel."); |
| 284 | } |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 285 | } |
| 286 | } |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 287 | } |
| 288 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 289 | // unbind the service and clear unhandled messges. |
George Hodulik | 30f0c6f | 2019-03-20 18:23:23 -0700 | [diff] [blame] | 290 | @Override |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 291 | public void destroy() { |
George Hodulik | 1f5d9bf | 2019-04-29 14:55:48 -0700 | [diff] [blame] | 292 | mHandler.removeMessages(RANKER_SERVICE_RESULT); |
| 293 | mHandler.removeMessages(RANKER_RESULT_TIMEOUT); |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 294 | if (mConnection != null) { |
| 295 | mContext.unbindService(mConnection); |
| 296 | mConnection.destroy(); |
Kang Li | c88749f | 2017-04-05 16:13:12 +0000 | [diff] [blame] | 297 | } |
George Hodulik | c681ce4 | 2019-04-12 17:10:31 -0700 | [diff] [blame] | 298 | afterCompute(); |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 299 | if (DEBUG) { |
| 300 | Log.d(TAG, "Unbinded Resolver Ranker."); |
Kang Li | c88749f | 2017-04-05 16:13:12 +0000 | [diff] [blame] | 301 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 302 | } |
Kang Li | c88749f | 2017-04-05 16:13:12 +0000 | [diff] [blame] | 303 | |
Kang Li | 2c57189 | 2017-07-05 14:47:32 -0700 | [diff] [blame] | 304 | // records metrics for evaluation. |
| 305 | private void logMetrics(int selectedPos) { |
| 306 | if (mRankerServiceName != null) { |
| 307 | MetricsLogger metricsLogger = new MetricsLogger(); |
| 308 | LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED); |
| 309 | log.setComponentName(mRankerServiceName); |
| 310 | int isCategoryUsed = (mAnnotations == null) ? 0 : 1; |
| 311 | log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, isCategoryUsed); |
| 312 | log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos); |
| 313 | metricsLogger.write(log); |
| 314 | } |
| 315 | } |
| 316 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 317 | // connect to a ranking service. |
George Hodulik | ccb0cbc | 2019-05-02 12:23:44 -0700 | [diff] [blame] | 318 | private void initRanker(Context context) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 319 | synchronized (mLock) { |
| 320 | if (mConnection != null && mRanker != null) { |
| 321 | if (DEBUG) { |
| 322 | Log.d(TAG, "Ranker still exists; reusing the existing one."); |
| 323 | } |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 324 | return; |
| 325 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 326 | } |
| 327 | Intent intent = resolveRankerService(); |
| 328 | if (intent == null) { |
| 329 | return; |
| 330 | } |
| 331 | mConnectSignal = new CountDownLatch(1); |
| 332 | mConnection = new ResolverRankerServiceConnection(mConnectSignal); |
| 333 | context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM); |
| 334 | } |
| 335 | |
| 336 | // resolve the service for ranking. |
| 337 | private Intent resolveRankerService() { |
| 338 | Intent intent = new Intent(ResolverRankerService.SERVICE_INTERFACE); |
| 339 | final List<ResolveInfo> resolveInfos = mPm.queryIntentServices(intent, 0); |
| 340 | for (ResolveInfo resolveInfo : resolveInfos) { |
| 341 | if (resolveInfo == null || resolveInfo.serviceInfo == null |
| 342 | || resolveInfo.serviceInfo.applicationInfo == null) { |
| 343 | if (DEBUG) { |
| 344 | Log.d(TAG, "Failed to retrieve a ranker: " + resolveInfo); |
| 345 | } |
| 346 | continue; |
| 347 | } |
| 348 | ComponentName componentName = new ComponentName( |
| 349 | resolveInfo.serviceInfo.applicationInfo.packageName, |
| 350 | resolveInfo.serviceInfo.name); |
| 351 | try { |
| 352 | final String perm = mPm.getServiceInfo(componentName, 0).permission; |
| 353 | if (!ResolverRankerService.BIND_PERMISSION.equals(perm)) { |
| 354 | Log.w(TAG, "ResolverRankerService " + componentName + " does not require" |
| 355 | + " permission " + ResolverRankerService.BIND_PERMISSION |
George Hodulik | 30f0c6f | 2019-03-20 18:23:23 -0700 | [diff] [blame] | 356 | + " - this service will not be queried for " |
| 357 | + "ResolverRankerServiceResolverComparator. add android:permission=\"" |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 358 | + ResolverRankerService.BIND_PERMISSION + "\"" |
| 359 | + " to the <service> tag for " + componentName |
| 360 | + " in the manifest."); |
| 361 | continue; |
| 362 | } |
Kang Li | 61cf4d1 | 2017-04-13 09:17:07 -0700 | [diff] [blame] | 363 | if (PackageManager.PERMISSION_GRANTED != mPm.checkPermission( |
| 364 | ResolverRankerService.HOLD_PERMISSION, |
| 365 | resolveInfo.serviceInfo.packageName)) { |
| 366 | Log.w(TAG, "ResolverRankerService " + componentName + " does not hold" |
| 367 | + " permission " + ResolverRankerService.HOLD_PERMISSION |
George Hodulik | 30f0c6f | 2019-03-20 18:23:23 -0700 | [diff] [blame] | 368 | + " - this service will not be queried for " |
| 369 | + "ResolverRankerServiceResolverComparator."); |
Kang Li | 61cf4d1 | 2017-04-13 09:17:07 -0700 | [diff] [blame] | 370 | continue; |
| 371 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 372 | } catch (NameNotFoundException e) { |
| 373 | Log.e(TAG, "Could not look up service " + componentName |
| 374 | + "; component name not found"); |
| 375 | continue; |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 376 | } |
| 377 | if (DEBUG) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 378 | Log.d(TAG, "Succeeded to retrieve a ranker: " + componentName); |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 379 | } |
Kang Li | 2c57189 | 2017-07-05 14:47:32 -0700 | [diff] [blame] | 380 | mResolvedRankerName = componentName; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 381 | intent.setComponent(componentName); |
| 382 | return intent; |
| 383 | } |
| 384 | return null; |
| 385 | } |
| 386 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 387 | private class ResolverRankerServiceConnection implements ServiceConnection { |
| 388 | private final CountDownLatch mConnectSignal; |
| 389 | |
| 390 | public ResolverRankerServiceConnection(CountDownLatch connectSignal) { |
| 391 | mConnectSignal = connectSignal; |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 392 | } |
| 393 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 394 | public final IResolverRankerResult resolverRankerResult = |
| 395 | new IResolverRankerResult.Stub() { |
| 396 | @Override |
| 397 | public void sendResult(List<ResolverTarget> targets) throws RemoteException { |
| 398 | if (DEBUG) { |
| 399 | Log.d(TAG, "Sending Result back to Resolver: " + targets); |
| 400 | } |
| 401 | synchronized (mLock) { |
| 402 | final Message msg = Message.obtain(); |
George Hodulik | 1f5d9bf | 2019-04-29 14:55:48 -0700 | [diff] [blame] | 403 | msg.what = RANKER_SERVICE_RESULT; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 404 | msg.obj = targets; |
| 405 | mHandler.sendMessage(msg); |
| 406 | } |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 407 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 408 | }; |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 409 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 410 | @Override |
| 411 | public void onServiceConnected(ComponentName name, IBinder service) { |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 412 | if (DEBUG) { |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 413 | Log.d(TAG, "onServiceConnected: " + name); |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 414 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 415 | synchronized (mLock) { |
| 416 | mRanker = IResolverRankerService.Stub.asInterface(service); |
| 417 | mConnectSignal.countDown(); |
| 418 | } |
Kang Li | a2c7774d | 2016-12-20 09:39:34 -0800 | [diff] [blame] | 419 | } |
Kang Li | 9988f36 | 2017-01-18 13:24:06 -0800 | [diff] [blame] | 420 | |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 421 | @Override |
| 422 | public void onServiceDisconnected(ComponentName name) { |
| 423 | if (DEBUG) { |
| 424 | Log.d(TAG, "onServiceDisconnected: " + name); |
| 425 | } |
| 426 | synchronized (mLock) { |
| 427 | destroy(); |
Kang Li | 9988f36 | 2017-01-18 13:24:06 -0800 | [diff] [blame] | 428 | } |
| 429 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 430 | |
| 431 | public void destroy() { |
| 432 | synchronized (mLock) { |
| 433 | mRanker = null; |
| 434 | } |
| 435 | } |
| 436 | } |
| 437 | |
George Hodulik | 1f5d9bf | 2019-04-29 14:55:48 -0700 | [diff] [blame] | 438 | @Override |
| 439 | void beforeCompute() { |
| 440 | super.beforeCompute(); |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 441 | mTargetsDict.clear(); |
| 442 | mTargets = null; |
Kang Li | 2c57189 | 2017-07-05 14:47:32 -0700 | [diff] [blame] | 443 | mRankerServiceName = new ComponentName(mContext, this.getClass()); |
| 444 | mResolvedRankerName = null; |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 445 | initRanker(mContext); |
| 446 | } |
| 447 | |
| 448 | // predict select probabilities if ranking service is valid. |
| 449 | private void predictSelectProbabilities(List<ResolverTarget> targets) { |
| 450 | if (mConnection == null) { |
| 451 | if (DEBUG) { |
| 452 | Log.d(TAG, "Has not found valid ResolverRankerService; Skip Prediction"); |
| 453 | } |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 454 | } else { |
| 455 | try { |
| 456 | mConnectSignal.await(CONNECTION_COST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); |
| 457 | synchronized (mLock) { |
| 458 | if (mRanker != null) { |
| 459 | mRanker.predict(targets, mConnection.resolverRankerResult); |
| 460 | return; |
| 461 | } else { |
| 462 | if (DEBUG) { |
| 463 | Log.d(TAG, "Ranker has not been initialized; skip predict."); |
| 464 | } |
| 465 | } |
| 466 | } |
| 467 | } catch (InterruptedException e) { |
| 468 | Log.e(TAG, "Error in Wait for Service Connection."); |
| 469 | } catch (RemoteException e) { |
| 470 | Log.e(TAG, "Error in Predict: " + e); |
| 471 | } |
| 472 | } |
George Hodulik | c681ce4 | 2019-04-12 17:10:31 -0700 | [diff] [blame] | 473 | afterCompute(); |
Kang Li | bbced1c | 2017-04-05 12:30:55 -0700 | [diff] [blame] | 474 | } |
| 475 | |
| 476 | // adds select prob as the default values, according to a pre-trained Logistic Regression model. |
| 477 | private void addDefaultSelectProbability(ResolverTarget target) { |
| 478 | float sum = 2.5543f * target.getLaunchScore() + 2.8412f * target.getTimeSpentScore() + |
| 479 | 0.269f * target.getRecencyScore() + 4.2222f * target.getChooserScore(); |
| 480 | target.setSelectProbability((float) (1.0 / (1.0 + Math.exp(1.6568f - sum)))); |
| 481 | } |
| 482 | |
| 483 | // sets features for each target |
| 484 | private void setFeatures(ResolverTarget target, float recencyScore, float launchScore, |
| 485 | float timeSpentScore, float chooserScore) { |
| 486 | target.setRecencyScore(recencyScore); |
| 487 | target.setLaunchScore(launchScore); |
| 488 | target.setTimeSpentScore(timeSpentScore); |
| 489 | target.setChooserScore(chooserScore); |
| 490 | } |
| 491 | |
| 492 | static boolean isPersistentProcess(ResolvedComponentInfo rci) { |
| 493 | if (rci != null && rci.getCount() > 0) { |
| 494 | return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags & |
| 495 | ApplicationInfo.FLAG_PERSISTENT) != 0; |
| 496 | } |
| 497 | return false; |
Adam Powell | d25267c | 2015-06-05 18:02:21 -0700 | [diff] [blame] | 498 | } |
| 499 | } |