1 /* 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 17 package com.android.server.autofill; 18 19 import static android.service.autofill.augmented.Helper.logResponse; 20 21 import static com.android.server.autofill.Helper.sDebug; 22 import static com.android.server.autofill.Helper.sVerbose; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.UserIdInt; 27 import android.app.AppGlobals; 28 import android.content.ClipData; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentSender; 33 import android.content.pm.PackageManager; 34 import android.content.pm.ServiceInfo; 35 import android.os.Bundle; 36 import android.os.Handler; 37 import android.os.IBinder; 38 import android.os.ICancellationSignal; 39 import android.os.RemoteException; 40 import android.os.SystemClock; 41 import android.service.autofill.Dataset; 42 import android.service.autofill.augmented.AugmentedAutofillService; 43 import android.service.autofill.augmented.IAugmentedAutofillService; 44 import android.service.autofill.augmented.IFillCallback; 45 import android.util.Pair; 46 import android.util.Slog; 47 import android.view.autofill.AutofillId; 48 import android.view.autofill.AutofillManager; 49 import android.view.autofill.AutofillValue; 50 import android.view.autofill.IAutoFillManagerClient; 51 import android.view.inputmethod.InlineSuggestionsRequest; 52 53 import com.android.internal.infra.AbstractRemoteService; 54 import com.android.internal.infra.AndroidFuture; 55 import com.android.internal.infra.ServiceConnector; 56 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 57 import com.android.internal.os.IResultReceiver; 58 import com.android.server.autofill.ui.InlineFillUi; 59 60 import java.util.ArrayList; 61 import java.util.List; 62 import java.util.concurrent.CancellationException; 63 import java.util.concurrent.TimeUnit; 64 import java.util.concurrent.TimeoutException; 65 import java.util.concurrent.atomic.AtomicReference; 66 import java.util.function.Function; 67 68 final class RemoteAugmentedAutofillService 69 extends ServiceConnector.Impl<IAugmentedAutofillService> { 70 71 private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName(); 72 73 private final int mIdleUnbindTimeoutMs; 74 private final int mRequestTimeoutMs; 75 private final ComponentName mComponentName; 76 private final RemoteAugmentedAutofillServiceCallbacks mCallbacks; 77 private final AutofillUriGrantsManager mUriGrantsManager; 78 RemoteAugmentedAutofillService(Context context, int serviceUid, ComponentName serviceName, int userId, RemoteAugmentedAutofillServiceCallbacks callbacks, boolean bindInstantServiceAllowed, boolean verbose, int idleUnbindTimeoutMs, int requestTimeoutMs)79 RemoteAugmentedAutofillService(Context context, int serviceUid, ComponentName serviceName, 80 int userId, RemoteAugmentedAutofillServiceCallbacks callbacks, 81 boolean bindInstantServiceAllowed, boolean verbose, int idleUnbindTimeoutMs, 82 int requestTimeoutMs) { 83 super(context, 84 new Intent(AugmentedAutofillService.SERVICE_INTERFACE).setComponent(serviceName), 85 bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, 86 userId, IAugmentedAutofillService.Stub::asInterface); 87 mIdleUnbindTimeoutMs = idleUnbindTimeoutMs; 88 mRequestTimeoutMs = requestTimeoutMs; 89 mComponentName = serviceName; 90 mCallbacks = callbacks; 91 mUriGrantsManager = new AutofillUriGrantsManager(serviceUid); 92 93 // Bind right away. 94 connect(); 95 } 96 97 @Nullable getComponentName(@onNull String componentName, @UserIdInt int userId, boolean isTemporary)98 static Pair<ServiceInfo, ComponentName> getComponentName(@NonNull String componentName, 99 @UserIdInt int userId, boolean isTemporary) { 100 int flags = PackageManager.GET_META_DATA; 101 if (!isTemporary) { 102 flags |= PackageManager.MATCH_SYSTEM_ONLY; 103 } 104 105 final ComponentName serviceComponent; 106 ServiceInfo serviceInfo = null; 107 try { 108 serviceComponent = ComponentName.unflattenFromString(componentName); 109 serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags, 110 userId); 111 if (serviceInfo == null) { 112 Slog.e(TAG, "Bad service name for flags " + flags + ": " + componentName); 113 return null; 114 } 115 } catch (Exception e) { 116 Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e); 117 return null; 118 } 119 return new Pair<>(serviceInfo, serviceComponent); 120 } 121 getComponentName()122 public ComponentName getComponentName() { 123 return mComponentName; 124 } 125 getAutofillUriGrantsManager()126 public AutofillUriGrantsManager getAutofillUriGrantsManager() { 127 return mUriGrantsManager; 128 } 129 130 @Override // from ServiceConnector.Impl onServiceConnectionStatusChanged( IAugmentedAutofillService service, boolean connected)131 protected void onServiceConnectionStatusChanged( 132 IAugmentedAutofillService service, boolean connected) { 133 try { 134 if (connected) { 135 service.onConnected(sDebug, sVerbose); 136 } else { 137 service.onDisconnected(); 138 } 139 } catch (Exception e) { 140 Slog.w(TAG, 141 "Exception calling onServiceConnectionStatusChanged(" + connected + "): ", e); 142 } 143 } 144 145 @Override // from AbstractRemoteService getAutoDisconnectTimeoutMs()146 protected long getAutoDisconnectTimeoutMs() { 147 return mIdleUnbindTimeoutMs; 148 } 149 150 /** 151 * Called by {@link Session} to request augmented autofill. 152 */ onRequestAutofillLocked(int sessionId, @NonNull IAutoFillManagerClient client, int taskId, @NonNull ComponentName activityComponent, @NonNull IBinder activityToken, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, @Nullable InlineSuggestionsRequest inlineSuggestionsRequest, @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService, int userId)153 public void onRequestAutofillLocked(int sessionId, @NonNull IAutoFillManagerClient client, 154 int taskId, @NonNull ComponentName activityComponent, @NonNull IBinder activityToken, 155 @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, 156 @Nullable InlineSuggestionsRequest inlineSuggestionsRequest, 157 @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback, 158 @NonNull Runnable onErrorCallback, 159 @Nullable RemoteInlineSuggestionRenderService remoteRenderService, int userId) { 160 long requestTime = SystemClock.elapsedRealtime(); 161 AtomicReference<ICancellationSignal> cancellationRef = new AtomicReference<>(); 162 163 postAsync(service -> { 164 AndroidFuture<Void> requestAutofill = new AndroidFuture<>(); 165 // TODO(b/122728762): set cancellation signal, timeout (from both client and service), 166 // cache IAugmentedAutofillManagerClient reference, etc... 167 client.getAugmentedAutofillClient(new IResultReceiver.Stub() { 168 @Override 169 public void send(int resultCode, Bundle resultData) throws RemoteException { 170 final IBinder realClient = resultData 171 .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT); 172 service.onFillRequest(sessionId, realClient, taskId, activityComponent, 173 focusedId, focusedValue, requestTime, inlineSuggestionsRequest, 174 new IFillCallback.Stub() { 175 @Override 176 public void onSuccess(@Nullable List<Dataset> inlineSuggestionsData, 177 @Nullable Bundle clientState, boolean showingFillWindow) { 178 mCallbacks.resetLastResponse(); 179 maybeRequestShowInlineSuggestions(sessionId, 180 inlineSuggestionsRequest, inlineSuggestionsData, 181 clientState, focusedId, focusedValue, 182 inlineSuggestionsCallback, client, onErrorCallback, 183 remoteRenderService, userId, 184 activityComponent, activityToken); 185 if (!showingFillWindow) { 186 requestAutofill.complete(null); 187 } 188 } 189 190 @Override 191 public boolean isCompleted() { 192 return requestAutofill.isDone() 193 && !requestAutofill.isCancelled(); 194 } 195 196 @Override 197 public void onCancellable(ICancellationSignal cancellation) { 198 if (requestAutofill.isCancelled()) { 199 dispatchCancellation(cancellation); 200 } else { 201 cancellationRef.set(cancellation); 202 } 203 } 204 205 @Override 206 public void cancel() { 207 requestAutofill.cancel(true); 208 } 209 }); 210 } 211 }); 212 return requestAutofill; 213 }).orTimeout(mRequestTimeoutMs, TimeUnit.MILLISECONDS) 214 .whenComplete((res, err) -> { 215 if (err instanceof CancellationException) { 216 dispatchCancellation(cancellationRef.get()); 217 } else if (err instanceof TimeoutException) { 218 Slog.w(TAG, "PendingAutofillRequest timed out (" + mRequestTimeoutMs 219 + "ms) for " + RemoteAugmentedAutofillService.this); 220 // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks 221 dispatchCancellation(cancellationRef.get()); 222 if (mComponentName != null) { 223 logResponse(MetricsEvent.TYPE_ERROR, mComponentName.getPackageName(), 224 activityComponent, sessionId, mRequestTimeoutMs); 225 } 226 } else if (err != null) { 227 Slog.e(TAG, "exception handling getAugmentedAutofillClient() for " 228 + sessionId + ": ", err); 229 } else { 230 // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks 231 } 232 }); 233 } 234 dispatchCancellation(@ullable ICancellationSignal cancellation)235 void dispatchCancellation(@Nullable ICancellationSignal cancellation) { 236 if (cancellation == null) { 237 return; 238 } 239 Handler.getMain().post(() -> { 240 try { 241 cancellation.cancel(); 242 } catch (RemoteException e) { 243 Slog.e(TAG, "Error requesting a cancellation", e); 244 } 245 }); 246 } 247 maybeRequestShowInlineSuggestions(int sessionId, @Nullable InlineSuggestionsRequest request, @Nullable List<Dataset> inlineSuggestionsData, @Nullable Bundle clientState, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback, @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService, int userId, @NonNull ComponentName targetActivity, @NonNull IBinder targetActivityToken)248 private void maybeRequestShowInlineSuggestions(int sessionId, 249 @Nullable InlineSuggestionsRequest request, 250 @Nullable List<Dataset> inlineSuggestionsData, @Nullable Bundle clientState, 251 @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, 252 @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback, 253 @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback, 254 @Nullable RemoteInlineSuggestionRenderService remoteRenderService, 255 int userId, 256 @NonNull ComponentName targetActivity, @NonNull IBinder targetActivityToken) { 257 if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty() 258 || inlineSuggestionsCallback == null || request == null 259 || remoteRenderService == null) { 260 // If it was an inline request and the response doesn't have any inline suggestions, 261 // we will send an empty response to IME. 262 if (inlineSuggestionsCallback != null && request != null) { 263 inlineSuggestionsCallback.apply(InlineFillUi.emptyUi(focusedId)); 264 } 265 return; 266 } 267 mCallbacks.setLastResponse(sessionId); 268 269 final String filterText = 270 focusedValue != null && focusedValue.isText() 271 ? focusedValue.getTextValue().toString() : null; 272 273 final InlineFillUi.InlineFillUiInfo inlineFillUiInfo = 274 new InlineFillUi.InlineFillUiInfo(request, focusedId, filterText, 275 remoteRenderService, userId, sessionId); 276 277 final InlineFillUi inlineFillUi = 278 InlineFillUi.forAugmentedAutofill( 279 inlineFillUiInfo, inlineSuggestionsData, 280 new InlineFillUi.InlineSuggestionUiCallback() { 281 @Override 282 public void autofill(Dataset dataset, int datasetIndex) { 283 if (dataset.getAuthentication() != null) { 284 mCallbacks.logAugmentedAutofillAuthenticationSelected(sessionId, 285 dataset.getId(), clientState); 286 final IntentSender action = dataset.getAuthentication(); 287 final int authenticationId = 288 AutofillManager.makeAuthenticationId( 289 Session.AUGMENTED_AUTOFILL_REQUEST_ID, 290 datasetIndex); 291 final Intent fillInIntent = new Intent(); 292 fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, 293 clientState); 294 try { 295 client.authenticate(sessionId, authenticationId, action, 296 fillInIntent, false); 297 } catch (RemoteException e) { 298 Slog.w(TAG, "Error starting auth flow"); 299 inlineSuggestionsCallback.apply( 300 InlineFillUi.emptyUi(focusedId)); 301 } 302 return; 303 } 304 mCallbacks.logAugmentedAutofillSelected(sessionId, 305 dataset.getId(), clientState); 306 try { 307 final ArrayList<AutofillId> fieldIds = dataset.getFieldIds(); 308 final ClipData content = dataset.getFieldContent(); 309 if (content != null) { 310 mUriGrantsManager.grantUriPermissions(targetActivity, 311 targetActivityToken, userId, content); 312 final AutofillId fieldId = fieldIds.get(0); 313 if (sDebug) { 314 Slog.d(TAG, "Calling client autofillContent(): " 315 + "id=" + fieldId + ", content=" + content); 316 } 317 client.autofillContent(sessionId, fieldId, content); 318 } else { 319 final int size = fieldIds.size(); 320 final boolean hideHighlight = size == 1 321 && fieldIds.get(0).equals(focusedId); 322 if (sDebug) { 323 Slog.d(TAG, "Calling client autofill(): " 324 + "ids=" + fieldIds 325 + ", values=" + dataset.getFieldValues()); 326 } 327 client.autofill( 328 sessionId, 329 fieldIds, 330 dataset.getFieldValues(), 331 hideHighlight); 332 } 333 inlineSuggestionsCallback.apply( 334 InlineFillUi.emptyUi(focusedId)); 335 } catch (RemoteException e) { 336 Slog.w(TAG, "Encounter exception autofilling the values"); 337 } 338 } 339 340 @Override 341 public void authenticate(int requestId, int datasetIndex) { 342 Slog.e(TAG, "authenticate not implemented for augmented autofill"); 343 } 344 345 @Override 346 public void startIntentSender(IntentSender intentSender) { 347 try { 348 client.startIntentSender(intentSender, new Intent()); 349 } catch (RemoteException e) { 350 Slog.w(TAG, "RemoteException starting intent sender"); 351 } 352 } 353 354 @Override 355 public void onError() { 356 onErrorCallback.run(); 357 } 358 }); 359 360 if (inlineSuggestionsCallback.apply(inlineFillUi)) { 361 mCallbacks.logAugmentedAutofillShown(sessionId, clientState); 362 } 363 } 364 365 @Override toString()366 public String toString() { 367 return "RemoteAugmentedAutofillService[" 368 + ComponentName.flattenToShortString(mComponentName) + "]"; 369 } 370 371 /** 372 * Called by {@link Session} when it's time to destroy all augmented autofill requests. 373 */ onDestroyAutofillWindowsRequest()374 public void onDestroyAutofillWindowsRequest() { 375 run((s) -> s.onDestroyAllFillWindowsRequest()); 376 } 377 378 public interface RemoteAugmentedAutofillServiceCallbacks 379 extends AbstractRemoteService.VultureCallback<RemoteAugmentedAutofillService> { resetLastResponse()380 void resetLastResponse(); 381 setLastResponse(int sessionId)382 void setLastResponse(int sessionId); 383 logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState)384 void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState); 385 logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId, @Nullable Bundle clientState)386 void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId, 387 @Nullable Bundle clientState); 388 logAugmentedAutofillAuthenticationSelected(int sessionId, @Nullable String suggestionId, @Nullable Bundle clientState)389 void logAugmentedAutofillAuthenticationSelected(int sessionId, 390 @Nullable String suggestionId, @Nullable Bundle clientState); 391 } 392 } 393