blob: 6c8c8bc361270a03a0979065c473a23ba0eb5112 [file] [log] [blame]
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -08001/*
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.ServiceInfo;
30import android.os.CancellationSignal;
31import android.os.IBinder;
32import android.os.RemoteException;
33import android.text.TextUtils;
34import android.util.Slog;
35import android.view.textclassifier.TextClassification;
36import android.view.textclassifier.TextClassifier;
37import android.view.textclassifier.TextLinks;
38import android.view.textclassifier.TextSelection;
39
40import com.android.internal.R;
41
42/**
43 * Abstract base class for the TextClassifier service.
44 *
45 * <p>A TextClassifier service provides text classification related features for the system.
46 * The system's default TextClassifierService is configured in
47 * {@code config_defaultTextClassifierService}. If this config has no value, a
48 * {@link android.view.textclassifier.TextClassifierImpl} is loaded in the calling app's process.
49 *
50 * <p>See: {@link TextClassifier}.
51 * See: {@link android.view.textclassifier.TextClassificationManager}.
52 *
53 * <p>Include the following in the manifest:
54 *
55 * <pre>
56 * {@literal
57 * <service android:name=".YourTextClassifierService"
58 * android:permission="android.permission.BIND_TEXTCLASSIFIER_SERVICE">
59 * <intent-filter>
60 * <action android:name="android.service.textclassifier.TextClassifierService" />
61 * </intent-filter>
62 * </service>}</pre>
63 *
64 * @see TextClassifier
65 * @hide
66 */
67@SystemApi
68public abstract class TextClassifierService extends Service {
69
70 private static final String LOG_TAG = "TextClassifierService";
71
72 /**
73 * The {@link Intent} that must be declared as handled by the service.
74 * To be supported, the service must also require the
75 * {@link android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE} permission so
76 * that other applications can not abuse it.
77 */
78 @SystemApi
79 public static final String SERVICE_INTERFACE =
80 "android.service.textclassifier.TextClassifierService";
81
82 private final ITextClassifierService.Stub mBinder = new ITextClassifierService.Stub() {
83
84 // TODO(b/72533911): Implement cancellation signal
85 @NonNull private final CancellationSignal mCancellationSignal = new CancellationSignal();
86
87 /** {@inheritDoc} */
88 @Override
89 public void onSuggestSelection(
90 CharSequence text, int selectionStartIndex, int selectionEndIndex,
91 TextSelection.Options options, ITextSelectionCallback callback)
92 throws RemoteException {
93 TextClassifierService.this.onSuggestSelection(
94 text, selectionStartIndex, selectionEndIndex, options, mCancellationSignal,
95 new Callback<TextSelection>() {
96 @Override
97 public void onSuccess(TextSelection result) {
98 try {
99 callback.onSuccess(result);
100 } catch (RemoteException e) {
101 Slog.d(LOG_TAG, "Error calling callback");
102 }
103 }
104
105 @Override
106 public void onFailure(CharSequence error) {
107 try {
108 if (callback.asBinder().isBinderAlive()) {
109 callback.onFailure();
110 }
111 } catch (RemoteException e) {
112 Slog.d(LOG_TAG, "Error calling callback");
113 }
114 }
115 });
116 }
117
118 /** {@inheritDoc} */
119 @Override
120 public void onClassifyText(
121 CharSequence text, int startIndex, int endIndex,
122 TextClassification.Options options, ITextClassificationCallback callback)
123 throws RemoteException {
124 TextClassifierService.this.onClassifyText(
125 text, startIndex, endIndex, options, mCancellationSignal,
126 new Callback<TextClassification>() {
127 @Override
128 public void onSuccess(TextClassification result) {
129 try {
130 callback.onSuccess(result);
131 } catch (RemoteException e) {
132 Slog.d(LOG_TAG, "Error calling callback");
133 }
134 }
135
136 @Override
137 public void onFailure(CharSequence error) {
138 try {
139 callback.onFailure();
140 } catch (RemoteException e) {
141 Slog.d(LOG_TAG, "Error calling callback");
142 }
143 }
144 });
145 }
146
147 /** {@inheritDoc} */
148 @Override
149 public void onGenerateLinks(
150 CharSequence text, TextLinks.Options options, ITextLinksCallback callback)
151 throws RemoteException {
152 TextClassifierService.this.onGenerateLinks(
153 text, options, mCancellationSignal,
154 new Callback<TextLinks>() {
155 @Override
156 public void onSuccess(TextLinks result) {
157 try {
158 callback.onSuccess(result);
159 } catch (RemoteException e) {
160 Slog.d(LOG_TAG, "Error calling callback");
161 }
162 }
163
164 @Override
165 public void onFailure(CharSequence error) {
166 try {
167 callback.onFailure();
168 } catch (RemoteException e) {
169 Slog.d(LOG_TAG, "Error calling callback");
170 }
171 }
172 });
173 }
174 };
175
176 @Nullable
177 @Override
178 public final IBinder onBind(Intent intent) {
179 if (SERVICE_INTERFACE.equals(intent.getAction())) {
180 return mBinder;
181 }
182 return null;
183 }
184
185 /**
186 * Returns suggested text selection start and end indices, recognized entity types, and their
187 * associated confidence scores. The entity types are ordered from highest to lowest scoring.
188 *
189 * @param text text providing context for the selected text (which is specified
190 * by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
191 * @param selectionStartIndex start index of the selected part of text
192 * @param selectionEndIndex end index of the selected part of text
193 * @param options optional input parameters
194 * @param cancellationSignal object to watch for canceling the current operation
195 * @param callback the callback to return the result to
196 */
197 public abstract void onSuggestSelection(
198 @NonNull CharSequence text,
199 @IntRange(from = 0) int selectionStartIndex,
200 @IntRange(from = 0) int selectionEndIndex,
201 @Nullable TextSelection.Options options,
202 @NonNull CancellationSignal cancellationSignal,
203 @NonNull Callback<TextSelection> callback);
204
205 /**
206 * Classifies the specified text and returns a {@link TextClassification} object that can be
207 * used to generate a widget for handling the classified text.
208 *
209 * @param text text providing context for the text to classify (which is specified
210 * by the sub sequence starting at startIndex and ending at endIndex)
211 * @param startIndex start index of the text to classify
212 * @param endIndex end index of the text to classify
213 * @param options optional input parameters
214 * @param cancellationSignal object to watch for canceling the current operation
215 * @param callback the callback to return the result to
216 */
217 public abstract void onClassifyText(
218 @NonNull CharSequence text,
219 @IntRange(from = 0) int startIndex,
220 @IntRange(from = 0) int endIndex,
221 @Nullable TextClassification.Options options,
222 @NonNull CancellationSignal cancellationSignal,
223 @NonNull Callback<TextClassification> callback);
224
225 /**
226 * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
227 * links information.
228 *
229 * @param text the text to generate annotations for
230 * @param options configuration for link generation
231 * @param cancellationSignal object to watch for canceling the current operation
232 * @param callback the callback to return the result to
233 */
234 public abstract void onGenerateLinks(
235 @NonNull CharSequence text,
236 @Nullable TextLinks.Options options,
237 @NonNull CancellationSignal cancellationSignal,
238 @NonNull Callback<TextLinks> callback);
239
240 /**
241 * Callbacks for TextClassifierService results.
242 *
243 * @param <T> the type of the result
244 * @hide
245 */
246 @SystemApi
247 public interface Callback<T> {
248 /**
249 * Returns the result.
250 */
251 void onSuccess(T result);
252
253 /**
254 * Signals a failure.
255 */
256 void onFailure(CharSequence error);
257 }
258
259 /**
260 * Returns the component name of the system default textclassifier service if it can be found
261 * on the system. Otherwise, returns null.
262 * @hide
263 */
264 @Nullable
265 public static ComponentName getServiceComponentName(Context context) {
266 final String str = context.getString(R.string.config_defaultTextClassifierService);
267 if (!TextUtils.isEmpty(str)) {
268 try {
269 final ComponentName componentName = ComponentName.unflattenFromString(str);
270 final Intent intent = new Intent(SERVICE_INTERFACE).setComponent(componentName);
271 final ServiceInfo si = context.getPackageManager()
272 .getServiceInfo(intent.getComponent(), 0);
273 final String permission = si == null ? null : si.permission;
274 if (Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE.equals(permission)) {
275 return componentName;
276 }
277 Slog.w(LOG_TAG, String.format(
278 "Service %s should require %s permission. Found %s permission",
279 intent.getComponent().flattenToString(),
280 Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE,
281 si.permission));
282 } catch (PackageManager.NameNotFoundException e) {
283 Slog.w(LOG_TAG, String.format("Service %s not found", str));
284 }
285 } else {
286 Slog.d(LOG_TAG, "No configured system TextClassifierService");
287 }
288 return null;
289 }
290}