ResolverRankerService.java revision 90370e0b2497deba9382ab7ff1539b6849df8139
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.service.resolver; 18 19import android.annotation.SdkConstant; 20import android.annotation.SystemApi; 21import android.app.Service; 22import android.content.ComponentName; 23import android.content.Intent; 24import android.os.IBinder; 25import android.os.Handler; 26import android.os.HandlerThread; 27import android.os.RemoteException; 28import android.service.resolver.ResolverTarget; 29import android.util.Log; 30 31import java.util.List; 32import java.util.Map; 33 34/** 35 * A service to rank apps according to usage stats of apps, when the system is resolving targets for 36 * an Intent. 37 * 38 * <p>To extend this class, you must declare the service in your manifest file with the 39 * {@link android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE} permission, and include an 40 * intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> 41 * <pre> 42 * <service android:name=".MyResolverRankerService" 43 * android:exported="true" 44 * android:priority="100" 45 * android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE"> 46 * <intent-filter> 47 * <action android:name="android.service.resolver.ResolverRankerService" /> 48 * </intent-filter> 49 * </service> 50 * </pre> 51 * @hide 52 */ 53@SystemApi 54public abstract class ResolverRankerService extends Service { 55 56 private static final String TAG = "ResolverRankerService"; 57 58 private static final boolean DEBUG = false; 59 60 /** 61 * The Intent action that a service must respond to. Add it to the intent filter of the service 62 * in its manifest. 63 */ 64 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 65 public static final String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService"; 66 67 /** 68 * The permission that a service must require to ensure that only Android system can bind to it. 69 * If this permission is not enforced in the AndroidManifest of the service, the system will 70 * skip that service. 71 */ 72 public static final String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; 73 74 private ResolverRankerServiceWrapper mWrapper = null; 75 76 /** 77 * Called by the system to retrieve a list of probabilities to rank apps/options. To implement 78 * it, set selectProbability of each input {@link ResolverTarget}. The higher the 79 * selectProbability is, the more likely the {@link ResolverTarget} will be selected by the 80 * user. Override this function to provide prediction results. 81 * 82 * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked. 83 * 84 * @throws Exception when the prediction task fails. 85 */ 86 public void onPredictSharingProbabilities(final List<ResolverTarget> targets) {} 87 88 /** 89 * Called by the system to train/update a ranking service, after the user makes a selection from 90 * the ranked list of apps. Override this function to enable model updates. 91 * 92 * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked. 93 * @param selectedPosition the position of the selected app in the list. 94 * 95 * @throws Exception when the training task fails. 96 */ 97 public void onTrainRankingModel( 98 final List<ResolverTarget> targets, final int selectedPosition) {} 99 100 private static final String HANDLER_THREAD_NAME = "RESOLVER_RANKER_SERVICE"; 101 private volatile Handler mHandler; 102 private HandlerThread mHandlerThread; 103 104 @Override 105 public IBinder onBind(Intent intent) { 106 if (DEBUG) Log.d(TAG, "onBind " + intent); 107 if (!SERVICE_INTERFACE.equals(intent.getAction())) { 108 if (DEBUG) Log.d(TAG, "bad intent action " + intent.getAction() + "; returning null"); 109 return null; 110 } 111 if (mHandlerThread == null) { 112 mHandlerThread = new HandlerThread(HANDLER_THREAD_NAME); 113 mHandlerThread.start(); 114 mHandler = new Handler(mHandlerThread.getLooper()); 115 } 116 if (mWrapper == null) { 117 mWrapper = new ResolverRankerServiceWrapper(); 118 } 119 return mWrapper; 120 } 121 122 @Override 123 public void onDestroy() { 124 mHandler = null; 125 if (mHandlerThread != null) { 126 mHandlerThread.quitSafely(); 127 } 128 super.onDestroy(); 129 } 130 131 private static void sendResult(List<ResolverTarget> targets, IResolverRankerResult result) { 132 try { 133 result.sendResult(targets); 134 } catch (Exception e) { 135 Log.e(TAG, "failed to send results: " + e); 136 } 137 } 138 139 private class ResolverRankerServiceWrapper extends IResolverRankerService.Stub { 140 141 @Override 142 public void predict(final List<ResolverTarget> targets, final IResolverRankerResult result) 143 throws RemoteException { 144 Runnable predictRunnable = new Runnable() { 145 @Override 146 public void run() { 147 try { 148 if (DEBUG) { 149 Log.d(TAG, "predict calls onPredictSharingProbabilities."); 150 } 151 onPredictSharingProbabilities(targets); 152 sendResult(targets, result); 153 } catch (Exception e) { 154 Log.e(TAG, "onPredictSharingProbabilities failed; send null results: " + e); 155 sendResult(null, result); 156 } 157 } 158 }; 159 final Handler h = mHandler; 160 if (h != null) { 161 h.post(predictRunnable); 162 } 163 } 164 165 @Override 166 public void train(final List<ResolverTarget> targets, final int selectedPosition) 167 throws RemoteException { 168 Runnable trainRunnable = new Runnable() { 169 @Override 170 public void run() { 171 try { 172 if (DEBUG) { 173 Log.d(TAG, "train calls onTranRankingModel"); 174 } 175 onTrainRankingModel(targets, selectedPosition); 176 } catch (Exception e) { 177 Log.e(TAG, "onTrainRankingModel failed; skip train: " + e); 178 } 179 } 180 }; 181 final Handler h = mHandler; 182 if (h != null) { 183 h.post(trainRunnable); 184 } 185 } 186 } 187} 188