blob: 9dfbc285c381c4d204cf15c589cd3800225fad14 [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;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010044import android.view.textclassifier.TextClassificationContext;
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +000045import android.view.textclassifier.TextClassificationManager;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010046import android.view.textclassifier.TextClassificationSessionId;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080047import android.view.textclassifier.TextClassifier;
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +000048import android.view.textclassifier.TextClassifierEvent;
Tony Mak0be540b2018-11-09 16:58:35 +000049import android.view.textclassifier.TextLanguage;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080050import android.view.textclassifier.TextLinks;
51import android.view.textclassifier.TextSelection;
52
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010053import com.android.internal.util.Preconditions;
54
Joanne Chung0b7c0d42019-10-18 21:02:26 +080055import java.lang.annotation.Retention;
56import java.lang.annotation.RetentionPolicy;
Tony Mak9920dbb2019-01-23 19:49:30 +000057import java.util.concurrent.ExecutorService;
58import java.util.concurrent.Executors;
59
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080060/**
61 * Abstract base class for the TextClassifier service.
62 *
63 * <p>A TextClassifier service provides text classification related features for the system.
Nikita Dubrovsky942db102019-09-24 10:49:19 -070064 * The system's default TextClassifierService provider is configured in
65 * {@code config_defaultTextClassifierPackage}. If this config has no value, a
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080066 * {@link android.view.textclassifier.TextClassifierImpl} is loaded in the calling app's process.
67 *
68 * <p>See: {@link TextClassifier}.
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +000069 * See: {@link TextClassificationManager}.
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080070 *
71 * <p>Include the following in the manifest:
72 *
73 * <pre>
74 * {@literal
75 * <service android:name=".YourTextClassifierService"
76 * android:permission="android.permission.BIND_TEXTCLASSIFIER_SERVICE">
77 * <intent-filter>
78 * <action android:name="android.service.textclassifier.TextClassifierService" />
79 * </intent-filter>
80 * </service>}</pre>
81 *
Tony Mak9920dbb2019-01-23 19:49:30 +000082 * <p>From {@link android.os.Build.VERSION_CODES#Q} onward, all callbacks are called on the main
83 * thread. Prior to Q, there is no guarantee on what thread the callback will happen. You should
84 * make sure the callbacks are executed in your desired thread by using a executor, a handler or
85 * something else along the line.
86 *
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080087 * @see TextClassifier
88 * @hide
89 */
90@SystemApi
Joanne Chungb50ab4b2019-10-14 16:40:57 +080091@TestApi
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080092public abstract class TextClassifierService extends Service {
93
94 private static final String LOG_TAG = "TextClassifierService";
95
96 /**
97 * The {@link Intent} that must be declared as handled by the service.
98 * To be supported, the service must also require the
99 * {@link android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE} permission so
100 * that other applications can not abuse it.
101 */
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800102 public static final String SERVICE_INTERFACE =
103 "android.service.textclassifier.TextClassifierService";
104
Tony Mak9920dbb2019-01-23 19:49:30 +0000105 /** @hide **/
Joanne Chung0b7c0d42019-10-18 21:02:26 +0800106 public static final int CONNECTED = 0;
107 /** @hide **/
108 public static final int DISCONNECTED = 1;
109 /** @hide */
110 @IntDef(value = {
111 CONNECTED,
112 DISCONNECTED
113 })
114 @Retention(RetentionPolicy.SOURCE)
115 public @interface ConnectionState{}
116
117 /** @hide **/
Tony Mak9920dbb2019-01-23 19:49:30 +0000118 private static final String KEY_RESULT = "key_result";
119
120 private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper(), null, true);
121 private final ExecutorService mSingleThreadExecutor = Executors.newSingleThreadExecutor();
122
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800123 private final ITextClassifierService.Stub mBinder = new ITextClassifierService.Stub() {
124
125 // TODO(b/72533911): Implement cancellation signal
126 @NonNull private final CancellationSignal mCancellationSignal = new CancellationSignal();
127
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800128 @Override
129 public void onSuggestSelection(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100130 TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000131 TextSelection.Request request, ITextClassifierCallback callback) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100132 Preconditions.checkNotNull(request);
133 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000134 mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestSelection(
135 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800136
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800137 }
138
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800139 @Override
140 public void onClassifyText(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100141 TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000142 TextClassification.Request request, ITextClassifierCallback callback) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100143 Preconditions.checkNotNull(request);
144 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000145 mMainThreadHandler.post(() -> TextClassifierService.this.onClassifyText(
146 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800147 }
148
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800149 @Override
150 public void onGenerateLinks(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100151 TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000152 TextLinks.Request request, ITextClassifierCallback callback) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100153 Preconditions.checkNotNull(request);
154 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000155 mMainThreadHandler.post(() -> TextClassifierService.this.onGenerateLinks(
156 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800157 }
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000158
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000159 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100160 public void onSelectionEvent(
161 TextClassificationSessionId sessionId,
Tony Mak0be540b2018-11-09 16:58:35 +0000162 SelectionEvent event) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100163 Preconditions.checkNotNull(event);
Tony Mak9920dbb2019-01-23 19:49:30 +0000164 mMainThreadHandler.post(
165 () -> TextClassifierService.this.onSelectionEvent(sessionId, event));
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100166 }
167
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100168 @Override
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000169 public void onTextClassifierEvent(
170 TextClassificationSessionId sessionId,
171 TextClassifierEvent event) {
172 Preconditions.checkNotNull(event);
Tony Mak9920dbb2019-01-23 19:49:30 +0000173 mMainThreadHandler.post(
174 () -> TextClassifierService.this.onTextClassifierEvent(sessionId, event));
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000175 }
176
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000177 @Override
Tony Mak0be540b2018-11-09 16:58:35 +0000178 public void onDetectLanguage(
179 TextClassificationSessionId sessionId,
180 TextLanguage.Request request,
Tony Mak9920dbb2019-01-23 19:49:30 +0000181 ITextClassifierCallback callback) {
Tony Mak0be540b2018-11-09 16:58:35 +0000182 Preconditions.checkNotNull(request);
183 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000184 mMainThreadHandler.post(() -> TextClassifierService.this.onDetectLanguage(
185 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Tony Mak0be540b2018-11-09 16:58:35 +0000186 }
187
Tony Mak0be540b2018-11-09 16:58:35 +0000188 @Override
189 public void onSuggestConversationActions(
190 TextClassificationSessionId sessionId,
191 ConversationActions.Request request,
Tony Mak9920dbb2019-01-23 19:49:30 +0000192 ITextClassifierCallback callback) {
Tony Mak0be540b2018-11-09 16:58:35 +0000193 Preconditions.checkNotNull(request);
194 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000195 mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestConversationActions(
196 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Tony Mak0be540b2018-11-09 16:58:35 +0000197 }
198
Tony Mak0be540b2018-11-09 16:58:35 +0000199 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100200 public void onCreateTextClassificationSession(
Tony Mak0be540b2018-11-09 16:58:35 +0000201 TextClassificationContext context, TextClassificationSessionId sessionId) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100202 Preconditions.checkNotNull(context);
203 Preconditions.checkNotNull(sessionId);
Tony Mak9920dbb2019-01-23 19:49:30 +0000204 mMainThreadHandler.post(
205 () -> TextClassifierService.this.onCreateTextClassificationSession(
206 context, sessionId));
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100207 }
208
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100209 @Override
Tony Mak0be540b2018-11-09 16:58:35 +0000210 public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000211 mMainThreadHandler.post(
212 () -> TextClassifierService.this.onDestroyTextClassificationSession(sessionId));
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000213 }
Joanne Chung0b7c0d42019-10-18 21:02:26 +0800214
215 @Override
216 public void onConnectedStateChanged(@ConnectionState int connected) {
217 mMainThreadHandler.post(connected == CONNECTED ? TextClassifierService.this::onConnected
218 : TextClassifierService.this::onDisconnected);
219 }
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800220 };
221
222 @Nullable
223 @Override
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800224 public final IBinder onBind(@NonNull Intent intent) {
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800225 if (SERVICE_INTERFACE.equals(intent.getAction())) {
226 return mBinder;
227 }
228 return null;
229 }
230
Joanne Chung0b7c0d42019-10-18 21:02:26 +0800231 @Override
232 public boolean onUnbind(@NonNull Intent intent) {
233 onDisconnected();
234 return super.onUnbind(intent);
235 }
236
237 /**
238 * Called when the Android system connects to service.
239 */
240 public void onConnected() {
241 }
242
243 /**
244 * Called when the Android system disconnects from the service.
245 *
246 * <p> At this point this service may no longer be an active {@link TextClassifierService}.
247 */
248 public void onDisconnected() {
249 }
250
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800251 /**
252 * Returns suggested text selection start and end indices, recognized entity types, and their
253 * associated confidence scores. The entity types are ordered from highest to lowest scoring.
254 *
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100255 * @param sessionId the session id
256 * @param request the text selection request
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800257 * @param cancellationSignal object to watch for canceling the current operation
258 * @param callback the callback to return the result to
259 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000260 @MainThread
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800261 public abstract void onSuggestSelection(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100262 @Nullable TextClassificationSessionId sessionId,
263 @NonNull TextSelection.Request request,
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800264 @NonNull CancellationSignal cancellationSignal,
265 @NonNull Callback<TextSelection> callback);
266
267 /**
268 * Classifies the specified text and returns a {@link TextClassification} object that can be
269 * used to generate a widget for handling the classified text.
270 *
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100271 * @param sessionId the session id
272 * @param request the text classification request
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800273 * @param cancellationSignal object to watch for canceling the current operation
274 * @param callback the callback to return the result to
275 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000276 @MainThread
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800277 public abstract void onClassifyText(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100278 @Nullable TextClassificationSessionId sessionId,
279 @NonNull TextClassification.Request request,
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800280 @NonNull CancellationSignal cancellationSignal,
281 @NonNull Callback<TextClassification> callback);
282
283 /**
284 * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
285 * links information.
286 *
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100287 * @param sessionId the session id
288 * @param request the text classification request
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800289 * @param cancellationSignal object to watch for canceling the current operation
290 * @param callback the callback to return the result to
291 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000292 @MainThread
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800293 public abstract void onGenerateLinks(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100294 @Nullable TextClassificationSessionId sessionId,
295 @NonNull TextLinks.Request request,
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800296 @NonNull CancellationSignal cancellationSignal,
297 @NonNull Callback<TextLinks> callback);
298
299 /**
Tony Mak0be540b2018-11-09 16:58:35 +0000300 * Detects and returns the language of the give text.
301 *
302 * @param sessionId the session id
303 * @param request the language detection request
304 * @param cancellationSignal object to watch for canceling the current operation
305 * @param callback the callback to return the result to
306 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000307 @MainThread
Tony Mak0be540b2018-11-09 16:58:35 +0000308 public void onDetectLanguage(
309 @Nullable TextClassificationSessionId sessionId,
310 @NonNull TextLanguage.Request request,
311 @NonNull CancellationSignal cancellationSignal,
312 @NonNull Callback<TextLanguage> callback) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000313 mSingleThreadExecutor.submit(() ->
314 callback.onSuccess(getLocalTextClassifier().detectLanguage(request)));
Tony Mak0be540b2018-11-09 16:58:35 +0000315 }
316
317 /**
318 * Suggests and returns a list of actions according to the given conversation.
319 *
320 * @param sessionId the session id
321 * @param request the conversation actions request
322 * @param cancellationSignal object to watch for canceling the current operation
323 * @param callback the callback to return the result to
324 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000325 @MainThread
Tony Mak0be540b2018-11-09 16:58:35 +0000326 public void onSuggestConversationActions(
327 @Nullable TextClassificationSessionId sessionId,
328 @NonNull ConversationActions.Request request,
329 @NonNull CancellationSignal cancellationSignal,
330 @NonNull Callback<ConversationActions> callback) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000331 mSingleThreadExecutor.submit(() ->
332 callback.onSuccess(getLocalTextClassifier().suggestConversationActions(request)));
Tony Mak0be540b2018-11-09 16:58:35 +0000333 }
334
335 /**
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000336 * Writes the selection event.
337 * This is called when a selection event occurs. e.g. user changed selection; or smart selection
338 * happened.
339 *
340 * <p>The default implementation ignores the event.
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100341 *
342 * @param sessionId the session id
343 * @param event the selection event
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000344 * @deprecated
345 * Use {@link #onTextClassifierEvent(TextClassificationSessionId, TextClassifierEvent)}
346 * instead
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000347 */
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000348 @Deprecated
Tony Mak9920dbb2019-01-23 19:49:30 +0000349 @MainThread
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100350 public void onSelectionEvent(
351 @Nullable TextClassificationSessionId sessionId, @NonNull SelectionEvent event) {}
352
353 /**
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000354 * Writes the TextClassifier event.
355 * This is called when a TextClassifier event occurs. e.g. user changed selection,
356 * smart selection happened, or a link was clicked.
357 *
358 * <p>The default implementation ignores the event.
359 *
360 * @param sessionId the session id
361 * @param event the TextClassifier event
362 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000363 @MainThread
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000364 public void onTextClassifierEvent(
365 @Nullable TextClassificationSessionId sessionId, @NonNull TextClassifierEvent event) {}
366
367 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100368 * Creates a new text classification session for the specified context.
369 *
370 * @param context the text classification context
371 * @param sessionId the session's Id
372 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000373 @MainThread
Jan Althaus39ccc7e2018-04-04 13:56:40 +0200374 public void onCreateTextClassificationSession(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100375 @NonNull TextClassificationContext context,
Jan Althaus39ccc7e2018-04-04 13:56:40 +0200376 @NonNull TextClassificationSessionId sessionId) {}
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100377
378 /**
379 * Destroys the text classification session identified by the specified sessionId.
380 *
381 * @param sessionId the id of the session to destroy
382 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000383 @MainThread
Jan Althaus39ccc7e2018-04-04 13:56:40 +0200384 public void onDestroyTextClassificationSession(
385 @NonNull TextClassificationSessionId sessionId) {}
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000386
387 /**
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000388 * Returns a TextClassifier that runs in this service's process.
389 * If the local TextClassifier is disabled, this returns {@link TextClassifier#NO_OP}.
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000390 *
391 * @deprecated Use {@link #getDefaultTextClassifierImplementation(Context)} instead.
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000392 */
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000393 @Deprecated
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000394 public final TextClassifier getLocalTextClassifier() {
Tony Makc5a74322020-02-04 17:18:15 +0000395 return TextClassifier.NO_OP;
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000396 }
397
398 /**
399 * Returns the platform's default TextClassifier implementation.
Tony Makc5a74322020-02-04 17:18:15 +0000400 *
401 * @throws RuntimeException if the TextClassifier from
402 * PackageManager#getDefaultTextClassifierPackageName() calls
403 * this method.
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000404 */
Abodunrinwa Toki9aed55e2019-04-10 20:30:31 +0100405 @NonNull
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000406 public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
Tony Mak293bdf32020-02-18 11:33:43 +0000407 final String defaultTextClassifierPackageName =
408 context.getPackageManager().getDefaultTextClassifierPackageName();
409 if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
Tony Makdf5d4bf2020-02-05 11:39:51 +0000410 return TextClassifier.NO_OP;
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000411 }
Tony Mak293bdf32020-02-18 11:33:43 +0000412 if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
413 throw new RuntimeException(
414 "The default text classifier itself should not call the"
415 + "getDefaultTextClassifierImplementation() method.");
Tony Makdf5d4bf2020-02-05 11:39:51 +0000416 }
Tony Mak293bdf32020-02-18 11:33:43 +0000417 final TextClassificationManager tcm =
418 context.getSystemService(TextClassificationManager.class);
419 return tcm.getTextClassifier(TextClassifier.DEFAULT_SYSTEM);
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000420 }
421
Tony Mak9920dbb2019-01-23 19:49:30 +0000422 /** @hide **/
423 public static <T extends Parcelable> T getResponse(Bundle bundle) {
424 return bundle.getParcelable(KEY_RESULT);
425 }
426
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000427 /**
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800428 * Callbacks for TextClassifierService results.
429 *
430 * @param <T> the type of the result
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800431 */
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800432 public interface Callback<T> {
433 /**
434 * Returns the result.
435 */
436 void onSuccess(T result);
437
438 /**
439 * Signals a failure.
440 */
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800441 void onFailure(@NonNull CharSequence error);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800442 }
443
444 /**
Tony Makc5a74322020-02-04 17:18:15 +0000445 * Returns the component name of the textclassifier service from the given package.
446 * Otherwise, returns null.
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800447 *
Tony Makc5a74322020-02-04 17:18:15 +0000448 * @param context
449 * @param packageName the package to look for.
450 * @param resolveFlags the flags that are used by PackageManager to resolve the component name.
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800451 * @hide
452 */
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800453 @Nullable
Tony Makc5a74322020-02-04 17:18:15 +0000454 public static ComponentName getServiceComponentName(
455 Context context, String packageName, int resolveFlags) {
Makoto Onuki700feef2018-02-15 10:59:41 -0800456 final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName);
457
Tony Makc5a74322020-02-04 17:18:15 +0000458 final ResolveInfo ri = context.getPackageManager().resolveService(intent, resolveFlags);
Makoto Onuki700feef2018-02-15 10:59:41 -0800459
460 if ((ri == null) || (ri.serviceInfo == null)) {
Felipe Lemef8192132018-10-12 13:42:40 -0700461 Slog.w(LOG_TAG, String.format("Package or service not found in package %s for user %d",
462 packageName, context.getUserId()));
Makoto Onuki700feef2018-02-15 10:59:41 -0800463 return null;
464 }
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800465
Makoto Onuki700feef2018-02-15 10:59:41 -0800466 final ServiceInfo si = ri.serviceInfo;
467
468 final String permission = si.permission;
469 if (Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE.equals(permission)) {
470 return si.getComponentName();
471 }
472 Slog.w(LOG_TAG, String.format(
473 "Service %s should require %s permission. Found %s permission",
474 si.getComponentName(),
475 Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE,
476 si.permission));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800477 return null;
478 }
Tony Mak9920dbb2019-01-23 19:49:30 +0000479
480 /**
481 * Forwards the callback result to a wrapped binder callback.
482 */
483 private static final class ProxyCallback<T extends Parcelable> implements Callback<T> {
Tony Makcdc81e82019-08-22 15:14:28 +0100484 private ITextClassifierCallback mTextClassifierCallback;
Tony Mak9920dbb2019-01-23 19:49:30 +0000485
486 private ProxyCallback(ITextClassifierCallback textClassifierCallback) {
Tony Makcdc81e82019-08-22 15:14:28 +0100487 mTextClassifierCallback = Preconditions.checkNotNull(textClassifierCallback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000488 }
489
490 @Override
491 public void onSuccess(T result) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000492 try {
493 Bundle bundle = new Bundle(1);
494 bundle.putParcelable(KEY_RESULT, result);
Tony Makcdc81e82019-08-22 15:14:28 +0100495 mTextClassifierCallback.onSuccess(bundle);
Tony Mak9920dbb2019-01-23 19:49:30 +0000496 } catch (RemoteException e) {
497 Slog.d(LOG_TAG, "Error calling callback");
498 }
499 }
500
501 @Override
502 public void onFailure(CharSequence error) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000503 try {
Joanne Chung0b7c2c42019-08-16 16:55:11 +0800504 Slog.w(LOG_TAG, "Request fail: " + error);
Tony Makcdc81e82019-08-22 15:14:28 +0100505 mTextClassifierCallback.onFailure();
Tony Mak9920dbb2019-01-23 19:49:30 +0000506 } catch (RemoteException e) {
507 Slog.d(LOG_TAG, "Error calling callback");
508 }
509 }
510 }
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800511}