blob: 816452679c45058421665a8846925268e6ebfc99 [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 com.android.server.textclassifier;
18
Eugene Susla7b24b2b2018-03-16 14:33:31 -070019import android.annotation.NonNull;
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +000020import android.annotation.Nullable;
Eugene Susla7b24b2b2018-03-16 14:33:31 -070021import android.annotation.UserIdInt;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080022import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.ServiceConnection;
Joanne Chungb50ab4b2019-10-14 16:40:57 +080026import android.content.pm.PackageManager;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080027import android.os.Binder;
Tony Mak6e1d5ad2020-01-16 14:18:22 +000028import android.os.Bundle;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080029import android.os.IBinder;
Joanne Chungb50ab4b2019-10-14 16:40:57 +080030import android.os.Process;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080031import android.os.RemoteException;
Eugene Susla7b24b2b2018-03-16 14:33:31 -070032import android.os.UserHandle;
Joanne Chungb50ab4b2019-10-14 16:40:57 +080033import android.provider.DeviceConfig;
Tony Mak9920dbb2019-01-23 19:49:30 +000034import android.service.textclassifier.ITextClassifierCallback;
Eugene Susla7b24b2b2018-03-16 14:33:31 -070035import android.service.textclassifier.ITextClassifierService;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080036import android.service.textclassifier.TextClassifierService;
Joanne Chung0b7c0d42019-10-18 21:02:26 +080037import android.service.textclassifier.TextClassifierService.ConnectionState;
Joanne Chungb50ab4b2019-10-14 16:40:57 +080038import android.text.TextUtils;
Tony Mak53195e02020-02-06 14:52:57 +000039import android.util.ArrayMap;
Eugene Susla7b24b2b2018-03-16 14:33:31 -070040import android.util.Slog;
41import android.util.SparseArray;
Tony Mak0be540b2018-11-09 16:58:35 +000042import android.view.textclassifier.ConversationActions;
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +000043import android.view.textclassifier.SelectionEvent;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080044import android.view.textclassifier.TextClassification;
Joanne Chungb50ab4b2019-10-14 16:40:57 +080045import android.view.textclassifier.TextClassificationConstants;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010046import android.view.textclassifier.TextClassificationContext;
Tony Makf93e9e52018-07-16 14:46:29 +020047import android.view.textclassifier.TextClassificationManager;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010048import android.view.textclassifier.TextClassificationSessionId;
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
54import com.android.internal.annotations.GuardedBy;
Tony Makf93e9e52018-07-16 14:46:29 +020055import com.android.internal.util.DumpUtils;
Eugene Susla7b24b2b2018-03-16 14:33:31 -070056import com.android.internal.util.FunctionalUtils;
Tony Mak6e1d5ad2020-01-16 14:18:22 +000057import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
Eugene Susla7b24b2b2018-03-16 14:33:31 -070058import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
Tony Makf93e9e52018-07-16 14:46:29 +020059import com.android.internal.util.IndentingPrintWriter;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080060import com.android.internal.util.Preconditions;
61import com.android.server.SystemService;
62
Tony Makf93e9e52018-07-16 14:46:29 +020063import java.io.FileDescriptor;
64import java.io.PrintWriter;
Eugene Susla7b24b2b2018-03-16 14:33:31 -070065import java.util.ArrayDeque;
Tony Makc5a74322020-02-04 17:18:15 +000066import java.util.ArrayList;
67import java.util.List;
Tony Mak53195e02020-02-06 14:52:57 +000068import java.util.Map;
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +000069import java.util.Objects;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080070import java.util.Queue;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080071
72/**
73 * A manager for TextClassifier services.
74 * Apps bind to the TextClassificationManagerService for text classification. This service
75 * reroutes calls to it to a {@link TextClassifierService} that it manages.
76 */
77public final class TextClassificationManagerService extends ITextClassifierService.Stub {
78
79 private static final String LOG_TAG = "TextClassificationManagerService";
80
Tony Mak6e1d5ad2020-01-16 14:18:22 +000081 private static final ITextClassifierCallback NO_OP_CALLBACK = new ITextClassifierCallback() {
82 @Override
83 public void onSuccess(Bundle result) {}
84
85 @Override
86 public void onFailure() {}
87
88 @Override
89 public IBinder asBinder() {
90 return null;
91 }
92 };
93
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080094 public static final class Lifecycle extends SystemService {
95
96 private final TextClassificationManagerService mManagerService;
97
98 public Lifecycle(Context context) {
99 super(context);
100 mManagerService = new TextClassificationManagerService(context);
101 }
102
103 @Override
104 public void onStart() {
105 try {
106 publishBinderService(Context.TEXT_CLASSIFICATION_SERVICE, mManagerService);
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800107 mManagerService.startListenSettings();
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800108 } catch (Throwable t) {
109 // Starting this service is not critical to the running of this device and should
110 // therefore not crash the device. If it fails, log the error and continue.
111 Slog.e(LOG_TAG, "Could not start the TextClassificationManagerService.", t);
112 }
113 }
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700114
115 @Override
116 public void onStartUser(int userId) {
117 processAnyPendingWork(userId);
118 }
119
120 @Override
121 public void onUnlockUser(int userId) {
122 // Rebind if we failed earlier due to locked encrypted user
123 processAnyPendingWork(userId);
124 }
125
126 private void processAnyPendingWork(int userId) {
127 synchronized (mManagerService.mLock) {
128 mManagerService.getUserStateLocked(userId).bindIfHasPendingRequestsLocked();
129 }
130 }
131
132 @Override
133 public void onStopUser(int userId) {
134 synchronized (mManagerService.mLock) {
135 UserState userState = mManagerService.peekUserStateLocked(userId);
136 if (userState != null) {
Tony Makc5a74322020-02-04 17:18:15 +0000137 userState.cleanupServiceLocked();
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700138 mManagerService.mUserStates.remove(userId);
139 }
140 }
141 }
142
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800143 }
144
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800145 private final TextClassifierSettingsListener mSettingsListener;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800146 private final Context mContext;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800147 private final Object mLock;
148 @GuardedBy("mLock")
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700149 final SparseArray<UserState> mUserStates = new SparseArray<>();
Tony Mak53195e02020-02-06 14:52:57 +0000150 private final SessionCache mSessionCache;
Tony Makc5a74322020-02-04 17:18:15 +0000151 private final TextClassificationConstants mSettings;
152 @Nullable
153 private final String mDefaultTextClassifierPackage;
154 @Nullable
155 private final String mSystemTextClassifierPackage;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800156
157 private TextClassificationManagerService(Context context) {
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000158 mContext = Objects.requireNonNull(context);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800159 mLock = new Object();
Tony Makc5a74322020-02-04 17:18:15 +0000160 mSettings = new TextClassificationConstants();
Joanne Chungc44f5292019-12-02 21:18:43 +0800161 mSettingsListener = new TextClassifierSettingsListener(mContext);
Tony Makc5a74322020-02-04 17:18:15 +0000162 PackageManager packageManager = mContext.getPackageManager();
163 mDefaultTextClassifierPackage = packageManager.getDefaultTextClassifierPackageName();
164 mSystemTextClassifierPackage = packageManager.getSystemTextClassifierPackageName();
Tony Mak53195e02020-02-06 14:52:57 +0000165 mSessionCache = new SessionCache(mLock);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800166 }
167
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800168 private void startListenSettings() {
169 mSettingsListener.registerObserver();
170 }
171
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800172 @Override
Joanne Chung0b7c0d42019-10-18 21:02:26 +0800173 public void onConnectedStateChanged(@ConnectionState int connected) {
174 }
175
176 @Override
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800177 public void onSuggestSelection(
Abodunrinwa Tokie8492692019-07-01 19:41:44 +0100178 @Nullable TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000179 TextSelection.Request request, ITextClassifierCallback callback)
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800180 throws RemoteException {
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000181 Objects.requireNonNull(request);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800182
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000183 handleRequest(
184 request.getUserId(),
185 request.getCallingPackageName(),
186 /* attemptToBind= */ true,
Tony Makc5a74322020-02-04 17:18:15 +0000187 request.getUseDefaultTextClassifier(),
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000188 service -> service.onSuggestSelection(sessionId, request, callback),
189 "onSuggestSelection",
190 callback);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800191 }
192
193 @Override
194 public void onClassifyText(
Abodunrinwa Tokie8492692019-07-01 19:41:44 +0100195 @Nullable TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000196 TextClassification.Request request, ITextClassifierCallback callback)
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800197 throws RemoteException {
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000198 Objects.requireNonNull(request);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800199
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000200 handleRequest(
201 request.getUserId(),
202 request.getCallingPackageName(),
203 /* attemptToBind= */ true,
Tony Makc5a74322020-02-04 17:18:15 +0000204 request.getUseDefaultTextClassifier(),
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000205 service -> service.onClassifyText(sessionId, request, callback),
206 "onClassifyText",
207 callback);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800208 }
209
210 @Override
211 public void onGenerateLinks(
Abodunrinwa Tokie8492692019-07-01 19:41:44 +0100212 @Nullable TextClassificationSessionId sessionId,
Tony Mak9920dbb2019-01-23 19:49:30 +0000213 TextLinks.Request request, ITextClassifierCallback callback)
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800214 throws RemoteException {
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000215 Objects.requireNonNull(request);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800216
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000217 handleRequest(
218 request.getUserId(),
219 request.getCallingPackageName(),
220 /* attemptToBind= */ true,
Tony Makc5a74322020-02-04 17:18:15 +0000221 request.getUseDefaultTextClassifier(),
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000222 service -> service.onGenerateLinks(sessionId, request, callback),
223 "onGenerateLinks",
224 callback);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800225 }
226
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000227 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100228 public void onSelectionEvent(
Abodunrinwa Tokie8492692019-07-01 19:41:44 +0100229 @Nullable TextClassificationSessionId sessionId, SelectionEvent event)
230 throws RemoteException {
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000231 Objects.requireNonNull(event);
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000232
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000233 handleRequest(
234 event.getUserId(),
Tony Makc5a74322020-02-04 17:18:15 +0000235 /* callingPackageName= */ null,
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000236 /* attemptToBind= */ false,
Tony Makc5a74322020-02-04 17:18:15 +0000237 event.getUseDefaultTextClassifier(),
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000238 service -> service.onSelectionEvent(sessionId, event),
239 "onSelectionEvent",
240 NO_OP_CALLBACK);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100241 }
Tony Makc5a74322020-02-04 17:18:15 +0000242
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000243 @Override
244 public void onTextClassifierEvent(
Abodunrinwa Tokie8492692019-07-01 19:41:44 +0100245 @Nullable TextClassificationSessionId sessionId,
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000246 TextClassifierEvent event) throws RemoteException {
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000247 Objects.requireNonNull(event);
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000248
Abodunrinwa Tokie8492692019-07-01 19:41:44 +0100249 final int userId = event.getEventContext() == null
250 ? UserHandle.getCallingUserId()
251 : event.getEventContext().getUserId();
Tony Makc5a74322020-02-04 17:18:15 +0000252 final boolean useDefaultTextClassifier =
253 event.getEventContext() != null
254 ? event.getEventContext().getUseDefaultTextClassifier()
255 : true;
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000256 handleRequest(
257 userId,
Tony Makc5a74322020-02-04 17:18:15 +0000258 /* callingPackageName= */ null,
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000259 /* attemptToBind= */ false,
Tony Makc5a74322020-02-04 17:18:15 +0000260 useDefaultTextClassifier,
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000261 service -> service.onTextClassifierEvent(sessionId, event),
262 "onTextClassifierEvent",
263 NO_OP_CALLBACK);
Abodunrinwa Toki37ccedc2018-12-11 00:35:11 +0000264 }
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100265
266 @Override
Tony Mak0be540b2018-11-09 16:58:35 +0000267 public void onDetectLanguage(
Abodunrinwa Tokie8492692019-07-01 19:41:44 +0100268 @Nullable TextClassificationSessionId sessionId,
Tony Mak0be540b2018-11-09 16:58:35 +0000269 TextLanguage.Request request,
Tony Mak9920dbb2019-01-23 19:49:30 +0000270 ITextClassifierCallback callback) throws RemoteException {
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000271 Objects.requireNonNull(request);
Tony Mak0be540b2018-11-09 16:58:35 +0000272
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000273 handleRequest(
274 request.getUserId(),
275 request.getCallingPackageName(),
276 /* attemptToBind= */ true,
Tony Makc5a74322020-02-04 17:18:15 +0000277 request.getUseDefaultTextClassifier(),
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000278 service -> service.onDetectLanguage(sessionId, request, callback),
279 "onDetectLanguage",
280 callback);
Tony Mak0be540b2018-11-09 16:58:35 +0000281 }
282
283 @Override
284 public void onSuggestConversationActions(
Abodunrinwa Tokie8492692019-07-01 19:41:44 +0100285 @Nullable TextClassificationSessionId sessionId,
Tony Mak0be540b2018-11-09 16:58:35 +0000286 ConversationActions.Request request,
Tony Mak9920dbb2019-01-23 19:49:30 +0000287 ITextClassifierCallback callback) throws RemoteException {
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000288 Objects.requireNonNull(request);
Tony Mak0be540b2018-11-09 16:58:35 +0000289
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000290 handleRequest(
291 request.getUserId(),
292 request.getCallingPackageName(),
293 /* attemptToBind= */ true,
Tony Makc5a74322020-02-04 17:18:15 +0000294 request.getUseDefaultTextClassifier(),
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000295 service -> service.onSuggestConversationActions(sessionId, request, callback),
296 "onSuggestConversationActions",
297 callback);
Tony Mak0be540b2018-11-09 16:58:35 +0000298 }
299
300 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100301 public void onCreateTextClassificationSession(
302 TextClassificationContext classificationContext, TextClassificationSessionId sessionId)
303 throws RemoteException {
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000304 Objects.requireNonNull(sessionId);
305 Objects.requireNonNull(classificationContext);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100306
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000307 final int userId = classificationContext.getUserId();
308 handleRequest(
309 userId,
310 classificationContext.getPackageName(),
311 /* attemptToBind= */ false,
Tony Makc5a74322020-02-04 17:18:15 +0000312 classificationContext.getUseDefaultTextClassifier(),
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000313 service -> {
314 service.onCreateTextClassificationSession(classificationContext, sessionId);
Tony Mak53195e02020-02-06 14:52:57 +0000315 mSessionCache.put(sessionId, classificationContext);
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000316 },
317 "onCreateTextClassificationSession",
318 NO_OP_CALLBACK);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100319 }
320
321 @Override
322 public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId)
323 throws RemoteException {
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000324 Objects.requireNonNull(sessionId);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100325
326 synchronized (mLock) {
Tony Mak53195e02020-02-06 14:52:57 +0000327 final StrippedTextClassificationContext textClassificationContext =
328 mSessionCache.get(sessionId);
Tony Makc5a74322020-02-04 17:18:15 +0000329 final int userId = textClassificationContext != null
Tony Mak53195e02020-02-06 14:52:57 +0000330 ? textClassificationContext.userId
Abodunrinwa Tokie8492692019-07-01 19:41:44 +0100331 : UserHandle.getCallingUserId();
Tony Makc5a74322020-02-04 17:18:15 +0000332 final boolean useDefaultTextClassifier =
333 textClassificationContext != null
Tony Mak53195e02020-02-06 14:52:57 +0000334 ? textClassificationContext.useDefaultTextClassifier
Tony Makc5a74322020-02-04 17:18:15 +0000335 : true;
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000336 handleRequest(
337 userId,
338 /* callingPackageName= */ null,
339 /* attemptToBind= */ false,
Tony Makc5a74322020-02-04 17:18:15 +0000340 useDefaultTextClassifier,
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000341 service -> {
342 service.onDestroyTextClassificationSession(sessionId);
Tony Mak53195e02020-02-06 14:52:57 +0000343 mSessionCache.remove(sessionId);
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000344 },
345 "onDestroyTextClassificationSession",
346 NO_OP_CALLBACK);
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000347 }
348 }
349
Andreas Gamped6d42062018-07-20 13:08:21 -0700350 @GuardedBy("mLock")
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700351 private UserState getUserStateLocked(int userId) {
352 UserState result = mUserStates.get(userId);
353 if (result == null) {
Tony Makc5a74322020-02-04 17:18:15 +0000354 result = new UserState(userId);
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700355 mUserStates.put(userId, result);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800356 }
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700357 return result;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800358 }
359
Andreas Gamped6d42062018-07-20 13:08:21 -0700360 @GuardedBy("mLock")
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700361 UserState peekUserStateLocked(int userId) {
362 return mUserStates.get(userId);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800363 }
364
Tony Makc5a74322020-02-04 17:18:15 +0000365 private int resolvePackageToUid(@Nullable String packageName, @UserIdInt int userId) {
366 if (packageName == null) {
367 return Process.INVALID_UID;
368 }
369 final PackageManager pm = mContext.getPackageManager();
370 try {
371 return pm.getPackageUidAsUser(packageName, userId);
372 } catch (PackageManager.NameNotFoundException e) {
373 Slog.e(LOG_TAG, "Could not get the UID for " + packageName);
374 }
375 return Process.INVALID_UID;
376 }
377
Tony Makf93e9e52018-07-16 14:46:29 +0200378 @Override
379 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
380 if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
381 IndentingPrintWriter pw = new IndentingPrintWriter(fout, " ");
Felipe Lemef8192132018-10-12 13:42:40 -0700382
Tony Mak384b8a692019-09-26 20:39:03 +0100383 // Create a TCM instance with the system server identity. TCM creates a ContentObserver
384 // to listen for settings changes. It does not pass the checkContentProviderAccess check
385 // if we are using the shell identity, because AMS does not track of processes spawn from
386 // shell.
387 Binder.withCleanCallingIdentity(
388 () -> mContext.getSystemService(TextClassificationManager.class).dump(pw));
389
390 pw.printPair("context", mContext);
391 pw.println();
Tony Makc5a74322020-02-04 17:18:15 +0000392 pw.printPair("defaultTextClassifierPackage", mDefaultTextClassifierPackage);
393 pw.println();
394 pw.printPair("systemTextClassifierPackage", mSystemTextClassifierPackage);
395 pw.println();
Felipe Lemef8192132018-10-12 13:42:40 -0700396 synchronized (mLock) {
397 int size = mUserStates.size();
Tony Makc5a74322020-02-04 17:18:15 +0000398 pw.print("Number user states: ");
399 pw.println(size);
Felipe Lemef8192132018-10-12 13:42:40 -0700400 if (size > 0) {
401 for (int i = 0; i < size; i++) {
402 pw.increaseIndent();
403 UserState userState = mUserStates.valueAt(i);
Tony Makc5a74322020-02-04 17:18:15 +0000404 pw.printPair("User", mUserStates.keyAt(i));
405 pw.println();
406 userState.dump(pw);
Felipe Lemef8192132018-10-12 13:42:40 -0700407 pw.decreaseIndent();
408 }
409 }
Tony Mak53195e02020-02-06 14:52:57 +0000410 pw.println("Number of active sessions: " + mSessionCache.size());
Felipe Lemef8192132018-10-12 13:42:40 -0700411 }
Tony Makf93e9e52018-07-16 14:46:29 +0200412 }
413
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000414 private void handleRequest(
415 @UserIdInt int userId,
416 @Nullable String callingPackageName,
417 boolean attemptToBind,
Tony Makc5a74322020-02-04 17:18:15 +0000418 boolean useDefaultTextClassifier,
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000419 @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
420 @NonNull String methodName,
Tony Makc5a74322020-02-04 17:18:15 +0000421 @NonNull ITextClassifierCallback callback) throws RemoteException {
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000422 Objects.requireNonNull(textClassifierServiceConsumer);
423 Objects.requireNonNull(methodName);
424 Objects.requireNonNull(callback);
425
Tony Makc5a74322020-02-04 17:18:15 +0000426 try {
427 validateCallingPackage(callingPackageName);
428 validateUser(userId);
429 } catch (Exception e) {
430 throw new RemoteException("Invalid request: " + e.getMessage(), e,
431 /* enableSuppression */ true, /* writableStackTrace */ true);
432 }
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000433 synchronized (mLock) {
434 UserState userState = getUserStateLocked(userId);
Tony Makc5a74322020-02-04 17:18:15 +0000435 ServiceState serviceState =
436 userState.getServiceStateLocked(useDefaultTextClassifier);
437 if (serviceState == null) {
438 Slog.d(LOG_TAG, "No configured system TextClassifierService");
439 callback.onFailure();
440 } else if (attemptToBind && !serviceState.bindLocked()) {
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000441 Slog.d(LOG_TAG, "Unable to bind TextClassifierService at " + methodName);
442 callback.onFailure();
Tony Makc5a74322020-02-04 17:18:15 +0000443 } else if (serviceState.isBoundLocked()) {
444 if (!serviceState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) {
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000445 return;
446 }
Tony Makc5a74322020-02-04 17:18:15 +0000447 textClassifierServiceConsumer.accept(serviceState.mService);
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000448 } else {
Tony Makc5a74322020-02-04 17:18:15 +0000449 serviceState.mPendingRequests.add(
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000450 new PendingRequest(
451 methodName,
Tony Makc5a74322020-02-04 17:18:15 +0000452 () -> textClassifierServiceConsumer.accept(serviceState.mService),
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000453 callback::onFailure, callback.asBinder(),
454 this,
Tony Makc5a74322020-02-04 17:18:15 +0000455 serviceState,
Tony Mak6e1d5ad2020-01-16 14:18:22 +0000456 Binder.getCallingUid()));
457 }
458 }
459 }
460
Tony Makc5a74322020-02-04 17:18:15 +0000461 private void onTextClassifierServicePackageOverrideChanged(String overriddenPackage) {
Joanne Chungc44f5292019-12-02 21:18:43 +0800462 synchronized (mLock) {
463 final int size = mUserStates.size();
464 for (int i = 0; i < size; i++) {
465 UserState userState = mUserStates.valueAt(i);
Tony Makc5a74322020-02-04 17:18:15 +0000466 userState.onTextClassifierServicePackageOverrideChangedLocked(overriddenPackage);
Joanne Chungc44f5292019-12-02 21:18:43 +0800467 }
468 }
469 }
470
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700471 private static final class PendingRequest implements IBinder.DeathRecipient {
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800472
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800473 private final int mUid;
Tony Makc5a74322020-02-04 17:18:15 +0000474 @Nullable
475 private final String mName;
476 @Nullable
477 private final IBinder mBinder;
478 @NonNull
479 private final Runnable mRequest;
480 @Nullable
481 private final Runnable mOnServiceFailure;
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700482 @GuardedBy("mLock")
Tony Makc5a74322020-02-04 17:18:15 +0000483 @NonNull
484 private final ServiceState mServiceState;
485 @NonNull
486 private final TextClassificationManagerService mService;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800487
488 /**
489 * Initializes a new pending request.
Tony Makc5a74322020-02-04 17:18:15 +0000490 *
491 * @param request action to perform when the service is bound
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800492 * @param onServiceFailure action to perform when the service dies or disconnects
Tony Makc5a74322020-02-04 17:18:15 +0000493 * @param binder binder to the process that made this pending request
494 * @parm service the TCMS instance.
495 * @param serviceState the service state of the service that will execute the request.
496 * @param uid the calling uid of the request.
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800497 */
Joanne Chung0b7c2c42019-08-16 16:55:11 +0800498 PendingRequest(@Nullable String name,
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700499 @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
500 @Nullable IBinder binder,
Tony Makc5a74322020-02-04 17:18:15 +0000501 @NonNull TextClassificationManagerService service,
502 @NonNull ServiceState serviceState, int uid) {
Joanne Chung0b7c2c42019-08-16 16:55:11 +0800503 mName = name;
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700504 mRequest =
Daulet Zhanguzin0ccd2ef2020-01-02 17:21:51 +0000505 logOnFailure(Objects.requireNonNull(request), "handling pending request");
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700506 mOnServiceFailure =
507 logOnFailure(onServiceFailure, "notifying callback of service failure");
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000508 mBinder = binder;
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700509 mService = service;
Tony Makc5a74322020-02-04 17:18:15 +0000510 mServiceState = Objects.requireNonNull(serviceState);
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000511 if (mBinder != null) {
512 try {
513 mBinder.linkToDeath(this, 0);
514 } catch (RemoteException e) {
515 e.printStackTrace();
516 }
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800517 }
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800518 mUid = uid;
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800519 }
520
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800521 @Override
522 public void binderDied() {
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700523 synchronized (mService.mLock) {
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800524 // No need to handle this pending request anymore. Remove.
525 removeLocked();
526 }
527 }
528
529 @GuardedBy("mLock")
530 private void removeLocked() {
Tony Makc5a74322020-02-04 17:18:15 +0000531 mServiceState.mPendingRequests.remove(this);
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000532 if (mBinder != null) {
533 mBinder.unlinkToDeath(this, 0);
534 }
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800535 }
536 }
537
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700538 private static Runnable logOnFailure(@Nullable ThrowingRunnable r, String opDesc) {
539 if (r == null) return null;
540 return FunctionalUtils.handleExceptions(r,
541 e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
542 }
543
Tony Makc5a74322020-02-04 17:18:15 +0000544 private void validateCallingPackage(@Nullable String callingPackage)
545 throws PackageManager.NameNotFoundException {
546 if (callingPackage != null) {
547 final int packageUid = mContext.getPackageManager()
548 .getPackageUidAsUser(callingPackage, UserHandle.getCallingUserId());
549 final int callingUid = Binder.getCallingUid();
550 Preconditions.checkArgument(
551 callingUid == packageUid
552 // Trust the system process:
553 || callingUid == android.os.Process.SYSTEM_UID,
554 "Invalid package name. callingPackage=" + callingPackage
555 + ", callingUid=" + callingUid);
556 }
557 }
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000558
Tony Makc5a74322020-02-04 17:18:15 +0000559 private void validateUser(@UserIdInt int userId) {
560 Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId");
561 final int callingUserId = UserHandle.getCallingUserId();
562 if (callingUserId != userId) {
563 mContext.enforceCallingOrSelfPermission(
564 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
565 "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId);
Abodunrinwa Tokiad52f4b2018-02-06 23:32:41 +0000566 }
567 }
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700568
Tony Mak53195e02020-02-06 14:52:57 +0000569 /**
570 * Stores the stripped down version of {@link TextClassificationContext}s, i.e. {@link
571 * StrippedTextClassificationContext}, keyed by {@link TextClassificationSessionId}. Sessions
572 * are cleaned up automatically when the client process is dead.
573 */
574 static final class SessionCache {
575 @NonNull
576 private final Object mLock;
577 @NonNull
578 @GuardedBy("mLock")
579 private final Map<TextClassificationSessionId, StrippedTextClassificationContext> mCache =
580 new ArrayMap<>();
581 @NonNull
582 @GuardedBy("mLock")
583 private final Map<TextClassificationSessionId, DeathRecipient> mDeathRecipients =
584 new ArrayMap<>();
585
586 SessionCache(@NonNull Object lock) {
587 mLock = Objects.requireNonNull(lock);
588 }
589
590 void put(@NonNull TextClassificationSessionId sessionId,
591 @NonNull TextClassificationContext textClassificationContext) {
592 synchronized (mLock) {
593 mCache.put(sessionId,
594 new StrippedTextClassificationContext(textClassificationContext));
595 try {
596 DeathRecipient deathRecipient = () -> remove(sessionId);
597 sessionId.getToken().linkToDeath(deathRecipient, /* flags= */ 0);
598 mDeathRecipients.put(sessionId, deathRecipient);
599 } catch (RemoteException e) {
600 Slog.w(LOG_TAG, "SessionCache: Failed to link to death", e);
601 }
602 }
603 }
604
605 @Nullable
606 StrippedTextClassificationContext get(@NonNull TextClassificationSessionId sessionId) {
607 Objects.requireNonNull(sessionId);
608 synchronized (mLock) {
609 return mCache.get(sessionId);
610 }
611 }
612
613 void remove(@NonNull TextClassificationSessionId sessionId) {
614 Objects.requireNonNull(sessionId);
615 synchronized (mLock) {
616 DeathRecipient deathRecipient = mDeathRecipients.get(sessionId);
617 if (deathRecipient != null) {
618 sessionId.getToken().unlinkToDeath(deathRecipient, /* flags= */ 0);
619 }
620 mDeathRecipients.remove(sessionId);
621 mCache.remove(sessionId);
622 }
623 }
624
625 int size() {
626 synchronized (mLock) {
627 return mCache.size();
628 }
629 }
630 }
631
632 /** A stripped down version of {@link TextClassificationContext}. */
633 static class StrippedTextClassificationContext {
634 @UserIdInt
635 public final int userId;
636 public final boolean useDefaultTextClassifier;
637
638 StrippedTextClassificationContext(TextClassificationContext textClassificationContext) {
639 userId = textClassificationContext.getUserId();
640 useDefaultTextClassifier = textClassificationContext.getUseDefaultTextClassifier();
641 }
642 }
643
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800644 private final class UserState {
Tony Makc5a74322020-02-04 17:18:15 +0000645 @UserIdInt
646 final int mUserId;
647 @Nullable
648 private final ServiceState mDefaultServiceState;
649 @Nullable
650 private final ServiceState mSystemServiceState;
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800651 @GuardedBy("mLock")
Tony Makc5a74322020-02-04 17:18:15 +0000652 @Nullable
653 private ServiceState mUntrustedServiceState;
654
655 private UserState(int userId) {
656 mUserId = userId;
657 mDefaultServiceState = TextUtils.isEmpty(mDefaultTextClassifierPackage)
658 ? null
659 : new ServiceState(userId, mDefaultTextClassifierPackage, /* isTrusted= */true);
660 mSystemServiceState = TextUtils.isEmpty(mSystemTextClassifierPackage)
661 ? null
662 : new ServiceState(userId, mSystemTextClassifierPackage, /* isTrusted= */ true);
663 }
664
665 @GuardedBy("mLock")
666 @Nullable
667 ServiceState getServiceStateLocked(boolean useDefaultTextClassifier) {
668 if (useDefaultTextClassifier) {
669 return mDefaultServiceState;
670 }
671 String textClassifierServicePackageOverride =
Tony Mak504c98c2020-02-11 16:08:06 +0000672 Binder.withCleanCallingIdentity(
673 mSettings::getTextClassifierServicePackageOverride);
Tony Makc5a74322020-02-04 17:18:15 +0000674 if (!TextUtils.isEmpty(textClassifierServicePackageOverride)) {
675 if (textClassifierServicePackageOverride.equals(mDefaultTextClassifierPackage)) {
676 return mDefaultServiceState;
677 }
678 if (textClassifierServicePackageOverride.equals(mSystemTextClassifierPackage)
679 && mSystemServiceState != null) {
680 return mSystemServiceState;
681 }
682 if (mUntrustedServiceState == null) {
683 mUntrustedServiceState =
684 new ServiceState(
685 mUserId,
686 textClassifierServicePackageOverride,
687 /* isTrusted= */false);
688 }
689 return mUntrustedServiceState;
690 }
691 return mSystemServiceState != null ? mSystemServiceState : mDefaultServiceState;
692 }
693
694 @GuardedBy("mLock")
695 void onTextClassifierServicePackageOverrideChangedLocked(String overriddenPackageName) {
696 // The override config is just used for testing, and the flag value is not expected
697 // to change often. So, let's keep it simple and just unbind all the services here. The
698 // right service will be bound when the next request comes.
699 for (ServiceState serviceState : getAllServiceStatesLocked()) {
700 serviceState.unbindIfBoundLocked();
701 }
702 mUntrustedServiceState = null;
703 }
704
705 @GuardedBy("mLock")
706 void bindIfHasPendingRequestsLocked() {
707 for (ServiceState serviceState : getAllServiceStatesLocked()) {
708 serviceState.bindIfHasPendingRequestsLocked();
709 }
710 }
711
712 @GuardedBy("mLock")
713 void cleanupServiceLocked() {
714 for (ServiceState serviceState : getAllServiceStatesLocked()) {
715 if (serviceState.mConnection != null) {
716 serviceState.mConnection.cleanupService();
717 }
718 }
719 }
720
721 @GuardedBy("mLock")
722 @NonNull
723 private List<ServiceState> getAllServiceStatesLocked() {
724 List<ServiceState> serviceStates = new ArrayList<>();
725 if (mDefaultServiceState != null) {
726 serviceStates.add(mDefaultServiceState);
727 }
728 if (mSystemServiceState != null) {
729 serviceStates.add(mSystemServiceState);
730 }
731 if (mUntrustedServiceState != null) {
732 serviceStates.add(mUntrustedServiceState);
733 }
734 return serviceStates;
735 }
736
737 void dump(IndentingPrintWriter pw) {
738 synchronized (mLock) {
739 pw.increaseIndent();
740 dump(pw, mDefaultServiceState, "Default");
741 dump(pw, mSystemServiceState, "System");
742 dump(pw, mUntrustedServiceState, "Untrusted");
743 pw.decreaseIndent();
744 }
745 }
746
747 private void dump(
748 IndentingPrintWriter pw, @Nullable ServiceState serviceState, String name) {
749 synchronized (mLock) {
750 if (serviceState != null) {
751 pw.print(name + ": ");
752 serviceState.dump(pw);
753 pw.println();
754 }
755 }
756 }
757 }
758
759 private final class ServiceState {
760 @UserIdInt
761 final int mUserId;
762 @NonNull
763 final String mPackageName;
764 @NonNull
765 final TextClassifierServiceConnection mConnection;
766 final boolean mIsTrusted;
767 @NonNull
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700768 @GuardedBy("mLock")
769 final Queue<PendingRequest> mPendingRequests = new ArrayDeque<>();
Tony Makc5a74322020-02-04 17:18:15 +0000770 @Nullable
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700771 @GuardedBy("mLock")
772 ITextClassifierService mService;
773 @GuardedBy("mLock")
774 boolean mBinding;
Tony Makc5a74322020-02-04 17:18:15 +0000775 @Nullable
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800776 @GuardedBy("mLock")
Joanne Chungc44f5292019-12-02 21:18:43 +0800777 ComponentName mBoundComponentName = null;
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800778 @GuardedBy("mLock")
Tony Makc5a74322020-02-04 17:18:15 +0000779 int mBoundServiceUid = Process.INVALID_UID;
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700780
Tony Makc5a74322020-02-04 17:18:15 +0000781 private ServiceState(@UserIdInt int userId, String packageName, boolean isTrusted) {
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700782 mUserId = userId;
Tony Makc5a74322020-02-04 17:18:15 +0000783 mPackageName = packageName;
784 mConnection = new TextClassifierServiceConnection(mUserId);
785 mIsTrusted = isTrusted;
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700786 }
787
788 @GuardedBy("mLock")
789 boolean isBoundLocked() {
790 return mService != null;
791 }
792
793 @GuardedBy("mLock")
794 private void handlePendingRequestsLocked() {
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700795 PendingRequest request;
796 while ((request = mPendingRequests.poll()) != null) {
797 if (isBoundLocked()) {
Joanne Chungc44f5292019-12-02 21:18:43 +0800798 if (!checkRequestAcceptedLocked(request.mUid, request.mName)) {
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800799 return;
800 }
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700801 request.mRequest.run();
802 } else {
803 if (request.mOnServiceFailure != null) {
Joanne Chung0b7c2c42019-08-16 16:55:11 +0800804 Slog.d(LOG_TAG, "Unable to bind TextClassifierService for PendingRequest "
805 + request.mName);
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700806 request.mOnServiceFailure.run();
807 }
808 }
809
810 if (request.mBinder != null) {
811 request.mBinder.unlinkToDeath(request, 0);
812 }
813 }
814 }
815
Andreas Gamped6d42062018-07-20 13:08:21 -0700816 @GuardedBy("mLock")
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700817 private boolean bindIfHasPendingRequestsLocked() {
818 return !mPendingRequests.isEmpty() && bindLocked();
819 }
820
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800821 @GuardedBy("mLock")
Tony Makc5a74322020-02-04 17:18:15 +0000822 void unbindIfBoundLocked() {
823 if (isBoundLocked()) {
824 Slog.v(LOG_TAG, "Unbinding " + mBoundComponentName + " for " + mUserId);
825 mContext.unbindService(mConnection);
826 mConnection.cleanupService();
827 }
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800828 }
829
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700830 /**
831 * @return true if the service is bound or in the process of being bound.
832 * Returns false otherwise.
833 */
Andreas Gamped6d42062018-07-20 13:08:21 -0700834 @GuardedBy("mLock")
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700835 private boolean bindLocked() {
836 if (isBoundLocked() || mBinding) {
837 return true;
838 }
839
840 // TODO: Handle bind timeout.
841 final boolean willBind;
842 final long identity = Binder.clearCallingIdentity();
843 try {
Tony Makc5a74322020-02-04 17:18:15 +0000844 final ComponentName componentName = getTextClassifierServiceComponent();
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700845 if (componentName == null) {
846 // Might happen if the storage is encrypted and the user is not unlocked
847 return false;
848 }
849 Intent serviceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
850 .setComponent(componentName);
851 Slog.d(LOG_TAG, "Binding to " + serviceIntent.getComponent());
852 willBind = mContext.bindServiceAsUser(
853 serviceIntent, mConnection,
Dianne Hackbornc390aa82019-01-09 16:38:22 -0800854 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
855 | Context.BIND_RESTRICT_ASSOCIATIONS,
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700856 UserHandle.of(mUserId));
857 mBinding = willBind;
858 } finally {
859 Binder.restoreCallingIdentity(identity);
860 }
861 return willBind;
862 }
863
Tony Makc5a74322020-02-04 17:18:15 +0000864 @Nullable
865 private ComponentName getTextClassifierServiceComponent() {
866 return TextClassifierService.getServiceComponentName(
867 mContext,
868 mPackageName,
869 mIsTrusted ? PackageManager.MATCH_SYSTEM_ONLY : 0);
870 }
871
Felipe Lemef8192132018-10-12 13:42:40 -0700872 private void dump(IndentingPrintWriter pw) {
873 pw.printPair("context", mContext);
874 pw.printPair("userId", mUserId);
875 synchronized (mLock) {
Tony Makc5a74322020-02-04 17:18:15 +0000876 pw.printPair("packageName", mPackageName);
Joanne Chungc44f5292019-12-02 21:18:43 +0800877 pw.printPair("boundComponentName", mBoundComponentName);
Tony Makc5a74322020-02-04 17:18:15 +0000878 pw.printPair("isTrusted", mIsTrusted);
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800879 pw.printPair("boundServiceUid", mBoundServiceUid);
Felipe Lemef8192132018-10-12 13:42:40 -0700880 pw.printPair("binding", mBinding);
881 pw.printPair("numberRequests", mPendingRequests.size());
882 }
883 }
884
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800885 @GuardedBy("mLock")
Joanne Chungc44f5292019-12-02 21:18:43 +0800886 private boolean checkRequestAcceptedLocked(int requestUid, @NonNull String methodName) {
Tony Makc5a74322020-02-04 17:18:15 +0000887 if (mIsTrusted || (requestUid == mBoundServiceUid)) {
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800888 return true;
889 }
Joanne Chungc44f5292019-12-02 21:18:43 +0800890 Slog.w(LOG_TAG, String.format(
891 "[%s] Non-default TextClassifierServices may only see text from the same uid.",
892 methodName));
893 return false;
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800894 }
895
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800896 @GuardedBy("mLock")
Joanne Chungc44f5292019-12-02 21:18:43 +0800897 private void updateServiceInfoLocked(int userId, @Nullable ComponentName componentName) {
898 mBoundComponentName = componentName;
Tony Makc5a74322020-02-04 17:18:15 +0000899 mBoundServiceUid =
900 mBoundComponentName == null
901 ? Process.INVALID_UID
902 : resolvePackageToUid(mBoundComponentName.getPackageName(), userId);
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800903 }
904
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700905 private final class TextClassifierServiceConnection implements ServiceConnection {
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800906
Tony Makc5a74322020-02-04 17:18:15 +0000907 @UserIdInt
908 private final int mUserId;
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800909
910 TextClassifierServiceConnection(int userId) {
911 mUserId = userId;
912 }
913
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700914 @Override
915 public void onServiceConnected(ComponentName name, IBinder service) {
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800916 final ITextClassifierService tcService = ITextClassifierService.Stub.asInterface(
917 service);
Joanne Chung0b7c0d42019-10-18 21:02:26 +0800918 try {
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800919 tcService.onConnectedStateChanged(TextClassifierService.CONNECTED);
Joanne Chung0b7c0d42019-10-18 21:02:26 +0800920 } catch (RemoteException e) {
921 Slog.e(LOG_TAG, "error in onConnectedStateChanged");
922 }
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800923 init(tcService, name);
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700924 }
925
926 @Override
927 public void onServiceDisconnected(ComponentName name) {
928 cleanupService();
929 }
930
931 @Override
932 public void onBindingDied(ComponentName name) {
933 cleanupService();
934 }
935
936 @Override
937 public void onNullBinding(ComponentName name) {
938 cleanupService();
939 }
940
941 void cleanupService() {
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800942 init(/* service */ null, /* name */ null);
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700943 }
944
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800945 private void init(@Nullable ITextClassifierService service,
946 @Nullable ComponentName name) {
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700947 synchronized (mLock) {
948 mService = service;
949 mBinding = false;
Joanne Chungc44f5292019-12-02 21:18:43 +0800950 updateServiceInfoLocked(mUserId, name);
Eugene Susla7b24b2b2018-03-16 14:33:31 -0700951 handlePendingRequestsLocked();
952 }
953 }
954 }
955 }
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800956
Joanne Chungc44f5292019-12-02 21:18:43 +0800957 private final class TextClassifierSettingsListener implements
958 DeviceConfig.OnPropertiesChangedListener {
Tony Makc5a74322020-02-04 17:18:15 +0000959 @NonNull
960 private final Context mContext;
961 @Nullable
962 private String mServicePackageOverride;
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800963
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800964
Joanne Chungc44f5292019-12-02 21:18:43 +0800965 TextClassifierSettingsListener(Context context) {
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800966 mContext = context;
Tony Makc5a74322020-02-04 17:18:15 +0000967 mServicePackageOverride = mSettings.getTextClassifierServicePackageOverride();
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800968 }
969
Tony Makc5a74322020-02-04 17:18:15 +0000970 void registerObserver() {
Joanne Chungc44f5292019-12-02 21:18:43 +0800971 DeviceConfig.addOnPropertiesChangedListener(
972 DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
973 mContext.getMainExecutor(),
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800974 this);
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800975 }
976
977 @Override
978 public void onPropertiesChanged(DeviceConfig.Properties properties) {
Tony Makc5a74322020-02-04 17:18:15 +0000979 final String currentServicePackageOverride =
980 mSettings.getTextClassifierServicePackageOverride();
981 if (TextUtils.equals(currentServicePackageOverride, mServicePackageOverride)) {
Joanne Chungc44f5292019-12-02 21:18:43 +0800982 return;
983 }
Tony Makc5a74322020-02-04 17:18:15 +0000984 mServicePackageOverride = currentServicePackageOverride;
985 onTextClassifierServicePackageOverrideChanged(currentServicePackageOverride);
Joanne Chungb50ab4b2019-10-14 16:40:57 +0800986 }
987 }
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800988}