138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li/* 238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * Copyright (C) 2017 The Android Open Source Project 338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * 438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * Licensed under the Apache License, Version 2.0 (the "License"); 538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * you may not use this file except in compliance with the License. 638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * You may obtain a copy of the License at 738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * 838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * http://www.apache.org/licenses/LICENSE-2.0 938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * 1038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * Unless required by applicable law or agreed to in writing, software 1138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * distributed under the License is distributed on an "AS IS" BASIS, 1238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * See the License for the specific language governing permissions and 1438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * limitations under the License. 1538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li */ 1638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 1738a6da6473563ce2dcee360cabe1183c2a7c926eKang Lipackage android.service.resolver; 1838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 1938a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.annotation.SdkConstant; 2038a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.annotation.SystemApi; 2138a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.app.Service; 2238a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.content.ComponentName; 2338a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.content.Intent; 2438a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.os.IBinder; 2538a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.os.Handler; 2638a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.os.HandlerThread; 2738a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.os.RemoteException; 2838a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.service.resolver.ResolverTarget; 2938a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport android.util.Log; 3038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 3138a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport java.util.List; 3238a6da6473563ce2dcee360cabe1183c2a7c926eKang Liimport java.util.Map; 3338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 3438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li/** 3538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * A service to rank apps according to usage stats of apps, when the system is resolving targets for 3638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * an Intent. 3738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * 3838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * <p>To extend this class, you must declare the service in your manifest file with the 3938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * {@link android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE} permission, and include an 4038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> 4138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * <pre> 4238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * <service android:name=".MyResolverRankerService" 4338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * android:exported="true" 4438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * android:priority="100" 4538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE"> 4638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * <intent-filter> 4738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * <action android:name="android.service.resolver.ResolverRankerService" /> 4838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * </intent-filter> 4938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * </service> 5038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * </pre> 5138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * @hide 5238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li */ 5338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li@SystemApi 5438a6da6473563ce2dcee360cabe1183c2a7c926eKang Lipublic abstract class ResolverRankerService extends Service { 5538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 5638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li private static final String TAG = "ResolverRankerService"; 5738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 5838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li private static final boolean DEBUG = false; 5938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 6038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li /** 6138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * The Intent action that a service must respond to. Add it to the intent filter of the service 6238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * in its manifest. 6338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li */ 6438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 6538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li public static final String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService"; 6638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 6738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li /** 680f80719a6f3c30619ad73a9f2452626ed5ef5bd0Kang Li * The permission that a service must hold. If the service does not hold the permission, the 690f80719a6f3c30619ad73a9f2452626ed5ef5bd0Kang Li * system will skip that service. 700f80719a6f3c30619ad73a9f2452626ed5ef5bd0Kang Li */ 710f80719a6f3c30619ad73a9f2452626ed5ef5bd0Kang Li public static final String HOLD_PERMISSION = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE"; 720f80719a6f3c30619ad73a9f2452626ed5ef5bd0Kang Li 730f80719a6f3c30619ad73a9f2452626ed5ef5bd0Kang Li /** 7438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * The permission that a service must require to ensure that only Android system can bind to it. 7538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * If this permission is not enforced in the AndroidManifest of the service, the system will 7638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * skip that service. 7738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li */ 7838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li public static final String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; 7938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 8038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li private ResolverRankerServiceWrapper mWrapper = null; 8138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 8238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li /** 8338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * Called by the system to retrieve a list of probabilities to rank apps/options. To implement 8438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * it, set selectProbability of each input {@link ResolverTarget}. The higher the 8538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * selectProbability is, the more likely the {@link ResolverTarget} will be selected by the 8638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * user. Override this function to provide prediction results. 8738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * 8838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked. 8938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * 9038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * @throws Exception when the prediction task fails. 9138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li */ 9238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li public void onPredictSharingProbabilities(final List<ResolverTarget> targets) {} 9338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 9438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li /** 9538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * Called by the system to train/update a ranking service, after the user makes a selection from 9638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * the ranked list of apps. Override this function to enable model updates. 9738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * 9838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked. 9938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * @param selectedPosition the position of the selected app in the list. 10038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * 10138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li * @throws Exception when the training task fails. 10238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li */ 10338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li public void onTrainRankingModel( 10438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li final List<ResolverTarget> targets, final int selectedPosition) {} 10538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 10638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li private static final String HANDLER_THREAD_NAME = "RESOLVER_RANKER_SERVICE"; 10738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li private volatile Handler mHandler; 10838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li private HandlerThread mHandlerThread; 10938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 11038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li @Override 11138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li public IBinder onBind(Intent intent) { 11238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li if (DEBUG) Log.d(TAG, "onBind " + intent); 11338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li if (!SERVICE_INTERFACE.equals(intent.getAction())) { 11438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li if (DEBUG) Log.d(TAG, "bad intent action " + intent.getAction() + "; returning null"); 11538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li return null; 11638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 11738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li if (mHandlerThread == null) { 11838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li mHandlerThread = new HandlerThread(HANDLER_THREAD_NAME); 11938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li mHandlerThread.start(); 12038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li mHandler = new Handler(mHandlerThread.getLooper()); 12138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 12238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li if (mWrapper == null) { 12338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li mWrapper = new ResolverRankerServiceWrapper(); 12438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 12538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li return mWrapper; 12638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 12738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 12838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li @Override 12938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li public void onDestroy() { 13038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li mHandler = null; 13138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li if (mHandlerThread != null) { 13238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li mHandlerThread.quitSafely(); 13338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 13438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li super.onDestroy(); 13538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 13638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 13738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li private static void sendResult(List<ResolverTarget> targets, IResolverRankerResult result) { 13838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li try { 13938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li result.sendResult(targets); 14038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } catch (Exception e) { 14138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li Log.e(TAG, "failed to send results: " + e); 14238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 14338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 14438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 14538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li private class ResolverRankerServiceWrapper extends IResolverRankerService.Stub { 14638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 14738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li @Override 14838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li public void predict(final List<ResolverTarget> targets, final IResolverRankerResult result) 14938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li throws RemoteException { 15038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li Runnable predictRunnable = new Runnable() { 15138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li @Override 15238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li public void run() { 15338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li try { 15438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li if (DEBUG) { 15538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li Log.d(TAG, "predict calls onPredictSharingProbabilities."); 15638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 15738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li onPredictSharingProbabilities(targets); 15838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li sendResult(targets, result); 15938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } catch (Exception e) { 16038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li Log.e(TAG, "onPredictSharingProbabilities failed; send null results: " + e); 16138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li sendResult(null, result); 16238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 16338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 16438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li }; 16538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li final Handler h = mHandler; 16638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li if (h != null) { 16738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li h.post(predictRunnable); 16838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 16938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 17038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li 17138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li @Override 17238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li public void train(final List<ResolverTarget> targets, final int selectedPosition) 17338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li throws RemoteException { 17438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li Runnable trainRunnable = new Runnable() { 17538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li @Override 17638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li public void run() { 17738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li try { 17838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li if (DEBUG) { 17938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li Log.d(TAG, "train calls onTranRankingModel"); 18038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 18138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li onTrainRankingModel(targets, selectedPosition); 18238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } catch (Exception e) { 18338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li Log.e(TAG, "onTrainRankingModel failed; skip train: " + e); 18438a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 18538a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 18638a6da6473563ce2dcee360cabe1183c2a7c926eKang Li }; 18738a6da6473563ce2dcee360cabe1183c2a7c926eKang Li final Handler h = mHandler; 18838a6da6473563ce2dcee360cabe1183c2a7c926eKang Li if (h != null) { 18938a6da6473563ce2dcee360cabe1183c2a7c926eKang Li h.post(trainRunnable); 19038a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 19138a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 19238a6da6473563ce2dcee360cabe1183c2a7c926eKang Li } 19338a6da6473563ce2dcee360cabe1183c2a7c926eKang Li} 194