blob: bb39be2b2cf0e1baa1c3f7b8f44951d9bfe5b9d9 [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;
Tony Mak9920dbb2019-01-23 19:49:30 +000020import android.annotation.MainThread;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080021import 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;
Makoto Onuki700feef2018-02-15 10:59:41 -080029import android.content.pm.ResolveInfo;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080030import android.content.pm.ServiceInfo;
Tony Mak9920dbb2019-01-23 19:49:30 +000031import android.os.Bundle;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080032import android.os.CancellationSignal;
Tony Mak9920dbb2019-01-23 19:49:30 +000033import android.os.Handler;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080034import android.os.IBinder;
Tony Mak9920dbb2019-01-23 19:49:30 +000035import android.os.Looper;
36import android.os.Parcelable;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080037import android.os.RemoteException;
38import android.text.TextUtils;
39import android.util.Slog;
Tony Mak0be540b2018-11-09 16:58:35 +000040import android.view.textclassifier.ConversationActions;
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +000041import android.view.textclassifier.SelectionEvent;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080042import android.view.textclassifier.TextClassification;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010043import android.view.textclassifier.TextClassificationContext;
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +000044import android.view.textclassifier.TextClassificationManager;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010045import android.view.textclassifier.TextClassificationSessionId;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080046import android.view.textclassifier.TextClassifier;
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +000047import android.view.textclassifier.TextClassifierEvent;
Tony Mak0be540b2018-11-09 16:58:35 +000048import android.view.textclassifier.TextLanguage;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080049import android.view.textclassifier.TextLinks;
50import android.view.textclassifier.TextSelection;
51
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010052import com.android.internal.util.Preconditions;
53
Tony Mak9920dbb2019-01-23 19:49:30 +000054import java.util.concurrent.ExecutorService;
55import java.util.concurrent.Executors;
56
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080057/**
58 * Abstract base class for the TextClassifier service.
59 *
60 * <p>A TextClassifier service provides text classification related features for the system.
61 * The system's default TextClassifierService is configured in
62 * {@code config_defaultTextClassifierService}. If this config has no value, a
63 * {@link android.view.textclassifier.TextClassifierImpl} is loaded in the calling app's process.
64 *
65 * <p>See: {@link TextClassifier}.
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +000066 * See: {@link TextClassificationManager}.
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080067 *
68 * <p>Include the following in the manifest:
69 *
70 * <pre>
71 * {@literal
72 * <service android:name=".YourTextClassifierService"
73 * android:permission="android.permission.BIND_TEXTCLASSIFIER_SERVICE">
74 * <intent-filter>
75 * <action android:name="android.service.textclassifier.TextClassifierService" />
76 * </intent-filter>
77 * </service>}</pre>
78 *
Tony Mak9920dbb2019-01-23 19:49:30 +000079 * <p>From {@link android.os.Build.VERSION_CODES#Q} onward, all callbacks are called on the main
80 * thread. Prior to Q, there is no guarantee on what thread the callback will happen. You should
81 * make sure the callbacks are executed in your desired thread by using a executor, a handler or
82 * something else along the line.
83 *
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080084 * @see TextClassifier
85 * @hide
86 */
87@SystemApi
88public abstract class TextClassifierService extends Service {
89
90 private static final String LOG_TAG = "TextClassifierService";
91
92 /**
93 * The {@link Intent} that must be declared as handled by the service.
94 * To be supported, the service must also require the
95 * {@link android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE} permission so
96 * that other applications can not abuse it.
97 */
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080098 public static final String SERVICE_INTERFACE =
99 "android.service.textclassifier.TextClassifierService";
100
Tony Mak9920dbb2019-01-23 19:49:30 +0000101 /** @hide **/
102 private static final String KEY_RESULT = "key_result";
103
104 private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper(), null, true);
105 private final ExecutorService mSingleThreadExecutor = Executors.newSingleThreadExecutor();
106
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800107 private final ITextClassifierService.Stub mBinder = new ITextClassifierService.Stub() {
108
109 // TODO(b/72533911): Implement cancellation signal
110 @NonNull private final CancellationSignal mCancellationSignal = new CancellationSignal();
111
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800112 @Override
113 public void onSuggestSelection(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100114 TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000115 TextSelection.Request request, ITextClassifierCallback callback) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100116 Preconditions.checkNotNull(request);
117 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000118 mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestSelection(
119 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800120
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800121 }
122
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800123 @Override
124 public void onClassifyText(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100125 TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000126 TextClassification.Request request, ITextClassifierCallback callback) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100127 Preconditions.checkNotNull(request);
128 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000129 mMainThreadHandler.post(() -> TextClassifierService.this.onClassifyText(
130 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800131 }
132
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800133 @Override
134 public void onGenerateLinks(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100135 TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000136 TextLinks.Request request, ITextClassifierCallback callback) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100137 Preconditions.checkNotNull(request);
138 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000139 mMainThreadHandler.post(() -> TextClassifierService.this.onGenerateLinks(
140 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800141 }
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000142
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000143 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100144 public void onSelectionEvent(
145 TextClassificationSessionId sessionId,
Tony Mak0be540b2018-11-09 16:58:35 +0000146 SelectionEvent event) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100147 Preconditions.checkNotNull(event);
Tony Mak9920dbb2019-01-23 19:49:30 +0000148 mMainThreadHandler.post(
149 () -> TextClassifierService.this.onSelectionEvent(sessionId, event));
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100150 }
151
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100152 @Override
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000153 public void onTextClassifierEvent(
154 TextClassificationSessionId sessionId,
155 TextClassifierEvent event) {
156 Preconditions.checkNotNull(event);
Tony Mak9920dbb2019-01-23 19:49:30 +0000157 mMainThreadHandler.post(
158 () -> TextClassifierService.this.onTextClassifierEvent(sessionId, event));
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000159 }
160
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000161 @Override
Tony Mak0be540b2018-11-09 16:58:35 +0000162 public void onDetectLanguage(
163 TextClassificationSessionId sessionId,
164 TextLanguage.Request request,
Tony Mak9920dbb2019-01-23 19:49:30 +0000165 ITextClassifierCallback callback) {
Tony Mak0be540b2018-11-09 16:58:35 +0000166 Preconditions.checkNotNull(request);
167 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000168 mMainThreadHandler.post(() -> TextClassifierService.this.onDetectLanguage(
169 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Tony Mak0be540b2018-11-09 16:58:35 +0000170 }
171
Tony Mak0be540b2018-11-09 16:58:35 +0000172 @Override
173 public void onSuggestConversationActions(
174 TextClassificationSessionId sessionId,
175 ConversationActions.Request request,
Tony Mak9920dbb2019-01-23 19:49:30 +0000176 ITextClassifierCallback callback) {
Tony Mak0be540b2018-11-09 16:58:35 +0000177 Preconditions.checkNotNull(request);
178 Preconditions.checkNotNull(callback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000179 mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestConversationActions(
180 sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
Tony Mak0be540b2018-11-09 16:58:35 +0000181 }
182
Tony Mak0be540b2018-11-09 16:58:35 +0000183 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100184 public void onCreateTextClassificationSession(
Tony Mak0be540b2018-11-09 16:58:35 +0000185 TextClassificationContext context, TextClassificationSessionId sessionId) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100186 Preconditions.checkNotNull(context);
187 Preconditions.checkNotNull(sessionId);
Tony Mak9920dbb2019-01-23 19:49:30 +0000188 mMainThreadHandler.post(
189 () -> TextClassifierService.this.onCreateTextClassificationSession(
190 context, sessionId));
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100191 }
192
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100193 @Override
Tony Mak0be540b2018-11-09 16:58:35 +0000194 public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000195 mMainThreadHandler.post(
196 () -> TextClassifierService.this.onDestroyTextClassificationSession(sessionId));
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000197 }
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800198 };
199
200 @Nullable
201 @Override
202 public final IBinder onBind(Intent intent) {
203 if (SERVICE_INTERFACE.equals(intent.getAction())) {
204 return mBinder;
205 }
206 return null;
207 }
208
209 /**
210 * Returns suggested text selection start and end indices, recognized entity types, and their
211 * associated confidence scores. The entity types are ordered from highest to lowest scoring.
212 *
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100213 * @param sessionId the session id
214 * @param request the text selection request
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800215 * @param cancellationSignal object to watch for canceling the current operation
216 * @param callback the callback to return the result to
217 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000218 @MainThread
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800219 public abstract void onSuggestSelection(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100220 @Nullable TextClassificationSessionId sessionId,
221 @NonNull TextSelection.Request request,
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800222 @NonNull CancellationSignal cancellationSignal,
223 @NonNull Callback<TextSelection> callback);
224
225 /**
226 * Classifies the specified text and returns a {@link TextClassification} object that can be
227 * used to generate a widget for handling the classified text.
228 *
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100229 * @param sessionId the session id
230 * @param request the text classification request
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800231 * @param cancellationSignal object to watch for canceling the current operation
232 * @param callback the callback to return the result to
233 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000234 @MainThread
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800235 public abstract void onClassifyText(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100236 @Nullable TextClassificationSessionId sessionId,
237 @NonNull TextClassification.Request request,
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800238 @NonNull CancellationSignal cancellationSignal,
239 @NonNull Callback<TextClassification> callback);
240
241 /**
242 * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
243 * links information.
244 *
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100245 * @param sessionId the session id
246 * @param request the text classification request
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800247 * @param cancellationSignal object to watch for canceling the current operation
248 * @param callback the callback to return the result to
249 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000250 @MainThread
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800251 public abstract void onGenerateLinks(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100252 @Nullable TextClassificationSessionId sessionId,
253 @NonNull TextLinks.Request request,
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800254 @NonNull CancellationSignal cancellationSignal,
255 @NonNull Callback<TextLinks> callback);
256
257 /**
Tony Mak0be540b2018-11-09 16:58:35 +0000258 * Detects and returns the language of the give text.
259 *
260 * @param sessionId the session id
261 * @param request the language detection request
262 * @param cancellationSignal object to watch for canceling the current operation
263 * @param callback the callback to return the result to
264 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000265 @MainThread
Tony Mak0be540b2018-11-09 16:58:35 +0000266 public void onDetectLanguage(
267 @Nullable TextClassificationSessionId sessionId,
268 @NonNull TextLanguage.Request request,
269 @NonNull CancellationSignal cancellationSignal,
270 @NonNull Callback<TextLanguage> callback) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000271 mSingleThreadExecutor.submit(() ->
272 callback.onSuccess(getLocalTextClassifier().detectLanguage(request)));
Tony Mak0be540b2018-11-09 16:58:35 +0000273 }
274
275 /**
276 * Suggests and returns a list of actions according to the given conversation.
277 *
278 * @param sessionId the session id
279 * @param request the conversation actions request
280 * @param cancellationSignal object to watch for canceling the current operation
281 * @param callback the callback to return the result to
282 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000283 @MainThread
Tony Mak0be540b2018-11-09 16:58:35 +0000284 public void onSuggestConversationActions(
285 @Nullable TextClassificationSessionId sessionId,
286 @NonNull ConversationActions.Request request,
287 @NonNull CancellationSignal cancellationSignal,
288 @NonNull Callback<ConversationActions> callback) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000289 mSingleThreadExecutor.submit(() ->
290 callback.onSuccess(getLocalTextClassifier().suggestConversationActions(request)));
Tony Mak0be540b2018-11-09 16:58:35 +0000291 }
292
293 /**
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000294 * Writes the selection event.
295 * This is called when a selection event occurs. e.g. user changed selection; or smart selection
296 * happened.
297 *
298 * <p>The default implementation ignores the event.
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100299 *
300 * @param sessionId the session id
301 * @param event the selection event
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000302 * @deprecated
303 * Use {@link #onTextClassifierEvent(TextClassificationSessionId, TextClassifierEvent)}
304 * instead
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000305 */
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000306 @Deprecated
Tony Mak9920dbb2019-01-23 19:49:30 +0000307 @MainThread
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100308 public void onSelectionEvent(
309 @Nullable TextClassificationSessionId sessionId, @NonNull SelectionEvent event) {}
310
311 /**
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000312 * Writes the TextClassifier event.
313 * This is called when a TextClassifier event occurs. e.g. user changed selection,
314 * smart selection happened, or a link was clicked.
315 *
316 * <p>The default implementation ignores the event.
317 *
318 * @param sessionId the session id
319 * @param event the TextClassifier event
320 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000321 @MainThread
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000322 public void onTextClassifierEvent(
323 @Nullable TextClassificationSessionId sessionId, @NonNull TextClassifierEvent event) {}
324
325 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100326 * Creates a new text classification session for the specified context.
327 *
328 * @param context the text classification context
329 * @param sessionId the session's Id
330 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000331 @MainThread
Jan Althaus39ccc7e2018-04-04 13:56:40 +0200332 public void onCreateTextClassificationSession(
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100333 @NonNull TextClassificationContext context,
Jan Althaus39ccc7e2018-04-04 13:56:40 +0200334 @NonNull TextClassificationSessionId sessionId) {}
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100335
336 /**
337 * Destroys the text classification session identified by the specified sessionId.
338 *
339 * @param sessionId the id of the session to destroy
340 */
Tony Mak9920dbb2019-01-23 19:49:30 +0000341 @MainThread
Jan Althaus39ccc7e2018-04-04 13:56:40 +0200342 public void onDestroyTextClassificationSession(
343 @NonNull TextClassificationSessionId sessionId) {}
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000344
345 /**
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000346 * Returns a TextClassifier that runs in this service's process.
347 * If the local TextClassifier is disabled, this returns {@link TextClassifier#NO_OP}.
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000348 *
349 * @deprecated Use {@link #getDefaultTextClassifierImplementation(Context)} instead.
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000350 */
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000351 @Deprecated
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000352 public final TextClassifier getLocalTextClassifier() {
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000353 // Deprecated: In the future, we may not guarantee that this runs in the service's process.
354 return getDefaultTextClassifierImplementation(this);
355 }
356
357 /**
358 * Returns the platform's default TextClassifier implementation.
359 */
Abodunrinwa Toki9aed55e2019-04-10 20:30:31 +0100360 @NonNull
Abodunrinwa Toki324572e2019-02-12 19:01:01 +0000361 public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
362 final TextClassificationManager tcm =
363 context.getSystemService(TextClassificationManager.class);
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000364 if (tcm != null) {
365 return tcm.getTextClassifier(TextClassifier.LOCAL);
366 }
367 return TextClassifier.NO_OP;
368 }
369
Tony Mak9920dbb2019-01-23 19:49:30 +0000370 /** @hide **/
371 public static <T extends Parcelable> T getResponse(Bundle bundle) {
372 return bundle.getParcelable(KEY_RESULT);
373 }
374
Abodunrinwa Tokie6d974a2018-03-06 18:18:30 +0000375 /**
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800376 * Callbacks for TextClassifierService results.
377 *
378 * @param <T> the type of the result
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800379 */
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800380 public interface Callback<T> {
381 /**
382 * Returns the result.
383 */
384 void onSuccess(T result);
385
386 /**
387 * Signals a failure.
388 */
389 void onFailure(CharSequence error);
390 }
391
392 /**
393 * Returns the component name of the system default textclassifier service if it can be found
394 * on the system. Otherwise, returns null.
395 * @hide
396 */
397 @Nullable
398 public static ComponentName getServiceComponentName(Context context) {
Makoto Onuki700feef2018-02-15 10:59:41 -0800399 final String packageName = context.getPackageManager().getSystemTextClassifierPackageName();
400 if (TextUtils.isEmpty(packageName)) {
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800401 Slog.d(LOG_TAG, "No configured system TextClassifierService");
Makoto Onuki700feef2018-02-15 10:59:41 -0800402 return null;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800403 }
Makoto Onuki700feef2018-02-15 10:59:41 -0800404
405 final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName);
406
407 final ResolveInfo ri = context.getPackageManager().resolveService(intent,
408 PackageManager.MATCH_SYSTEM_ONLY);
409
410 if ((ri == null) || (ri.serviceInfo == null)) {
Felipe Lemef8192132018-10-12 13:42:40 -0700411 Slog.w(LOG_TAG, String.format("Package or service not found in package %s for user %d",
412 packageName, context.getUserId()));
Makoto Onuki700feef2018-02-15 10:59:41 -0800413 return null;
414 }
415 final ServiceInfo si = ri.serviceInfo;
416
417 final String permission = si.permission;
418 if (Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE.equals(permission)) {
419 return si.getComponentName();
420 }
421 Slog.w(LOG_TAG, String.format(
422 "Service %s should require %s permission. Found %s permission",
423 si.getComponentName(),
424 Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE,
425 si.permission));
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800426 return null;
427 }
Tony Mak9920dbb2019-01-23 19:49:30 +0000428
429 /**
430 * Forwards the callback result to a wrapped binder callback.
431 */
432 private static final class ProxyCallback<T extends Parcelable> implements Callback<T> {
Tony Mak7ba3fd42019-08-14 20:38:06 +0100433 private ITextClassifierCallback mTextClassifierCallback;
Tony Mak9920dbb2019-01-23 19:49:30 +0000434
435 private ProxyCallback(ITextClassifierCallback textClassifierCallback) {
Tony Mak7ba3fd42019-08-14 20:38:06 +0100436 mTextClassifierCallback = Preconditions.checkNotNull(textClassifierCallback);
Tony Mak9920dbb2019-01-23 19:49:30 +0000437 }
438
439 @Override
440 public void onSuccess(T result) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000441 try {
442 Bundle bundle = new Bundle(1);
443 bundle.putParcelable(KEY_RESULT, result);
Tony Mak7ba3fd42019-08-14 20:38:06 +0100444 mTextClassifierCallback.onSuccess(bundle);
Tony Mak9920dbb2019-01-23 19:49:30 +0000445 } catch (RemoteException e) {
446 Slog.d(LOG_TAG, "Error calling callback");
447 }
448 }
449
450 @Override
451 public void onFailure(CharSequence error) {
Tony Mak9920dbb2019-01-23 19:49:30 +0000452 try {
Tony Mak7ba3fd42019-08-14 20:38:06 +0100453 mTextClassifierCallback.onFailure();
Tony Mak9920dbb2019-01-23 19:49:30 +0000454 } catch (RemoteException e) {
455 Slog.d(LOG_TAG, "Error calling callback");
456 }
457 }
458 }
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800459}