TextClassifierService.java revision ad52f4b97c897689d0b4dbfe344229a9970136eb
1/* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.service.textclassifier; 18 19import android.Manifest; 20import android.annotation.IntRange; 21import android.annotation.NonNull; 22import android.annotation.Nullable; 23import android.annotation.SystemApi; 24import android.app.Service; 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.Intent; 28import android.content.pm.PackageManager; 29import android.content.pm.ResolveInfo; 30import android.content.pm.ServiceInfo; 31import android.os.CancellationSignal; 32import android.os.IBinder; 33import android.os.RemoteException; 34import android.text.TextUtils; 35import android.util.Slog; 36import android.view.textclassifier.SelectionEvent; 37import android.view.textclassifier.TextClassification; 38import android.view.textclassifier.TextClassificationManager; 39import android.view.textclassifier.TextClassifier; 40import android.view.textclassifier.TextLinks; 41import android.view.textclassifier.TextSelection; 42 43/** 44 * Abstract base class for the TextClassifier service. 45 * 46 * <p>A TextClassifier service provides text classification related features for the system. 47 * The system's default TextClassifierService is configured in 48 * {@code config_defaultTextClassifierService}. If this config has no value, a 49 * {@link android.view.textclassifier.TextClassifierImpl} is loaded in the calling app's process. 50 * 51 * <p>See: {@link TextClassifier}. 52 * See: {@link TextClassificationManager}. 53 * 54 * <p>Include the following in the manifest: 55 * 56 * <pre> 57 * {@literal 58 * <service android:name=".YourTextClassifierService" 59 * android:permission="android.permission.BIND_TEXTCLASSIFIER_SERVICE"> 60 * <intent-filter> 61 * <action android:name="android.service.textclassifier.TextClassifierService" /> 62 * </intent-filter> 63 * </service>}</pre> 64 * 65 * @see TextClassifier 66 * @hide 67 */ 68@SystemApi 69public abstract class TextClassifierService extends Service { 70 71 private static final String LOG_TAG = "TextClassifierService"; 72 73 /** 74 * The {@link Intent} that must be declared as handled by the service. 75 * To be supported, the service must also require the 76 * {@link android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE} permission so 77 * that other applications can not abuse it. 78 */ 79 @SystemApi 80 public static final String SERVICE_INTERFACE = 81 "android.service.textclassifier.TextClassifierService"; 82 83 private final ITextClassifierService.Stub mBinder = new ITextClassifierService.Stub() { 84 85 // TODO(b/72533911): Implement cancellation signal 86 @NonNull private final CancellationSignal mCancellationSignal = new CancellationSignal(); 87 88 /** {@inheritDoc} */ 89 @Override 90 public void onSuggestSelection( 91 CharSequence text, int selectionStartIndex, int selectionEndIndex, 92 TextSelection.Options options, ITextSelectionCallback callback) 93 throws RemoteException { 94 TextClassifierService.this.onSuggestSelection( 95 text, selectionStartIndex, selectionEndIndex, options, mCancellationSignal, 96 new Callback<TextSelection>() { 97 @Override 98 public void onSuccess(TextSelection result) { 99 try { 100 callback.onSuccess(result); 101 } catch (RemoteException e) { 102 Slog.d(LOG_TAG, "Error calling callback"); 103 } 104 } 105 106 @Override 107 public void onFailure(CharSequence error) { 108 try { 109 if (callback.asBinder().isBinderAlive()) { 110 callback.onFailure(); 111 } 112 } catch (RemoteException e) { 113 Slog.d(LOG_TAG, "Error calling callback"); 114 } 115 } 116 }); 117 } 118 119 /** {@inheritDoc} */ 120 @Override 121 public void onClassifyText( 122 CharSequence text, int startIndex, int endIndex, 123 TextClassification.Options options, ITextClassificationCallback callback) 124 throws RemoteException { 125 TextClassifierService.this.onClassifyText( 126 text, startIndex, endIndex, options, mCancellationSignal, 127 new Callback<TextClassification>() { 128 @Override 129 public void onSuccess(TextClassification result) { 130 try { 131 callback.onSuccess(result); 132 } catch (RemoteException e) { 133 Slog.d(LOG_TAG, "Error calling callback"); 134 } 135 } 136 137 @Override 138 public void onFailure(CharSequence error) { 139 try { 140 callback.onFailure(); 141 } catch (RemoteException e) { 142 Slog.d(LOG_TAG, "Error calling callback"); 143 } 144 } 145 }); 146 } 147 148 /** {@inheritDoc} */ 149 @Override 150 public void onGenerateLinks( 151 CharSequence text, TextLinks.Options options, ITextLinksCallback callback) 152 throws RemoteException { 153 TextClassifierService.this.onGenerateLinks( 154 text, options, mCancellationSignal, 155 new Callback<TextLinks>() { 156 @Override 157 public void onSuccess(TextLinks result) { 158 try { 159 callback.onSuccess(result); 160 } catch (RemoteException e) { 161 Slog.d(LOG_TAG, "Error calling callback"); 162 } 163 } 164 165 @Override 166 public void onFailure(CharSequence error) { 167 try { 168 callback.onFailure(); 169 } catch (RemoteException e) { 170 Slog.d(LOG_TAG, "Error calling callback"); 171 } 172 } 173 }); 174 } 175 176 /** {@inheritDoc} */ 177 @Override 178 public void onSelectionEvent(SelectionEvent event) throws RemoteException { 179 TextClassifierService.this.onSelectionEvent(event); 180 } 181 }; 182 183 @Nullable 184 @Override 185 public final IBinder onBind(Intent intent) { 186 if (SERVICE_INTERFACE.equals(intent.getAction())) { 187 return mBinder; 188 } 189 return null; 190 } 191 192 /** 193 * Returns suggested text selection start and end indices, recognized entity types, and their 194 * associated confidence scores. The entity types are ordered from highest to lowest scoring. 195 * 196 * @param text text providing context for the selected text (which is specified 197 * by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex) 198 * @param selectionStartIndex start index of the selected part of text 199 * @param selectionEndIndex end index of the selected part of text 200 * @param options optional input parameters 201 * @param cancellationSignal object to watch for canceling the current operation 202 * @param callback the callback to return the result to 203 */ 204 public abstract void onSuggestSelection( 205 @NonNull CharSequence text, 206 @IntRange(from = 0) int selectionStartIndex, 207 @IntRange(from = 0) int selectionEndIndex, 208 @Nullable TextSelection.Options options, 209 @NonNull CancellationSignal cancellationSignal, 210 @NonNull Callback<TextSelection> callback); 211 212 /** 213 * Classifies the specified text and returns a {@link TextClassification} object that can be 214 * used to generate a widget for handling the classified text. 215 * 216 * @param text text providing context for the text to classify (which is specified 217 * by the sub sequence starting at startIndex and ending at endIndex) 218 * @param startIndex start index of the text to classify 219 * @param endIndex end index of the text to classify 220 * @param options optional input parameters 221 * @param cancellationSignal object to watch for canceling the current operation 222 * @param callback the callback to return the result to 223 */ 224 public abstract void onClassifyText( 225 @NonNull CharSequence text, 226 @IntRange(from = 0) int startIndex, 227 @IntRange(from = 0) int endIndex, 228 @Nullable TextClassification.Options options, 229 @NonNull CancellationSignal cancellationSignal, 230 @NonNull Callback<TextClassification> callback); 231 232 /** 233 * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with 234 * links information. 235 * 236 * @param text the text to generate annotations for 237 * @param options configuration for link generation 238 * @param cancellationSignal object to watch for canceling the current operation 239 * @param callback the callback to return the result to 240 */ 241 public abstract void onGenerateLinks( 242 @NonNull CharSequence text, 243 @Nullable TextLinks.Options options, 244 @NonNull CancellationSignal cancellationSignal, 245 @NonNull Callback<TextLinks> callback); 246 247 /** 248 * Writes the selection event. 249 * This is called when a selection event occurs. e.g. user changed selection; or smart selection 250 * happened. 251 * 252 * <p>The default implementation ignores the event. 253 */ 254 public void onSelectionEvent(@NonNull SelectionEvent event) {} 255 256 /** 257 * Returns a TextClassifier that runs in this service's process. 258 * If the local TextClassifier is disabled, this returns {@link TextClassifier#NO_OP}. 259 */ 260 public final TextClassifier getLocalTextClassifier() { 261 final TextClassificationManager tcm = getSystemService(TextClassificationManager.class); 262 if (tcm != null) { 263 return tcm.getTextClassifier(TextClassifier.LOCAL); 264 } 265 return TextClassifier.NO_OP; 266 } 267 268 /** 269 * Callbacks for TextClassifierService results. 270 * 271 * @param <T> the type of the result 272 * @hide 273 */ 274 @SystemApi 275 public interface Callback<T> { 276 /** 277 * Returns the result. 278 */ 279 void onSuccess(T result); 280 281 /** 282 * Signals a failure. 283 */ 284 void onFailure(CharSequence error); 285 } 286 287 /** 288 * Returns the component name of the system default textclassifier service if it can be found 289 * on the system. Otherwise, returns null. 290 * @hide 291 */ 292 @Nullable 293 public static ComponentName getServiceComponentName(Context context) { 294 final String packageName = context.getPackageManager().getSystemTextClassifierPackageName(); 295 if (TextUtils.isEmpty(packageName)) { 296 Slog.d(LOG_TAG, "No configured system TextClassifierService"); 297 return null; 298 } 299 300 final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName); 301 302 final ResolveInfo ri = context.getPackageManager().resolveService(intent, 303 PackageManager.MATCH_SYSTEM_ONLY); 304 305 if ((ri == null) || (ri.serviceInfo == null)) { 306 Slog.w(LOG_TAG, String.format("Package or service not found in package %s", 307 packageName)); 308 return null; 309 } 310 final ServiceInfo si = ri.serviceInfo; 311 312 final String permission = si.permission; 313 if (Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE.equals(permission)) { 314 return si.getComponentName(); 315 } 316 Slog.w(LOG_TAG, String.format( 317 "Service %s should require %s permission. Found %s permission", 318 si.getComponentName(), 319 Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE, 320 si.permission)); 321 return null; 322 } 323} 324