blob: 3ff6f549e33771cc3141cb9a18bc69c887afbc4d [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;
Joanne Chung0b7c0d42019-10-18 21:02:26 +080020import android.annotation.IntDef;
Tony Mak9920dbb2019-01-23 19:49:30 +000021import android.annotation.MainThread;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080022import android.annotation.NonNull;
23import android.annotation.Nullable;
24import android.annotation.SystemApi;
Joanne Chungb50ab4b2019-10-14 16:40:57 +080025import android.annotation.TestApi;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080026import android.app.Service;
27import android.content.ComponentName;
28import android.content.Context;
29import android.content.Intent;
Makoto Onuki700feef2018-02-15 10:59:41 -080030import android.content.pm.ResolveInfo;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080031import android.content.pm.ServiceInfo;
Tony Mak9920dbb2019-01-23 19:49:30 +000032import android.os.Bundle;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080033import android.os.CancellationSignal;
Tony Mak9920dbb2019-01-23 19:49:30 +000034import android.os.Handler;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080035import android.os.IBinder;
Tony Mak9920dbb2019-01-23 19:49:30 +000036import android.os.Looper;
37import android.os.Parcelable;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080038import android.os.RemoteException;
39import android.text.TextUtils;
40import android.util.Slog;
Tony Mak0be540b2018-11-09 16:58:35 +000041import android.view.textclassifier.ConversationActions;
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +000042import android.view.textclassifier.SelectionEvent;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080043import android.view.textclassifier.TextClassification;
Tony Makdf5d4bf2020-02-05 11:39:51 +000044import android.view.textclassifier.TextClassificationConstants;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010045import android.view.textclassifier.TextClassificationContext;
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +000046import android.view.textclassifier.TextClassificationManager;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010047import android.view.textclassifier.TextClassificationSessionId;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080048import android.view.textclassifier.TextClassifier;
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +000049import android.view.textclassifier.TextClassifierEvent;
Tony Mak0be540b2018-11-09 16:58:35 +000050import android.view.textclassifier.TextLanguage;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080051import android.view.textclassifier.TextLinks;
52import android.view.textclassifier.TextSelection;
53
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010054import com.android.internal.util.Preconditions;
55
Joanne Chung0b7c0d42019-10-18 21:02:26 +080056import java.lang.annotation.Retention;
57import java.lang.annotation.RetentionPolicy;
Tony Mak9920dbb2019-01-23 19:49:30 +000058import java.util.concurrent.ExecutorService;
59import java.util.concurrent.Executors;
60
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080061/**
62 * Abstract base class for the TextClassifier service.
63 *
64 * <p>A TextClassifier service provides text classification related features for the system.
Nikita Dubrovsky942db102019-09-24 10:49:19 -070065 * The system's default TextClassifierService provider is configured in
66 * {@code config_defaultTextClassifierPackage}. If this config has no value, a
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080067 * {@link android.view.textclassifier.TextClassifierImpl} is loaded in the calling app's process.
68 *
69 * <p>See: {@link TextClassifier}.
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +000070 * See: {@link TextClassificationManager}.
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080071 *
72 * <p>Include the following in the manifest:
73 *
74 * <pre>
75 * {@literal
76 * <service android:name=".YourTextClassifierService"
77 * android:permission="android.permission.BIND_TEXTCLASSIFIER_SERVICE">
78 * <intent-filter>
79 * <action android:name="android.service.textclassifier.TextClassifierService" />
80 * </intent-filter>
81 * </service>}</pre>
82 *
Tony Mak9920dbb2019-01-23 19:49:30 +000083 * <p>From {@link android.os.Build.VERSION_CODES#Q} onward, all callbacks are called on the main
84 * thread. Prior to Q, there is no guarantee on what thread the callback will happen. You should
85 * make sure the callbacks are executed in your desired thread by using a executor, a handler or
86 * something else along the line.
87 *
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080088 * @see TextClassifier
89 * @hide
90 */
91@SystemApi
Joanne Chungb50ab4b2019-10-14 16:40:57 +080092@TestApi
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080093public abstract class TextClassifierService extends Service {
94
95 private static final String LOG_TAG = "TextClassifierService";
96
97 /**
98 * The {@link Intent} that must be declared as handled by the service.
99 * To be supported, the service must also require the
100 * {@link android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE} permission so
101 * that other applications can not abuse it.
102 */
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800103 public static final String SERVICE_INTERFACE =
104 "android.service.textclassifier.TextClassifierService";
105
Tony Mak9920dbb2019-01-23 19:49:30 +0000106 /** @hide **/
Joanne Chung0b7c0d42019-10-18 21:02:26 +0800107 public static final int CONNECTED = 0;
108 /** @hide **/
109 public static final int DISCONNECTED = 1;
110 /** @hide */
111 @IntDef(value = {
112 CONNECTED,
113 DISCONNECTED
114 })
115 @Retention(RetentionPolicy.SOURCE)
116 public @interface ConnectionState{}
117
118 /** @hide **/
Tony Mak9920dbb2019-01-23 19:49:30 +0000119 private static final String KEY_RESULT = "key_result";
120
121 private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper(), null, true);
122 private final ExecutorService mSingleThreadExecutor = Executors.newSingleThreadExecutor();
123
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800124 private final ITextClassifierService.Stub mBinder = new ITextClassifierService.Stub() {
125
126 // TODO(b/72533911): Implement cancellation signal
127 @NonNull private final CancellationSignal mCancellationSignal = new CancellationSignal();
128
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800129 @Override
130 public void onSuggestSelection(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100131 TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000132 TextSelection.Request request, ITextClassifierCallback callback) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100133 Preconditions.checkNotNull(request);
134 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000135 mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestSelection(
136 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800137
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800138 }
139
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800140 @Override
141 public void onClassifyText(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100142 TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000143 TextClassification.Request request, ITextClassifierCallback callback) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100144 Preconditions.checkNotNull(request);
145 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000146 mMainThreadHandler.post(() -> TextClassifierService.this.onClassifyText(
147 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800148 }
149
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800150 @Override
151 public void onGenerateLinks(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100152 TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000153 TextLinks.Request request, ITextClassifierCallback callback) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100154 Preconditions.checkNotNull(request);
155 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000156 mMainThreadHandler.post(() -> TextClassifierService.this.onGenerateLinks(
157 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800158 }
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000159
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000160 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100161 public void onSelectionEvent(
162 TextClassificationSessionId sessionId,
Tony Mak0be540b2018-11-09 16:58:35 +0000163 SelectionEvent event) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100164 Preconditions.checkNotNull(event);
Tony Mak9920dbb2019-01-23 19:49:30 +0000165 mMainThreadHandler.post(
166 () -> TextClassifierService.this.onSelectionEvent(sessionId, event));
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100167 }
168
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100169 @Override
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000170 public void onTextClassifierEvent(
171 TextClassificationSessionId sessionId,
172 TextClassifierEvent event) {
173 Preconditions.checkNotNull(event);
Tony Mak9920dbb2019-01-23 19:49:30 +0000174 mMainThreadHandler.post(
175 () -> TextClassifierService.this.onTextClassifierEvent(sessionId, event));
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000176 }
177
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000178 @Override
Tony Mak0be540b2018-11-09 16:58:35 +0000179 public void onDetectLanguage(
180 TextClassificationSessionId sessionId,
181 TextLanguage.Request request,
Tony Mak9920dbb2019-01-23 19:49:30 +0000182 ITextClassifierCallback callback) {
Tony Mak0be540b2018-11-09 16:58:35 +0000183 Preconditions.checkNotNull(request);
184 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000185 mMainThreadHandler.post(() -> TextClassifierService.this.onDetectLanguage(
186 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Tony Mak0be540b2018-11-09 16:58:35 +0000187 }
188
Tony Mak0be540b2018-11-09 16:58:35 +0000189 @Override
190 public void onSuggestConversationActions(
191 TextClassificationSessionId sessionId,
192 ConversationActions.Request request,
Tony Mak9920dbb2019-01-23 19:49:30 +0000193 ITextClassifierCallback callback) {
Tony Mak0be540b2018-11-09 16:58:35 +0000194 Preconditions.checkNotNull(request);
195 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000196 mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestConversationActions(
197 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Tony Mak0be540b2018-11-09 16:58:35 +0000198 }
199
Tony Mak0be540b2018-11-09 16:58:35 +0000200 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100201 public void onCreateTextClassificationSession(
Tony Mak0be540b2018-11-09 16:58:35 +0000202 TextClassificationContext context, TextClassificationSessionId sessionId) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100203 Preconditions.checkNotNull(context);
204 Preconditions.checkNotNull(sessionId);
Tony Mak9920dbb2019-01-23 19:49:30 +0000205 mMainThreadHandler.post(
206 () -> TextClassifierService.this.onCreateTextClassificationSession(
207 context, sessionId));
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100208 }
209
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100210 @Override
Tony Mak0be540b2018-11-09 16:58:35 +0000211 public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000212 mMainThreadHandler.post(
213 () -> TextClassifierService.this.onDestroyTextClassificationSession(sessionId));
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000214 }
Joanne Chung0b7c0d42019-10-18 21:02:26 +0800215
216 @Override
217 public void onConnectedStateChanged(@ConnectionState int connected) {
218 mMainThreadHandler.post(connected == CONNECTED ? TextClassifierService.this::onConnected
219 : TextClassifierService.this::onDisconnected);
220 }
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800221 };
222
223 @Nullable
224 @Override
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800225 public final IBinder onBind(@NonNull Intent intent) {
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800226 if (SERVICE_INTERFACE.equals(intent.getAction())) {
227 return mBinder;
228 }
229 return null;
230 }
231
Joanne Chung0b7c0d42019-10-18 21:02:26 +0800232 @Override
233 public boolean onUnbind(@NonNull Intent intent) {
234 onDisconnected();
235 return super.onUnbind(intent);
236 }
237
238 /**
239 * Called when the Android system connects to service.
240 */
241 public void onConnected() {
242 }
243
244 /**
245 * Called when the Android system disconnects from the service.
246 *
247 * <p> At this point this service may no longer be an active {@link TextClassifierService}.
248 */
249 public void onDisconnected() {
250 }
251
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800252 /**
253 * Returns suggested text selection start and end indices, recognized entity types, and their
254 * associated confidence scores. The entity types are ordered from highest to lowest scoring.
255 *
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100256 * @param sessionId the session id
257 * @param request the text selection request
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800258 * @param cancellationSignal object to watch for canceling the current operation
259 * @param callback the callback to return the result to
260 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000261 @MainThread
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800262 public abstract void onSuggestSelection(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100263 @Nullable TextClassificationSessionId sessionId,
264 @NonNull TextSelection.Request request,
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800265 @NonNull CancellationSignal cancellationSignal,
266 @NonNull Callback<TextSelection> callback);
267
268 /**
269 * Classifies the specified text and returns a {@link TextClassification} object that can be
270 * used to generate a widget for handling the classified text.
271 *
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100272 * @param sessionId the session id
273 * @param request the text classification request
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800274 * @param cancellationSignal object to watch for canceling the current operation
275 * @param callback the callback to return the result to
276 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000277 @MainThread
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800278 public abstract void onClassifyText(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100279 @Nullable TextClassificationSessionId sessionId,
280 @NonNull TextClassification.Request request,
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800281 @NonNull CancellationSignal cancellationSignal,
282 @NonNull Callback<TextClassification> callback);
283
284 /**
285 * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
286 * links information.
287 *
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100288 * @param sessionId the session id
289 * @param request the text classification request
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800290 * @param cancellationSignal object to watch for canceling the current operation
291 * @param callback the callback to return the result to
292 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000293 @MainThread
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800294 public abstract void onGenerateLinks(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100295 @Nullable TextClassificationSessionId sessionId,
296 @NonNull TextLinks.Request request,
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800297 @NonNull CancellationSignal cancellationSignal,
298 @NonNull Callback<TextLinks> callback);
299
300 /**
Tony Mak0be540b2018-11-09 16:58:35 +0000301 * Detects and returns the language of the give text.
302 *
303 * @param sessionId the session id
304 * @param request the language detection request
305 * @param cancellationSignal object to watch for canceling the current operation
306 * @param callback the callback to return the result to
307 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000308 @MainThread
Tony Mak0be540b2018-11-09 16:58:35 +0000309 public void onDetectLanguage(
310 @Nullable TextClassificationSessionId sessionId,
311 @NonNull TextLanguage.Request request,
312 @NonNull CancellationSignal cancellationSignal,
313 @NonNull Callback<TextLanguage> callback) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000314 mSingleThreadExecutor.submit(() ->
315 callback.onSuccess(getLocalTextClassifier().detectLanguage(request)));
Tony Mak0be540b2018-11-09 16:58:35 +0000316 }
317
318 /**
319 * Suggests and returns a list of actions according to the given conversation.
320 *
321 * @param sessionId the session id
322 * @param request the conversation actions request
323 * @param cancellationSignal object to watch for canceling the current operation
324 * @param callback the callback to return the result to
325 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000326 @MainThread
Tony Mak0be540b2018-11-09 16:58:35 +0000327 public void onSuggestConversationActions(
328 @Nullable TextClassificationSessionId sessionId,
329 @NonNull ConversationActions.Request request,
330 @NonNull CancellationSignal cancellationSignal,
331 @NonNull Callback<ConversationActions> callback) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000332 mSingleThreadExecutor.submit(() ->
333 callback.onSuccess(getLocalTextClassifier().suggestConversationActions(request)));
Tony Mak0be540b2018-11-09 16:58:35 +0000334 }
335
336 /**
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000337 * Writes the selection event.
338 * This is called when a selection event occurs. e.g. user changed selection; or smart selection
339 * happened.
340 *
341 * <p>The default implementation ignores the event.
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100342 *
343 * @param sessionId the session id
344 * @param event the selection event
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000345 * @deprecated
346 * Use {@link #onTextClassifierEvent(TextClassificationSessionId, TextClassifierEvent)}
347 * instead
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000348 */
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000349 @Deprecated
Tony Mak9920dbb2019-01-23 19:49:30 +0000350 @MainThread
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100351 public void onSelectionEvent(
352 @Nullable TextClassificationSessionId sessionId, @NonNull SelectionEvent event) {}
353
354 /**
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000355 * Writes the TextClassifier event.
356 * This is called when a TextClassifier event occurs. e.g. user changed selection,
357 * smart selection happened, or a link was clicked.
358 *
359 * <p>The default implementation ignores the event.
360 *
361 * @param sessionId the session id
362 * @param event the TextClassifier event
363 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000364 @MainThread
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000365 public void onTextClassifierEvent(
366 @Nullable TextClassificationSessionId sessionId, @NonNull TextClassifierEvent event) {}
367
368 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100369 * Creates a new text classification session for the specified context.
370 *
371 * @param context the text classification context
372 * @param sessionId the session's Id
373 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000374 @MainThread
Jan Althaus39ccc7e2018-04-04 13:56:40 +0200375 public void onCreateTextClassificationSession(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100376 @NonNull TextClassificationContext context,
Jan Althaus39ccc7e2018-04-04 13:56:40 +0200377 @NonNull TextClassificationSessionId sessionId) {}
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100378
379 /**
380 * Destroys the text classification session identified by the specified sessionId.
381 *
382 * @param sessionId the id of the session to destroy
383 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000384 @MainThread
Jan Althaus39ccc7e2018-04-04 13:56:40 +0200385 public void onDestroyTextClassificationSession(
386 @NonNull TextClassificationSessionId sessionId) {}
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000387
388 /**
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000389 * Returns a TextClassifier that runs in this service's process.
390 * If the local TextClassifier is disabled, this returns {@link TextClassifier#NO_OP}.
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000391 *
392 * @deprecated Use {@link #getDefaultTextClassifierImplementation(Context)} instead.
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000393 */
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000394 @Deprecated
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000395 public final TextClassifier getLocalTextClassifier() {
Tony Makc5a74322020-02-04 17:18:15 +0000396 return TextClassifier.NO_OP;
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000397 }
398
399 /**
400 * Returns the platform's default TextClassifier implementation.
Tony Makc5a74322020-02-04 17:18:15 +0000401 *
402 * @throws RuntimeException if the TextClassifier from
403 * PackageManager#getDefaultTextClassifierPackageName() calls
404 * this method.
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000405 */
Abodunrinwa Toki9aed55e2019-04-10 20:30:31 +0100406 @NonNull
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000407 public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
408 final TextClassificationManager tcm =
409 context.getSystemService(TextClassificationManager.class);
Tony Makdf5d4bf2020-02-05 11:39:51 +0000410 if (tcm == null) {
411 return TextClassifier.NO_OP;
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000412 }
Tony Makdf5d4bf2020-02-05 11:39:51 +0000413 TextClassificationConstants settings = new TextClassificationConstants();
414 if (settings.getUseDefaultTextClassifierAsDefaultImplementation()) {
415 final String defaultTextClassifierPackageName =
416 context.getPackageManager().getDefaultTextClassifierPackageName();
417 if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
418 return TextClassifier.NO_OP;
419 }
420 if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
421 throw new RuntimeException(
422 "The default text classifier itself should not call the"
423 + "getDefaultTextClassifierImplementation() method.");
424 }
425 return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE);
426 } else {
427 return tcm.getTextClassifier(TextClassifier.LOCAL);
428 }
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000429 }
430
Tony Mak9920dbb2019-01-23 19:49:30 +0000431 /** @hide **/
432 public static <T extends Parcelable> T getResponse(Bundle bundle) {
433 return bundle.getParcelable(KEY_RESULT);
434 }
435
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000436 /**
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800437 * Callbacks for TextClassifierService results.
438 *
439 * @param <T> the type of the result
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800440 */
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800441 public interface Callback<T> {
442 /**
443 * Returns the result.
444 */
445 void onSuccess(T result);
446
447 /**
448 * Signals a failure.
449 */
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800450 void onFailure(@NonNull CharSequence error);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800451 }
452
453 /**
Tony Makc5a74322020-02-04 17:18:15 +0000454 * Returns the component name of the textclassifier service from the given package.
455 * Otherwise, returns null.
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800456 *
Tony Makc5a74322020-02-04 17:18:15 +0000457 * @param context
458 * @param packageName the package to look for.
459 * @param resolveFlags the flags that are used by PackageManager to resolve the component name.
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800460 * @hide
461 */
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800462 @Nullable
Tony Makc5a74322020-02-04 17:18:15 +0000463 public static ComponentName getServiceComponentName(
464 Context context, String packageName, int resolveFlags) {
Makoto Onuki700feef2018-02-15 10:59:41 -0800465 final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName);
466
Tony Makc5a74322020-02-04 17:18:15 +0000467 final ResolveInfo ri = context.getPackageManager().resolveService(intent, resolveFlags);
Makoto Onuki700feef2018-02-15 10:59:41 -0800468
469 if ((ri == null) || (ri.serviceInfo == null)) {
Felipe Lemef8192132018-10-12 13:42:40 -0700470 Slog.w(LOG_TAG, String.format("Package or service not found in package %s for user %d",
471 packageName, context.getUserId()));
Makoto Onuki700feef2018-02-15 10:59:41 -0800472 return null;
473 }
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800474
Makoto Onuki700feef2018-02-15 10:59:41 -0800475 final ServiceInfo si = ri.serviceInfo;
476
477 final String permission = si.permission;
478 if (Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE.equals(permission)) {
479 return si.getComponentName();
480 }
481 Slog.w(LOG_TAG, String.format(
482 "Service %s should require %s permission. Found %s permission",
483 si.getComponentName(),
484 Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE,
485 si.permission));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800486 return null;
487 }
Tony Mak9920dbb2019-01-23 19:49:30 +0000488
489 /**
490 * Forwards the callback result to a wrapped binder callback.
491 */
492 private static final class ProxyCallback<T extends Parcelable> implements Callback<T> {
Tony Makcdc81e82019-08-22 15:14:28 +0100493 private ITextClassifierCallback mTextClassifierCallback;
Tony Mak9920dbb2019-01-23 19:49:30 +0000494
495 private ProxyCallback(ITextClassifierCallback textClassifierCallback) {
Tony Makcdc81e82019-08-22 15:14:28 +0100496 mTextClassifierCallback = Preconditions.checkNotNull(textClassifierCallback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000497 }
498
499 @Override
500 public void onSuccess(T result) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000501 try {
502 Bundle bundle = new Bundle(1);
503 bundle.putParcelable(KEY_RESULT, result);
Tony Makcdc81e82019-08-22 15:14:28 +0100504 mTextClassifierCallback.onSuccess(bundle);
Tony Mak9920dbb2019-01-23 19:49:30 +0000505 } catch (RemoteException e) {
506 Slog.d(LOG_TAG, "Error calling callback");
507 }
508 }
509
510 @Override
511 public void onFailure(CharSequence error) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000512 try {
Joanne Chung0b7c2c42019-08-16 16:55:11 +0800513 Slog.w(LOG_TAG, "Request fail: " + error);
Tony Makcdc81e82019-08-22 15:14:28 +0100514 mTextClassifierCallback.onFailure();
Tony Mak9920dbb2019-01-23 19:49:30 +0000515 } catch (RemoteException e) {
516 Slog.d(LOG_TAG, "Error calling callback");
517 }
518 }
519 }
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800520}