diff options
| -rw-r--r-- | Android.mk | 3 | ||||
| -rw-r--r-- | api/system-current.txt | 31 | ||||
| -rw-r--r-- | core/java/android/service/resolver/IResolverRankerResult.aidl | 27 | ||||
| -rw-r--r-- | core/java/android/service/resolver/IResolverRankerService.aidl | 29 | ||||
| -rw-r--r-- | core/java/android/service/resolver/ResolverRankerService.java | 187 | ||||
| -rw-r--r-- | core/java/android/service/resolver/ResolverTarget.aidl | 22 | ||||
| -rw-r--r-- | core/java/android/service/resolver/ResolverTarget.java | 216 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/LRResolverRankerService.java | 199 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/ResolverActivity.java | 3 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/ResolverComparator.java | 516 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/ResolverListController.java | 44 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 17 |
12 files changed, 197 insertions, 1097 deletions
diff --git a/Android.mk b/Android.mk index eea465f5e951..634272b1d7ea 100644 --- a/Android.mk +++ b/Android.mk @@ -320,8 +320,6 @@ LOCAL_SRC_FILES += \ core/java/android/service/wallpaper/IWallpaperService.aidl \ core/java/android/service/chooser/IChooserTargetService.aidl \ core/java/android/service/chooser/IChooserTargetResult.aidl \ - core/java/android/service/resolver/IResolverRankerService.aidl \ - core/java/android/service/resolver/IResolverRankerResult.aidl \ core/java/android/text/ITextClassificationService.aidl \ core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\ core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\ @@ -731,7 +729,6 @@ aidl_files := \ frameworks/base/core/java/android/service/notification/SnoozeCriterion.aidl \ frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \ frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \ - frameworks/base/core/java/android/service/resolver/ResolverTarget.aidl \ frameworks/base/core/java/android/speech/tts/Voice.aidl \ frameworks/base/core/java/android/app/usage/CacheQuotaHint.aidl \ frameworks/base/core/java/android/app/usage/ExternalStorageStats.aidl \ diff --git a/api/system-current.txt b/api/system-current.txt index db2ac192c428..73cf77634355 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -53,7 +53,6 @@ package android { field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; - field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"; field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; @@ -40460,36 +40459,6 @@ package android.service.quicksettings { } -package android.service.resolver { - - public abstract class ResolverRankerService extends android.app.Service { - ctor public ResolverRankerService(); - method public android.os.IBinder onBind(android.content.Intent); - method public void onPredictSharingProbabilities(java.util.List<android.service.resolver.ResolverTarget>); - method public void onTrainRankingModel(java.util.List<android.service.resolver.ResolverTarget>, int); - field public static final java.lang.String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; - field public static final java.lang.String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService"; - } - - public final class ResolverTarget implements android.os.Parcelable { - ctor public ResolverTarget(); - method public int describeContents(); - method public float getChooserScore(); - method public float getLaunchScore(); - method public float getRecencyScore(); - method public float getSelectProbability(); - method public float getTimeSpentScore(); - method public void setChooserScore(float); - method public void setLaunchScore(float); - method public void setRecencyScore(float); - method public void setSelectProbability(float); - method public void setTimeSpentScore(float); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.resolver.ResolverTarget> CREATOR; - } - -} - package android.service.restrictions { public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver { diff --git a/core/java/android/service/resolver/IResolverRankerResult.aidl b/core/java/android/service/resolver/IResolverRankerResult.aidl deleted file mode 100644 index bda315420b7b..000000000000 --- a/core/java/android/service/resolver/IResolverRankerResult.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.service.resolver; - -import android.service.resolver.ResolverTarget; - -/** - * @hide - */ -oneway interface IResolverRankerResult -{ - void sendResult(in List<ResolverTarget> results); -}
\ No newline at end of file diff --git a/core/java/android/service/resolver/IResolverRankerService.aidl b/core/java/android/service/resolver/IResolverRankerService.aidl deleted file mode 100644 index f0d747d974a7..000000000000 --- a/core/java/android/service/resolver/IResolverRankerService.aidl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.service.resolver; - -import android.service.resolver.IResolverRankerResult; -import android.service.resolver.ResolverTarget; - -/** - * @hide - */ -oneway interface IResolverRankerService -{ - void predict(in List<ResolverTarget> targets, IResolverRankerResult result); - void train(in List<ResolverTarget> targets, int selectedPosition); -}
\ No newline at end of file diff --git a/core/java/android/service/resolver/ResolverRankerService.java b/core/java/android/service/resolver/ResolverRankerService.java deleted file mode 100644 index 05067479bf45..000000000000 --- a/core/java/android/service/resolver/ResolverRankerService.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.service.resolver; - -import android.annotation.SdkConstant; -import android.annotation.SystemApi; -import android.app.Service; -import android.content.ComponentName; -import android.content.Intent; -import android.os.IBinder; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.RemoteException; -import android.service.resolver.ResolverTarget; -import android.util.Log; - -import java.util.List; -import java.util.Map; - -/** - * A service to rank apps according to usage stats of apps, when the system is resolving targets for - * an Intent. - * - * <p>To extend this class, you must declare the service in your manifest file with the - * {@link android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE} permission, and include an - * intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> - * <pre> - * <service android:name=".MyResolverRankerService" - * android:exported="true" - * android:priority="100" - * android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE"> - * <intent-filter> - * <action android:name="android.service.resolver.ResolverRankerService" /> - * </intent-filter> - * </service> - * </pre> - * @hide - */ -@SystemApi -public abstract class ResolverRankerService extends Service { - - private static final String TAG = "ResolverRankerService"; - - private static final boolean DEBUG = false; - - /** - * The Intent action that a service must respond to. Add it to the intent filter of the service - * in its manifest. - */ - @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) - public static final String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService"; - - /** - * The permission that a service must require to ensure that only Android system can bind to it. - * If this permission is not enforced in the AndroidManifest of the service, the system will - * skip that service. - */ - public static final String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; - - private ResolverRankerServiceWrapper mWrapper = null; - - /** - * Called by the system to retrieve a list of probabilities to rank apps/options. To implement - * it, set selectProbability of each input {@link ResolverTarget}. The higher the - * selectProbability is, the more likely the {@link ResolverTarget} will be selected by the - * user. Override this function to provide prediction results. - * - * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked. - * - * @throws Exception when the prediction task fails. - */ - public void onPredictSharingProbabilities(final List<ResolverTarget> targets) {} - - /** - * Called by the system to train/update a ranking service, after the user makes a selection from - * the ranked list of apps. Override this function to enable model updates. - * - * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked. - * @param selectedPosition the position of the selected app in the list. - * - * @throws Exception when the training task fails. - */ - public void onTrainRankingModel( - final List<ResolverTarget> targets, final int selectedPosition) {} - - private static final String HANDLER_THREAD_NAME = "RESOLVER_RANKER_SERVICE"; - private volatile Handler mHandler; - private HandlerThread mHandlerThread; - - @Override - public IBinder onBind(Intent intent) { - if (DEBUG) Log.d(TAG, "onBind " + intent); - if (!SERVICE_INTERFACE.equals(intent.getAction())) { - if (DEBUG) Log.d(TAG, "bad intent action " + intent.getAction() + "; returning null"); - return null; - } - if (mHandlerThread == null) { - mHandlerThread = new HandlerThread(HANDLER_THREAD_NAME); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - } - if (mWrapper == null) { - mWrapper = new ResolverRankerServiceWrapper(); - } - return mWrapper; - } - - @Override - public void onDestroy() { - mHandler = null; - if (mHandlerThread != null) { - mHandlerThread.quitSafely(); - } - super.onDestroy(); - } - - private static void sendResult(List<ResolverTarget> targets, IResolverRankerResult result) { - try { - result.sendResult(targets); - } catch (Exception e) { - Log.e(TAG, "failed to send results: " + e); - } - } - - private class ResolverRankerServiceWrapper extends IResolverRankerService.Stub { - - @Override - public void predict(final List<ResolverTarget> targets, final IResolverRankerResult result) - throws RemoteException { - Runnable predictRunnable = new Runnable() { - @Override - public void run() { - try { - if (DEBUG) { - Log.d(TAG, "predict calls onPredictSharingProbabilities."); - } - onPredictSharingProbabilities(targets); - sendResult(targets, result); - } catch (Exception e) { - Log.e(TAG, "onPredictSharingProbabilities failed; send null results: " + e); - sendResult(null, result); - } - } - }; - final Handler h = mHandler; - if (h != null) { - h.post(predictRunnable); - } - } - - @Override - public void train(final List<ResolverTarget> targets, final int selectedPosition) - throws RemoteException { - Runnable trainRunnable = new Runnable() { - @Override - public void run() { - try { - if (DEBUG) { - Log.d(TAG, "train calls onTranRankingModel"); - } - onTrainRankingModel(targets, selectedPosition); - } catch (Exception e) { - Log.e(TAG, "onTrainRankingModel failed; skip train: " + e); - } - } - }; - final Handler h = mHandler; - if (h != null) { - h.post(trainRunnable); - } - } - } -} diff --git a/core/java/android/service/resolver/ResolverTarget.aidl b/core/java/android/service/resolver/ResolverTarget.aidl deleted file mode 100644 index 6cab2d4df908..000000000000 --- a/core/java/android/service/resolver/ResolverTarget.aidl +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.service.resolver; - -/** - * @hide - */ -parcelable ResolverTarget; diff --git a/core/java/android/service/resolver/ResolverTarget.java b/core/java/android/service/resolver/ResolverTarget.java deleted file mode 100644 index fb3e2d738469..000000000000 --- a/core/java/android/service/resolver/ResolverTarget.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.service.resolver; - -import android.annotation.SystemApi; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.ArrayMap; - -import java.util.Map; - -/** - * A ResolverTarget contains features by which an app or option will be ranked, in - * {@link ResolverRankerService}. - * @hide - */ -@SystemApi -public final class ResolverTarget implements Parcelable { - private static final String TAG = "ResolverTarget"; - - /** - * a float score for recency of last use. - */ - private float mRecencyScore; - - /** - * a float score for total time spent. - */ - private float mTimeSpentScore; - - /** - * a float score for number of launches. - */ - private float mLaunchScore; - - /** - * a float score for number of selected. - */ - private float mChooserScore; - - /** - * a float score for the probability to be selected. - */ - private float mSelectProbability; - - // constructor for the class. - public ResolverTarget() {} - - ResolverTarget(Parcel in) { - mRecencyScore = in.readFloat(); - mTimeSpentScore = in.readFloat(); - mLaunchScore = in.readFloat(); - mChooserScore = in.readFloat(); - mSelectProbability = in.readFloat(); - } - - /** - * Gets the score for how recently the target was used in the foreground. - * - * @return a float score whose range is [0, 1]. The higher the score is, the more recently the - * target was used. - */ - public float getRecencyScore() { - return mRecencyScore; - } - - /** - * Sets the score for how recently the target was used in the foreground. - * - * @param recencyScore a float score whose range is [0, 1]. The higher the score is, the more - * recently the target was used. - */ - public void setRecencyScore(float recencyScore) { - this.mRecencyScore = recencyScore; - } - - /** - * Gets the score for how long the target has been used in the foreground. - * - * @return a float score whose range is [0, 1]. The higher the score is, the longer the target - * has been used for. - */ - public float getTimeSpentScore() { - return mTimeSpentScore; - } - - /** - * Sets the score for how long the target has been used in the foreground. - * - * @param timeSpentScore a float score whose range is [0, 1]. The higher the score is, the - * longer the target has been used for. - */ - public void setTimeSpentScore(float timeSpentScore) { - this.mTimeSpentScore = timeSpentScore; - } - - /** - * Gets the score for how many times the target has been launched to the foreground. - * - * @return a float score whose range is [0, 1]. The higher the score is, the more times the - * target has been launched. - */ - public float getLaunchScore() { - return mLaunchScore; - } - - /** - * Sets the score for how many times the target has been launched to the foreground. - * - * @param launchScore a float score whose range is [0, 1]. The higher the score is, the more - * times the target has been launched. - */ - public void setLaunchScore(float launchScore) { - this.mLaunchScore = launchScore; - } - - /** - * Gets the score for how many times the target has been selected by the user to share the same - * types of content. - * - * @return a float score whose range is [0, 1]. The higher the score is, the - * more times the target has been selected by the user to share the same types of content for. - */ - public float getChooserScore() { - return mChooserScore; - } - - /** - * Sets the score for how many times the target has been selected by the user to share the same - * types of content. - * - * @param chooserScore a float score whose range is [0, 1]. The higher the score is, the more - * times the target has been selected by the user to share the same types - * of content for. - */ - public void setChooserScore(float chooserScore) { - this.mChooserScore = chooserScore; - } - - /** - * Gets the probability of how likely this target will be selected by the user. - * - * @return a float score whose range is [0, 1]. The higher the score is, the more likely the - * user is going to select this target. - */ - public float getSelectProbability() { - return mSelectProbability; - } - - /** - * Sets the probability for how like this target will be selected by the user. - * - * @param selectProbability a float score whose range is [0, 1]. The higher the score is, the - * more likely tht user is going to select this target. - */ - public void setSelectProbability(float selectProbability) { - this.mSelectProbability = selectProbability; - } - - // serialize the class to a string. - @Override - public String toString() { - return "ResolverTarget{" - + mRecencyScore + ", " - + mTimeSpentScore + ", " - + mLaunchScore + ", " - + mChooserScore + ", " - + mSelectProbability + "}"; - } - - // describes the kinds of special objects contained in this Parcelable instance's marshaled - // representation. - @Override - public int describeContents() { - return 0; - } - - // flattens this object in to a Parcel. - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeFloat(mRecencyScore); - dest.writeFloat(mTimeSpentScore); - dest.writeFloat(mLaunchScore); - dest.writeFloat(mChooserScore); - dest.writeFloat(mSelectProbability); - } - - // creator definition for the class. - public static final Creator<ResolverTarget> CREATOR - = new Creator<ResolverTarget>() { - @Override - public ResolverTarget createFromParcel(Parcel source) { - return new ResolverTarget(source); - } - - @Override - public ResolverTarget[] newArray(int size) { - return new ResolverTarget[size]; - } - }; -} diff --git a/core/java/com/android/internal/app/LRResolverRankerService.java b/core/java/com/android/internal/app/LRResolverRankerService.java deleted file mode 100644 index 1cad7c770b7c..000000000000 --- a/core/java/com/android/internal/app/LRResolverRankerService.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.app; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Environment; -import android.os.IBinder; -import android.os.storage.StorageManager; -import android.service.resolver.ResolverRankerService; -import android.service.resolver.ResolverTarget; -import android.util.ArrayMap; -import android.util.Log; - -import java.io.File; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * A Logistic Regression based {@link android.service.resolver.ResolverRankerService}, to be used - * in {@link ResolverComparator}. - */ -public final class LRResolverRankerService extends ResolverRankerService { - private static final String TAG = "LRResolverRankerService"; - - private static final boolean DEBUG = false; - - private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params"; - private static final String BIAS_PREF_KEY = "bias"; - private static final String VERSION_PREF_KEY = "version"; - - private static final String LAUNCH_SCORE = "launch"; - private static final String TIME_SPENT_SCORE = "timeSpent"; - private static final String RECENCY_SCORE = "recency"; - private static final String CHOOSER_SCORE = "chooser"; - - // parameters for a pre-trained model, to initialize the app ranker. When updating the - // pre-trained model, please update these params, as well as initModel(). - private static final int CURRENT_VERSION = 1; - private static final float LEARNING_RATE = 0.0001f; - private static final float REGULARIZER_PARAM = 0.0001f; - - private SharedPreferences mParamSharedPref; - private ArrayMap<String, Float> mFeatureWeights; - private float mBias; - - @Override - public IBinder onBind(Intent intent) { - initModel(); - return super.onBind(intent); - } - - @Override - public void onPredictSharingProbabilities(List<ResolverTarget> targets) { - final int size = targets.size(); - for (int i = 0; i < size; ++i) { - ResolverTarget target = targets.get(i); - ArrayMap<String, Float> features = getFeatures(target); - target.setSelectProbability(predict(features)); - } - } - - @Override - public void onTrainRankingModel(List<ResolverTarget> targets, int selectedPosition) { - final int size = targets.size(); - if (selectedPosition < 0 || selectedPosition >= size) { - if (DEBUG) { - Log.d(TAG, "Invalid Position of Selected App " + selectedPosition); - } - return; - } - final ArrayMap<String, Float> positive = getFeatures(targets.get(selectedPosition)); - final float positiveProbability = targets.get(selectedPosition).getSelectProbability(); - final int targetSize = targets.size(); - for (int i = 0; i < targetSize; ++i) { - if (i == selectedPosition) { - continue; - } - final ArrayMap<String, Float> negative = getFeatures(targets.get(i)); - final float negativeProbability = targets.get(i).getSelectProbability(); - if (negativeProbability > positiveProbability) { - update(negative, negativeProbability, false); - update(positive, positiveProbability, true); - } - } - commitUpdate(); - } - - private void initModel() { - mParamSharedPref = getParamSharedPref(); - mFeatureWeights = new ArrayMap<>(4); - if (mParamSharedPref == null || - mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) { - // Initializing the app ranker to a pre-trained model. When updating the pre-trained - // model, please increment CURRENT_VERSION, and update LEARNING_RATE and - // REGULARIZER_PARAM. - mBias = -1.6568f; - mFeatureWeights.put(LAUNCH_SCORE, 2.5543f); - mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f); - mFeatureWeights.put(RECENCY_SCORE, 0.269f); - mFeatureWeights.put(CHOOSER_SCORE, 4.2222f); - } else { - mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f); - mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f)); - mFeatureWeights.put( - TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f)); - mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f)); - mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f)); - } - } - - private ArrayMap<String, Float> getFeatures(ResolverTarget target) { - ArrayMap<String, Float> features = new ArrayMap<>(4); - features.put(RECENCY_SCORE, target.getRecencyScore()); - features.put(TIME_SPENT_SCORE, target.getTimeSpentScore()); - features.put(LAUNCH_SCORE, target.getLaunchScore()); - features.put(CHOOSER_SCORE, target.getChooserScore()); - return features; - } - - private float predict(ArrayMap<String, Float> target) { - if (target == null) { - return 0.0f; - } - final int featureSize = target.size(); - float sum = 0.0f; - for (int i = 0; i < featureSize; i++) { - String featureName = target.keyAt(i); - float weight = mFeatureWeights.getOrDefault(featureName, 0.0f); - sum += weight * target.valueAt(i); - } - return (float) (1.0 / (1.0 + Math.exp(-mBias - sum))); - } - - private void update(ArrayMap<String, Float> target, float predict, boolean isSelected) { - if (target == null) { - return; - } - final int featureSize = target.size(); - float error = isSelected ? 1.0f - predict : -predict; - for (int i = 0; i < featureSize; i++) { - String featureName = target.keyAt(i); - float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f); - mBias += LEARNING_RATE * error; - currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight + - LEARNING_RATE * error * target.valueAt(i); - mFeatureWeights.put(featureName, currentWeight); - } - if (DEBUG) { - Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias); - } - } - - private void commitUpdate() { - try { - SharedPreferences.Editor editor = mParamSharedPref.edit(); - editor.putFloat(BIAS_PREF_KEY, mBias); - final int size = mFeatureWeights.size(); - for (int i = 0; i < size; i++) { - editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i)); - } - editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION); - editor.apply(); - } catch (Exception e) { - Log.e(TAG, "Failed to commit update" + e); - } - } - - private SharedPreferences getParamSharedPref() { - // The package info in the context isn't initialized in the way it is for normal apps, - // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we - // build the path manually below using the same policy that appears in ContextImpl. - if (DEBUG) { - Log.d(TAG, "Context Package Name: " + getPackageName()); - } - final File prefsFile = new File(new File( - Environment.getDataUserCePackageDirectory( - StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()), - "shared_prefs"), - PARAM_SHARED_PREF_NAME + ".xml"); - return getSharedPreferences(prefsFile, Context.MODE_PRIVATE); - } -}
\ No newline at end of file diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 622b70843cc2..3f1c9adb1b68 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -530,9 +530,6 @@ public class ResolverActivity extends Activity { getMainThreadHandler().removeCallbacks(mPostListReadyRunnable); mPostListReadyRunnable = null; } - if (mAdapter != null && mAdapter.mResolverListController != null) { - mAdapter.mResolverListController.destroy(); - } } @Override diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java index 73b62a5fe60d..096fcb83e755 100644 --- a/core/java/com/android/internal/app/ResolverComparator.java +++ b/core/java/com/android/internal/app/ResolverComparator.java @@ -26,34 +26,20 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.SharedPreferences; -import android.content.ServiceConnection; import android.os.Environment; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; import android.os.storage.StorageManager; import android.os.UserHandle; -import android.service.resolver.IResolverRankerService; -import android.service.resolver.IResolverRankerResult; -import android.service.resolver.ResolverRankerService; -import android.service.resolver.ResolverTarget; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; import java.io.File; -import java.lang.InterruptedException; import java.text.Collator; import java.util.ArrayList; import java.util.Comparator; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -75,15 +61,11 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { private static final float RECENCY_MULTIPLIER = 2.f; - // message types - private static final int RESOLVER_RANKER_SERVICE_RESULT = 0; - private static final int RESOLVER_RANKER_RESULT_TIMEOUT = 1; - - // timeout for establishing connections with a ResolverRankerService. - private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200; - // timeout for establishing connections with a ResolverRankerService, collecting features and - // predicting ranking scores. - private static final int WATCHDOG_TIMEOUT_MILLIS = 500; + // feature names used in ranking. + private static final String LAUNCH_SCORE = "launch"; + private static final String TIME_SPENT_SCORE = "timeSpent"; + private static final String RECENCY_SCORE = "recency"; + private static final String CHOOSER_SCORE = "chooser"; private final Collator mCollator; private final boolean mHttp; @@ -92,74 +74,18 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { private final Map<String, UsageStats> mStats; private final long mCurrentTime; private final long mSinceTime; - private final LinkedHashMap<ComponentName, ResolverTarget> mTargetsDict = new LinkedHashMap<>(); + private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>(); private final String mReferrerPackage; - private final Object mLock = new Object(); - private ArrayList<ResolverTarget> mTargets; private String mContentType; private String[] mAnnotations; private String mAction; - private IResolverRankerService mRanker; - private ResolverRankerServiceConnection mConnection; - private AfterCompute mAfterCompute; - private Context mContext; - private CountDownLatch mConnectSignal; - - private final Handler mHandler = new Handler(Looper.getMainLooper()) { - public void handleMessage(Message msg) { - switch (msg.what) { - case RESOLVER_RANKER_SERVICE_RESULT: - if (DEBUG) { - Log.d(TAG, "RESOLVER_RANKER_SERVICE_RESULT"); - } - if (mHandler.hasMessages(RESOLVER_RANKER_RESULT_TIMEOUT)) { - if (msg.obj != null) { - final List<ResolverTarget> receivedTargets = - (List<ResolverTarget>) msg.obj; - if (receivedTargets != null && mTargets != null - && receivedTargets.size() == mTargets.size()) { - final int size = mTargets.size(); - for (int i = 0; i < size; ++i) { - mTargets.get(i).setSelectProbability( - receivedTargets.get(i).getSelectProbability()); - } - } else { - Log.e(TAG, "Sizes of sent and received ResolverTargets diff."); - } - } else { - Log.e(TAG, "Receiving null prediction results."); - } - mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT); - mAfterCompute.afterCompute(); - } - break; - - case RESOLVER_RANKER_RESULT_TIMEOUT: - if (DEBUG) { - Log.d(TAG, "RESOLVER_RANKER_RESULT_TIMEOUT; unbinding services"); - } - mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT); - mAfterCompute.afterCompute(); - break; + private LogisticRegressionAppRanker mRanker; - default: - super.handleMessage(msg); - } - } - }; - - public interface AfterCompute { - public void afterCompute (); - } - - public ResolverComparator(Context context, Intent intent, String referrerPackage, - AfterCompute afterCompute) { + public ResolverComparator(Context context, Intent intent, String referrerPackage) { mCollator = Collator.getInstance(context.getResources().getConfiguration().locale); String scheme = intent.getScheme(); mHttp = "http".equals(scheme) || "https".equals(scheme); mReferrerPackage = referrerPackage; - mAfterCompute = afterCompute; - mContext = context; mPm = context.getPackageManager(); mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); @@ -170,9 +96,9 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { mContentType = intent.getType(); getContentAnnotations(intent); mAction = intent.getAction(); + mRanker = new LogisticRegressionAppRanker(context); } - // get annotations of content from intent. public void getContentAnnotations(Intent intent) { ArrayList<String> annotations = intent.getStringArrayListExtra( Intent.EXTRA_CONTENT_ANNOTATIONS); @@ -188,24 +114,20 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { } } - public void setCallBack(AfterCompute afterCompute) { - mAfterCompute = afterCompute; - } - - // compute features for each target according to usage stats of targets. public void compute(List<ResolvedComponentInfo> targets) { - reset(); + mScoredTargets.clear(); final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD; - float mostRecencyScore = 1.0f; - float mostTimeSpentScore = 1.0f; - float mostLaunchScore = 1.0f; - float mostChooserScore = 1.0f; + long mostRecentlyUsedTime = recentSinceTime + 1; + long mostTimeSpent = 1; + int mostLaunched = 1; + int mostSelected = 1; for (ResolvedComponentInfo target : targets) { - final ResolverTarget resolverTarget = new ResolverTarget(); - mTargetsDict.put(target.name, resolverTarget); + final ScoredTarget scoredTarget + = new ScoredTarget(target.getResolveInfoAt(0).activityInfo); + mScoredTargets.put(target.name, scoredTarget); final UsageStats pkStats = mStats.get(target.name.getPackageName()); if (pkStats != null) { // Only count recency for apps that weren't the caller @@ -213,33 +135,31 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { // 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 long lastTimeUsed = pkStats.getLastTimeUsed(); + scoredTarget.lastTimeUsed = lastTimeUsed; + if (lastTimeUsed > mostRecentlyUsedTime) { + mostRecentlyUsedTime = lastTimeUsed; } } - final float timeSpentScore = (float) pkStats.getTotalTimeInForeground(); - resolverTarget.setTimeSpentScore(timeSpentScore); - if (timeSpentScore > mostTimeSpentScore) { - mostTimeSpentScore = timeSpentScore; + final long timeSpent = pkStats.getTotalTimeInForeground(); + scoredTarget.timeSpent = timeSpent; + if (timeSpent > mostTimeSpent) { + mostTimeSpent = timeSpent; } - final float launchScore = (float) pkStats.mLaunchCount; - resolverTarget.setLaunchScore(launchScore); - if (launchScore > mostLaunchScore) { - mostLaunchScore = launchScore; + final int launched = pkStats.mLaunchCount; + scoredTarget.launchCount = launched; + if (launched > mostLaunched) { + mostLaunched = launched; } - float chooserScore = 0.0f; + int selected = 0; if (pkStats.mChooserCounts != null && mAction != null && pkStats.mChooserCounts.get(mAction) != null) { - chooserScore = (float) pkStats.mChooserCounts.get(mAction) - .getOrDefault(mContentType, 0); + selected = 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) + selected += pkStats.mChooserCounts.get(mAction) .getOrDefault(mAnnotations[i], 0); } } @@ -249,37 +169,44 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { Log.d(TAG, "Action type is null"); } else { Log.d(TAG, "Chooser Count of " + mAction + ":" + - target.name.getPackageName() + " is " + - Float.toString(chooserScore)); + target.name.getPackageName() + " is " + Integer.toString(selected)); } } - resolverTarget.setChooserScore(chooserScore); - if (chooserScore > mostChooserScore) { - mostChooserScore = chooserScore; + scoredTarget.chooserCount = selected; + if (selected > mostSelected) { + mostSelected = selected; } } } + if (DEBUG) { - Log.d(TAG, "compute - mostRecencyScore: " + mostRecencyScore - + " mostTimeSpentScore: " + mostTimeSpentScore - + " mostLaunchScore: " + mostLaunchScore - + " mostChooserScore: " + mostChooserScore); + Log.d(TAG, "compute - mostRecentlyUsedTime: " + mostRecentlyUsedTime + + " mostTimeSpent: " + mostTimeSpent + + " recentSinceTime: " + recentSinceTime + + " mostLaunched: " + mostLaunched); } - mTargets = new ArrayList<>(mTargetsDict.values()); - for (ResolverTarget target : mTargets) { - final float recency = target.getRecencyScore() / mostRecencyScore; - setFeatures(target, recency * recency * RECENCY_MULTIPLIER, - target.getLaunchScore() / mostLaunchScore, - target.getTimeSpentScore() / mostTimeSpentScore, - target.getChooserScore() / mostChooserScore); - addDefaultSelectProbability(target); + for (ScoredTarget target : mScoredTargets.values()) { + final float recency = (float) Math.max(target.lastTimeUsed - recentSinceTime, 0) + / (mostRecentlyUsedTime - recentSinceTime); + target.setFeatures((float) target.launchCount / mostLaunched, + (float) target.timeSpent / mostTimeSpent, + recency * recency * RECENCY_MULTIPLIER, + (float) target.chooserCount / mostSelected); + target.selectProb = mRanker.predict(target.getFeatures()); if (DEBUG) { Log.d(TAG, "Scores: " + target); } } - predictSelectProbabilities(mTargets); + } + + static boolean isPersistentProcess(ResolvedComponentInfo rci) { + if (rci != null && rci.getCount() > 0) { + return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags & + ApplicationInfo.FLAG_PERSISTENT) != 0; + } + return false; } @Override @@ -318,16 +245,16 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { // Pinned items stay stable within a normal lexical sort and ignore scoring. if (!lPinned && !rPinned) { if (mStats != null) { - final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName( + final ScoredTarget lhsTarget = mScoredTargets.get(new ComponentName( lhs.activityInfo.packageName, lhs.activityInfo.name)); - final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName( + final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName( rhs.activityInfo.packageName, rhs.activityInfo.name)); - final int selectProbabilityDiff = Float.compare( - rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability()); + final int selectProbDiff = Float.compare( + rhsTarget.selectProb, lhsTarget.selectProb); - if (selectProbabilityDiff != 0) { - return selectProbabilityDiff > 0 ? 1 : -1; + if (selectProbDiff != 0) { + return selectProbDiff > 0 ? 1 : -1; } } } @@ -341,234 +268,177 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { } public float getScore(ComponentName name) { - final ResolverTarget target = mTargetsDict.get(name); + final ScoredTarget target = mScoredTargets.get(name); if (target != null) { - return target.getSelectProbability(); + return target.selectProb; } return 0; } - public void updateChooserCounts(String packageName, int userId, String action) { - if (mUsm != null) { - mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action); + static class ScoredTarget { + public final ComponentInfo componentInfo; + public long lastTimeUsed; + public long timeSpent; + public long launchCount; + public long chooserCount; + public ArrayMap<String, Float> features; + public float selectProb; + + public ScoredTarget(ComponentInfo ci) { + componentInfo = ci; + features = new ArrayMap<>(5); } - } - // update ranking model when the connection to it is valid. - public void updateModel(ComponentName componentName) { - synchronized (mLock) { - if (mRanker != null) { - try { - int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet()) - .indexOf(componentName); - if (selectedPos > 0) { - mRanker.train(mTargets, selectedPos); - } else { - if (DEBUG) { - Log.d(TAG, "Selected a unknown component: " + componentName); - } - } - } catch (RemoteException e) { - Log.e(TAG, "Error in Train: " + e); - } - } else { - if (DEBUG) { - Log.d(TAG, "Ranker is null; skip updateModel."); - } - } + @Override + public String toString() { + return "ScoredTarget{" + componentInfo + + " lastTimeUsed: " + lastTimeUsed + + " timeSpent: " + timeSpent + + " launchCount: " + launchCount + + " chooserCount: " + chooserCount + + " selectProb: " + selectProb + + "}"; } - } - // unbind the service and clear unhandled messges. - public void destroy() { - mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT); - mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT); - if (mConnection != null) { - mContext.unbindService(mConnection); - mConnection.destroy(); + public void setFeatures(float launchCountScore, float usageTimeScore, float recencyScore, + float chooserCountScore) { + features.put(LAUNCH_SCORE, launchCountScore); + features.put(TIME_SPENT_SCORE, usageTimeScore); + features.put(RECENCY_SCORE, recencyScore); + features.put(CHOOSER_SCORE, chooserCountScore); } - if (DEBUG) { - Log.d(TAG, "Unbinded Resolver Ranker."); + + public ArrayMap<String, Float> getFeatures() { + return features; } } - // connect to a ranking service. - private void initRanker(Context context) { - synchronized (mLock) { - if (mConnection != null && mRanker != null) { - if (DEBUG) { - Log.d(TAG, "Ranker still exists; reusing the existing one."); - } - return; - } - } - Intent intent = resolveRankerService(); - if (intent == null) { - return; + public void updateChooserCounts(String packageName, int userId, String action) { + if (mUsm != null) { + mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action); } - mConnectSignal = new CountDownLatch(1); - mConnection = new ResolverRankerServiceConnection(mConnectSignal); - context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM); } - // resolve the service for ranking. - private Intent resolveRankerService() { - Intent intent = new Intent(ResolverRankerService.SERVICE_INTERFACE); - final List<ResolveInfo> resolveInfos = mPm.queryIntentServices(intent, 0); - for (ResolveInfo resolveInfo : resolveInfos) { - if (resolveInfo == null || resolveInfo.serviceInfo == null - || resolveInfo.serviceInfo.applicationInfo == null) { - if (DEBUG) { - Log.d(TAG, "Failed to retrieve a ranker: " + resolveInfo); - } - continue; - } - ComponentName componentName = new ComponentName( - resolveInfo.serviceInfo.applicationInfo.packageName, - resolveInfo.serviceInfo.name); - try { - final String perm = mPm.getServiceInfo(componentName, 0).permission; - if (!ResolverRankerService.BIND_PERMISSION.equals(perm)) { - Log.w(TAG, "ResolverRankerService " + componentName + " does not require" - + " permission " + ResolverRankerService.BIND_PERMISSION - + " - this service will not be queried for ResolverComparator." - + " add android:permission=\"" - + ResolverRankerService.BIND_PERMISSION + "\"" - + " to the <service> tag for " + componentName - + " in the manifest."); - continue; - } - } catch (NameNotFoundException e) { - Log.e(TAG, "Could not look up service " + componentName - + "; component name not found"); + public void updateModel(ComponentName componentName) { + if (mScoredTargets == null || componentName == null || + !mScoredTargets.containsKey(componentName)) { + return; + } + ScoredTarget selected = mScoredTargets.get(componentName); + for (ComponentName targetComponent : mScoredTargets.keySet()) { + if (targetComponent.equals(componentName)) { continue; } - if (DEBUG) { - Log.d(TAG, "Succeeded to retrieve a ranker: " + componentName); + ScoredTarget target = mScoredTargets.get(targetComponent); + // A potential point of optimization. Save updates or derive a closed form for the + // positive case, to avoid calculating them repeatedly. + if (target.selectProb >= selected.selectProb) { + mRanker.update(target.getFeatures(), target.selectProb, false); + mRanker.update(selected.getFeatures(), selected.selectProb, true); } - intent.setComponent(componentName); - return intent; } - return null; + mRanker.commitUpdate(); } - // set a watchdog, to avoid waiting for ranking service for too long. - private void startWatchDog(int timeOutLimit) { - if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + timeOutLimit + "ms"); - if (mHandler == null) { - Log.d(TAG, "Error: Handler is Null; Needs to be initialized."); - } - mHandler.sendEmptyMessageDelayed(RESOLVER_RANKER_RESULT_TIMEOUT, timeOutLimit); - } + class LogisticRegressionAppRanker { + private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params"; + private static final String BIAS_PREF_KEY = "bias"; + private static final String VERSION_PREF_KEY = "version"; - private class ResolverRankerServiceConnection implements ServiceConnection { - private final CountDownLatch mConnectSignal; + // parameters for a pre-trained model, to initialize the app ranker. When updating the + // pre-trained model, please update these params, as well as initModel(). + private static final int CURRENT_VERSION = 1; + private static final float LEARNING_RATE = 0.0001f; + private static final float REGULARIZER_PARAM = 0.0001f; - public ResolverRankerServiceConnection(CountDownLatch connectSignal) { - mConnectSignal = connectSignal; - } + private SharedPreferences mParamSharedPref; + private ArrayMap<String, Float> mFeatureWeights; + private float mBias; - public final IResolverRankerResult resolverRankerResult = - new IResolverRankerResult.Stub() { - @Override - public void sendResult(List<ResolverTarget> targets) throws RemoteException { - if (DEBUG) { - Log.d(TAG, "Sending Result back to Resolver: " + targets); - } - synchronized (mLock) { - final Message msg = Message.obtain(); - msg.what = RESOLVER_RANKER_SERVICE_RESULT; - msg.obj = targets; - mHandler.sendMessage(msg); - } - } - }; + public LogisticRegressionAppRanker(Context context) { + mParamSharedPref = getParamSharedPref(context); + initModel(); + } - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) { - Log.d(TAG, "onServiceConnected: " + name); + public float predict(ArrayMap<String, Float> target) { + if (target == null) { + return 0.0f; } - synchronized (mLock) { - mRanker = IResolverRankerService.Stub.asInterface(service); - mConnectSignal.countDown(); + final int featureSize = target.size(); + float sum = 0.0f; + for (int i = 0; i < featureSize; i++) { + String featureName = target.keyAt(i); + float weight = mFeatureWeights.getOrDefault(featureName, 0.0f); + sum += weight * target.valueAt(i); } + return (float) (1.0 / (1.0 + Math.exp(-mBias - sum))); } - @Override - public void onServiceDisconnected(ComponentName name) { - if (DEBUG) { - Log.d(TAG, "onServiceDisconnected: " + name); + public void update(ArrayMap<String, Float> target, float predict, boolean isSelected) { + if (target == null) { + return; } - synchronized (mLock) { - destroy(); + final int featureSize = target.size(); + float error = isSelected ? 1.0f - predict : -predict; + for (int i = 0; i < featureSize; i++) { + String featureName = target.keyAt(i); + float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f); + mBias += LEARNING_RATE * error; + currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight + + LEARNING_RATE * error * target.valueAt(i); + mFeatureWeights.put(featureName, currentWeight); + } + if (DEBUG) { + Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias); } } - public void destroy() { - synchronized (mLock) { - mRanker = null; + public void commitUpdate() { + SharedPreferences.Editor editor = mParamSharedPref.edit(); + editor.putFloat(BIAS_PREF_KEY, mBias); + final int size = mFeatureWeights.size(); + for (int i = 0; i < size; i++) { + editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i)); } + editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION); + editor.apply(); } - } - private void reset() { - mTargetsDict.clear(); - mTargets = null; - startWatchDog(WATCHDOG_TIMEOUT_MILLIS); - initRanker(mContext); - } - - // predict select probabilities if ranking service is valid. - private void predictSelectProbabilities(List<ResolverTarget> targets) { - if (mConnection == null) { + private SharedPreferences getParamSharedPref(Context context) { + // The package info in the context isn't initialized in the way it is for normal apps, + // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we + // build the path manually below using the same policy that appears in ContextImpl. if (DEBUG) { - Log.d(TAG, "Has not found valid ResolverRankerService; Skip Prediction"); - } - return; - } else { - try { - mConnectSignal.await(CONNECTION_COST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - synchronized (mLock) { - if (mRanker != null) { - mRanker.predict(targets, mConnection.resolverRankerResult); - return; - } else { - if (DEBUG) { - Log.d(TAG, "Ranker has not been initialized; skip predict."); - } - } - } - } catch (InterruptedException e) { - Log.e(TAG, "Error in Wait for Service Connection."); - } catch (RemoteException e) { - Log.e(TAG, "Error in Predict: " + e); + Log.d(TAG, "Context Package Name: " + context.getPackageName()); } + final File prefsFile = new File(new File( + Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL, + context.getUserId(), context.getPackageName()), + "shared_prefs"), + PARAM_SHARED_PREF_NAME + ".xml"); + return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE); } - mAfterCompute.afterCompute(); - } - // adds select prob as the default values, according to a pre-trained Logistic Regression model. - private void addDefaultSelectProbability(ResolverTarget target) { - float sum = 2.5543f * target.getLaunchScore() + 2.8412f * target.getTimeSpentScore() + - 0.269f * target.getRecencyScore() + 4.2222f * target.getChooserScore(); - target.setSelectProbability((float) (1.0 / (1.0 + Math.exp(1.6568f - sum)))); - } - - // sets features for each target - private void setFeatures(ResolverTarget target, float recencyScore, float launchScore, - float timeSpentScore, float chooserScore) { - target.setRecencyScore(recencyScore); - target.setLaunchScore(launchScore); - target.setTimeSpentScore(timeSpentScore); - target.setChooserScore(chooserScore); - } - - static boolean isPersistentProcess(ResolvedComponentInfo rci) { - if (rci != null && rci.getCount() > 0) { - return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags & - ApplicationInfo.FLAG_PERSISTENT) != 0; + private void initModel() { + mFeatureWeights = new ArrayMap<>(4); + if (mParamSharedPref == null || + mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) { + // Initializing the app ranker to a pre-trained model. When updating the pre-trained + // model, please increment CURRENT_VERSION, and update LEARNING_RATE and + // REGULARIZER_PARAM. + mBias = -1.6568f; + mFeatureWeights.put(LAUNCH_SCORE, 2.5543f); + mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f); + mFeatureWeights.put(RECENCY_SCORE, 0.269f); + mFeatureWeights.put(CHOOSER_SCORE, 4.2222f); + } else { + mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f); + mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f)); + mFeatureWeights.put( + TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f)); + mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f)); + mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f)); + } } - return false; } } diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index e8bebb74bdd0..4071ff4ebd5a 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -32,10 +32,8 @@ import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import java.lang.InterruptedException; import java.util.ArrayList; import java.util.Collections; -import java.util.concurrent.CountDownLatch; import java.util.List; /** @@ -207,42 +205,14 @@ public class ResolverListController { return listToReturn; } - private class ComputeCallback implements ResolverComparator.AfterCompute { - - private CountDownLatch mFinishComputeSignal; - - public ComputeCallback(CountDownLatch finishComputeSignal) { - mFinishComputeSignal = finishComputeSignal; - } - - public void afterCompute () { - mFinishComputeSignal.countDown(); - } - } - @VisibleForTesting @WorkerThread public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) { - final CountDownLatch finishComputeSignal = new CountDownLatch(1); - ComputeCallback callback = new ComputeCallback(finishComputeSignal); if (mResolverComparator == null) { - mResolverComparator = - new ResolverComparator(mContext, mTargetIntent, mReferrerPackage, callback); - } else { - mResolverComparator.setCallBack(callback); - } - try { - long beforeRank = System.currentTimeMillis(); - mResolverComparator.compute(inputList); - finishComputeSignal.await(); - Collections.sort(inputList, mResolverComparator); - long afterRank = System.currentTimeMillis(); - if (DEBUG) { - Log.d(TAG, "Time Cost: " + Long.toString(afterRank - beforeRank)); - } - } catch (InterruptedException e) { - Log.e(TAG, "Compute & Sort was interrupted: " + e); + mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage); } + mResolverComparator.compute(inputList); + Collections.sort(inputList, mResolverComparator); } private static boolean isSameResolvedComponent(ResolveInfo a, @@ -263,7 +233,7 @@ public class ResolverListController { @VisibleForTesting public float getScore(ResolverActivity.DisplayResolveInfo target) { if (mResolverComparator == null) { - return 0.0f; + mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage); } return mResolverComparator.getScore(target.getResolvedComponentName()); } @@ -279,10 +249,4 @@ public class ResolverListController { mResolverComparator.updateChooserCounts(packageName, userId, action); } } - - public void destroy() { - if (mResolverComparator != null) { - mResolverComparator.destroy(); - } - } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index faaca532dee9..ffcb84762eb5 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3129,15 +3129,6 @@ <permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE" android:protectionLevel="signature" /> - <!-- @SystemApi Must be required by services that extend - {@link android.service.resolver.ResolverRankerService}, to ensure that only the system can - bind to them. - <p>Protection level: signature - @hide - --> - <permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE" - android:protectionLevel="signature" /> - <!-- Must be required by a {@link android.service.notification.ConditionProviderService}, to ensure that only the system can bind to it. @@ -3643,14 +3634,6 @@ android:permission="android.permission.BIND_JOB_SERVICE" > </service> - <service android:name="com.android.internal.app.LRResolverRankerService" - android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE" - android:exported="false" - android:priority="-1" > - <intent-filter> - <action android:name="android.service.resolver.ResolverRankerService" /> - </intent-filter> - </service> </application> </manifest> |