1 /*
2  * Copyright (C) 2020 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 
17 package com.android.server.autofill;
18 
19 import static com.android.server.autofill.Helper.sDebug;
20 import static com.android.server.autofill.Helper.sVerbose;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.UserIdInt;
25 import android.app.AppGlobals;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.pm.PackageManager;
30 import android.content.pm.ServiceInfo;
31 import android.os.Build;
32 import android.os.ICancellationSignal;
33 import android.os.RemoteException;
34 import android.os.SystemClock;
35 import android.service.assist.classification.FieldClassificationRequest;
36 import android.service.assist.classification.FieldClassificationResponse;
37 import android.service.assist.classification.FieldClassificationService;
38 import android.service.assist.classification.IFieldClassificationCallback;
39 import android.service.assist.classification.IFieldClassificationService;
40 import android.util.Log;
41 import android.util.Pair;
42 import android.util.Slog;
43 
44 import com.android.internal.infra.AbstractRemoteService;
45 import com.android.internal.infra.ServiceConnector;
46 
47 import java.lang.ref.WeakReference;
48 
49 /**
50  * Class responsible for connection with the Remote {@link FieldClassificationService}.
51  * This class is instantiated when {@link AutofillManagerServiceImpl} is established.
52  * The connection is supposed to be bounded forever, as such, this class persists beyond
53  * Autofill {@link Session}'s lifecycle. As such, it can't contain information relevant to Session.
54  * This design is completely different from {@link RemoteFillService}.
55  */
56 final class RemoteFieldClassificationService
57         extends ServiceConnector.Impl<IFieldClassificationService> {
58 
59     private static final String TAG =
60             "Autofill" + RemoteFieldClassificationService.class.getSimpleName();
61 
62     // Bind forever.
63     private static final long TIMEOUT_IDLE_UNBIND_MS =
64             AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
65     private final ComponentName mComponentName;
66 
67     public interface FieldClassificationServiceCallbacks {
onClassificationRequestSuccess(@onNull FieldClassificationResponse response)68         void onClassificationRequestSuccess(@NonNull FieldClassificationResponse response);
onClassificationRequestFailure(int requestId, @Nullable CharSequence message)69         void onClassificationRequestFailure(int requestId, @Nullable CharSequence message);
onClassificationRequestTimeout(int requestId)70         void onClassificationRequestTimeout(int requestId);
onServiceDied(@onNull RemoteFieldClassificationService service)71         void onServiceDied(@NonNull RemoteFieldClassificationService service);
logFieldClassificationEvent( long startTime, @NonNull FieldClassificationResponse response, @FieldClassificationEventLogger.FieldClassificationStatus int status)72         void logFieldClassificationEvent(
73                 long startTime, @NonNull FieldClassificationResponse response,
74                 @FieldClassificationEventLogger.FieldClassificationStatus int status);
75     }
76 
RemoteFieldClassificationService(Context context, ComponentName serviceName, int serviceUid, int userId)77     RemoteFieldClassificationService(Context context, ComponentName serviceName,
78             int serviceUid, int userId) {
79         super(context,
80                 // TODO(b/266379948): Update service
81                 new Intent(FieldClassificationService.SERVICE_INTERFACE).setComponent(serviceName),
82                 /* bindingFlags= */ 0, userId, IFieldClassificationService.Stub::asInterface);
83         mComponentName = serviceName;
84         if (sDebug) {
85             Slog.d(TAG, "About to connect to serviceName: " + serviceName);
86         }
87         // Bind right away.
88         connect();
89     }
90 
91     @Nullable
getComponentName(@onNull String serviceName, @UserIdInt int userId, boolean isTemporary)92     static Pair<ServiceInfo, ComponentName> getComponentName(@NonNull String serviceName,
93             @UserIdInt int userId, boolean isTemporary) {
94         int flags = PackageManager.GET_META_DATA;
95         if (!isTemporary) {
96             flags |= PackageManager.MATCH_SYSTEM_ONLY;
97         }
98 
99         final ComponentName serviceComponent;
100         ServiceInfo serviceInfo = null;
101         try {
102             serviceComponent = ComponentName.unflattenFromString(serviceName);
103             serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags,
104                     userId);
105             if (serviceInfo == null) {
106                 Slog.e(TAG, "Bad service name for flags " + flags + ": " + serviceName);
107                 return null;
108             }
109         } catch (Exception e) {
110             Slog.e(TAG, "Error getting service info for '" + serviceName + "': " + e);
111             return null;
112         }
113         return new Pair<>(serviceInfo, serviceComponent);
114     }
115 
getComponentName()116     public ComponentName getComponentName() {
117         return mComponentName;
118     }
119 
120     @Override // from ServiceConnector.Impl
onServiceConnectionStatusChanged(IFieldClassificationService service, boolean connected)121     protected void onServiceConnectionStatusChanged(IFieldClassificationService service,
122             boolean connected) {
123         try {
124             if (connected) {
125                 service.onConnected(false, false);
126             } else {
127                 service.onDisconnected();
128             }
129         } catch (Exception e) {
130             Slog.w(TAG,
131                     "Exception calling onServiceConnectionStatusChanged(" + connected + "): ", e);
132         }
133     }
134 
135     @Override // from AbstractRemoteService
getAutoDisconnectTimeoutMs()136     protected long getAutoDisconnectTimeoutMs() {
137         return TIMEOUT_IDLE_UNBIND_MS;
138     }
139 
onFieldClassificationRequest(@onNull FieldClassificationRequest request, WeakReference<FieldClassificationServiceCallbacks> fieldClassificationServiceCallbacksWeakRef)140     public void onFieldClassificationRequest(@NonNull FieldClassificationRequest request,
141             WeakReference<FieldClassificationServiceCallbacks>
142                 fieldClassificationServiceCallbacksWeakRef) {
143         final long startTime = SystemClock.elapsedRealtime();
144         if (sVerbose) {
145             Slog.v(TAG, "onFieldClassificationRequest request:" + request);
146         }
147 
148         run(
149                 (s) ->
150                         s.onFieldClassificationRequest(
151                                 request,
152                                 new IFieldClassificationCallback.Stub() {
153                                     @Override
154                                     public void onCancellable(ICancellationSignal cancellation) {
155                                         if (sDebug) {
156                                             Log.d(TAG, "onCancellable");
157                                         }
158                                         FieldClassificationServiceCallbacks
159                                                 fieldClassificationServiceCallbacks =
160                                                 Helper.weakDeref(
161                                                         fieldClassificationServiceCallbacksWeakRef,
162                                                         TAG, "onCancellable "
163                                                 );
164                                         logFieldClassificationEvent(
165                                                 startTime,
166                                                 fieldClassificationServiceCallbacks,
167                                                 FieldClassificationEventLogger.STATUS_CANCELLED,
168                                                 null);
169                                     }
170 
171                                     @Override
172                                     public void onSuccess(FieldClassificationResponse response) {
173                                         if (sDebug) {
174                                             if (Build.IS_DEBUGGABLE) {
175                                                 Slog.d(TAG, "onSuccess Response: " + response);
176                                             } else {
177                                                 String msg = "";
178                                                 if (response == null
179                                                         || response.getClassifications() == null) {
180                                                     msg = "null response";
181                                                 } else {
182                                                     msg = "size: "
183                                                             + response.getClassifications().size();
184                                                 }
185                                                 Slog.d(TAG, "onSuccess " + msg);
186                                             }
187                                         }
188                                         FieldClassificationServiceCallbacks
189                                                 fieldClassificationServiceCallbacks =
190                                                         Helper.weakDeref(
191                                                                 fieldClassificationServiceCallbacksWeakRef,
192                                                                 TAG, "onSuccess "
193                                                         );
194                                         logFieldClassificationEvent(
195                                                 startTime,
196                                                 fieldClassificationServiceCallbacks,
197                                                 FieldClassificationEventLogger.STATUS_SUCCESS,
198                                                 response);
199                                         if (fieldClassificationServiceCallbacks == null) {
200                                             return;
201                                         }
202                                         fieldClassificationServiceCallbacks
203                                                 .onClassificationRequestSuccess(response);
204                                     }
205 
206                                     @Override
207                                     public void onFailure() {
208                                         if (sDebug) {
209                                             Slog.d(TAG, "onFailure");
210                                         }
211                                         FieldClassificationServiceCallbacks
212                                                 fieldClassificationServiceCallbacks =
213                                                         Helper.weakDeref(
214                                                                 fieldClassificationServiceCallbacksWeakRef,
215                                                                 TAG, "onFailure "
216                                                         );
217                                         logFieldClassificationEvent(
218                                                 startTime,
219                                                 fieldClassificationServiceCallbacks,
220                                                 FieldClassificationEventLogger.STATUS_FAIL,
221                                                 null);
222                                         if (fieldClassificationServiceCallbacks == null) {
223                                             return;
224                                         }
225                                         fieldClassificationServiceCallbacks
226                                                 .onClassificationRequestFailure(0, null);
227                                     }
228 
229                                     @Override
230                                     public boolean isCompleted() throws RemoteException {
231                                         return false;
232                                     }
233 
234                                     @Override
235                                     public void cancel() throws RemoteException {}
236                                 }));
237     }
238 
logFieldClassificationEvent( long startTime, @Nullable FieldClassificationServiceCallbacks fieldClassificationServiceCallbacks, @FieldClassificationEventLogger.FieldClassificationStatus int status, FieldClassificationResponse response)239     private void logFieldClassificationEvent(
240             long startTime,
241             @Nullable FieldClassificationServiceCallbacks fieldClassificationServiceCallbacks,
242             @FieldClassificationEventLogger.FieldClassificationStatus int status,
243             FieldClassificationResponse response) {
244         if (fieldClassificationServiceCallbacks == null) {
245             final FieldClassificationEventLogger logger =
246                     FieldClassificationEventLogger.createLogger();
247             logger.startNewLogForRequest();
248             logger.maybeSetLatencyMillis(
249                     SystemClock.elapsedRealtime() - startTime);
250             logger.maybeSetSessionGc(true);
251             logger.maybeSetRequestStatus(status);
252             logger.logAndEndEvent();
253         } else {
254             fieldClassificationServiceCallbacks.logFieldClassificationEvent(
255                     startTime, response, status);
256         }
257 
258     }
259 }
260