Loading core/java/android/util/DebugUtils.java +20 −0 Original line number Diff line number Diff line Loading @@ -24,7 +24,11 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; /** * <p>Various utilities for debugging and logging.</p> Loading Loading @@ -270,4 +274,20 @@ public class DebugUtils { private static String constNameWithoutPrefix(String prefix, Field field) { return field.getName().substring(prefix.length()); } /** * Returns method names from current stack trace, where {@link StackTraceElement#getClass} * starts with the given classes name * * @hide */ public static List<String> callersWithin(Class<?> cls, int offset) { List<String> result = Arrays.stream(Thread.currentThread().getStackTrace()) .skip(offset + 3) .filter(st -> st.getClassName().startsWith(cls.getName())) .map(StackTraceElement::getMethodName) .collect(Collectors.toList()); Collections.reverse(result); return result; } } core/java/com/android/internal/infra/AndroidFuture.java 0 → 100644 +226 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.infra; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; import android.os.Message; import android.util.ExceptionUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; import java.util.function.Function; /** * A customized {@link CompletableFuture} with focus on reducing the number of allocations involved * in a typical future usage scenario for Android. * * In particular this involves allocations optimizations in: * <ul> * <li>{@link #thenCompose(Function)}</li> * <li>{@link #orTimeout(long, TimeUnit)}</li> * <li>{@link #whenComplete(BiConsumer)}</li> * </ul> * * @param <T> see {@link CompletableFuture} */ public class AndroidFuture<T> extends CompletableFuture<T> { private static final String LOG_TAG = AndroidFuture.class.getSimpleName(); @GuardedBy("this") private @Nullable BiConsumer<? super T, ? super Throwable> mListener; private @NonNull Handler mTimeoutHandler = Handler.getMain(); @Override public boolean complete(@Nullable T value) { boolean changed = super.complete(value); if (changed) { onCompleted(value, null); } return changed; } @Override public boolean completeExceptionally(@NonNull Throwable ex) { boolean changed = super.completeExceptionally(ex); if (changed) { onCompleted(null, ex); } return super.completeExceptionally(ex); } private void onCompleted(@Nullable T res, @Nullable Throwable err) { cancelTimeout(); BiConsumer<? super T, ? super Throwable> listener; synchronized (this) { listener = mListener; mListener = null; } if (listener != null) { callListener(listener, res, err); } } @Override public AndroidFuture<T> whenComplete( @NonNull BiConsumer<? super T, ? super Throwable> action) { Preconditions.checkNotNull(action); synchronized (this) { if (!isDone()) { BiConsumer<? super T, ? super Throwable> oldListener = mListener; mListener = oldListener == null ? action : (res, err) -> { callListener(oldListener, res, err); callListener(action, res, err); }; return this; } } // isDone() == true at this point T res = null; Throwable err = null; try { res = get(); } catch (ExecutionException e) { err = e.getCause(); } catch (Throwable e) { err = e; } callListener(action, res, err); return this; } /** * Calls the provided listener, handling any exceptions that may arise. */ // package-private to avoid synthetic method when called from lambda static <TT> void callListener( @NonNull BiConsumer<? super TT, ? super Throwable> listener, @Nullable TT res, @Nullable Throwable err) { try { try { listener.accept(res, err); } catch (Throwable t) { if (err == null) { // listener happy-case threw, but exception case might not throw, so report the // same exception thrown by listener's happy-path to it again listener.accept(null, t); } else { // listener exception-case threw // give up on listener but preserve the original exception when throwing up ExceptionUtils.getRootCause(t).initCause(err); throw t; } } } catch (Throwable t2) { // give up on listener and log the result & exception to logcat Log.e(LOG_TAG, "Failed to call whenComplete listener. res = " + res, t2); } } /** @inheritDoc */ //@Override //TODO uncomment once java 9 APIs are exposed to frameworks public AndroidFuture<T> orTimeout(long timeout, @NonNull TimeUnit unit) { Message msg = PooledLambda.obtainMessage(AndroidFuture::triggerTimeout, this); msg.obj = this; mTimeoutHandler.sendMessageDelayed(msg, unit.toMillis(timeout)); return this; } void triggerTimeout() { cancelTimeout(); if (!isDone()) { completeExceptionally(new TimeoutException()); } } protected void cancelTimeout() { mTimeoutHandler.removeCallbacksAndMessages(this); } /** * Specifies the handler on which timeout is to be triggered */ public AndroidFuture<T> setTimeoutHandler(@NonNull Handler h) { cancelTimeout(); mTimeoutHandler = Preconditions.checkNotNull(h); return this; } @Override public <U> AndroidFuture<U> thenCompose( @NonNull Function<? super T, ? extends CompletionStage<U>> fn) { return (AndroidFuture<U>) new ThenCompose<>(this, fn); } private static class ThenCompose<T, U> extends AndroidFuture<Object> implements BiConsumer<Object, Throwable> { private final AndroidFuture<T> mSource; private Function<? super T, ? extends CompletionStage<U>> mFn; ThenCompose(@NonNull AndroidFuture<T> source, @NonNull Function<? super T, ? extends CompletionStage<U>> fn) { mSource = source; mFn = Preconditions.checkNotNull(fn); // subscribe to first job completion source.whenComplete(this); } @Override public void accept(Object res, Throwable err) { Function<? super T, ? extends CompletionStage<U>> fn; synchronized (this) { fn = mFn; mFn = null; } if (fn != null) { // first job completed CompletionStage<U> secondJob; try { secondJob = Preconditions.checkNotNull(fn.apply((T) res)); } catch (Throwable t) { completeExceptionally(t); return; } // subscribe to second job completion secondJob.whenComplete(this); } else { // second job completed if (err != null) { completeExceptionally(err); } else { complete(res); } } } } } core/java/com/android/internal/infra/ServiceConnector.java 0 → 100644 +699 −0 File added.Preview size limit exceeded, changes collapsed. Show changes services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +2 −2 Original line number Diff line number Diff line Loading @@ -1112,7 +1112,7 @@ final class AutofillManagerServiceImpl final RemoteAugmentedAutofillService remoteService = mRemoteAugmentedAutofillService; if (remoteService != null) { remoteService.destroy(); remoteService.unbind(); } mRemoteAugmentedAutofillService = null; } Loading @@ -1135,7 +1135,7 @@ final class AutofillManagerServiceImpl Slog.v(TAG, "updateRemoteAugmentedAutofillService(): " + "destroying old remote service"); } mRemoteAugmentedAutofillService.destroy(); mRemoteAugmentedAutofillService.unbind(); mRemoteAugmentedAutofillService = null; mRemoteAugmentedAutofillServiceInfo = null; resetAugmentedAutofillWhitelistLocked(); Loading services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +106 −188 Original line number Diff line number Diff line Loading @@ -25,9 +25,11 @@ import android.annotation.UserIdInt; import android.app.AppGlobals; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.ICancellationSignal; import android.os.RemoteException; Loading @@ -42,30 +44,39 @@ import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import com.android.internal.infra.AbstractSinglePendingRequestRemoteService; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; import com.android.internal.os.IResultReceiver; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; final class RemoteAugmentedAutofillService extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService, IAugmentedAutofillService> { extends ServiceConnector.Impl<IAugmentedAutofillService> { private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName(); private final int mIdleUnbindTimeoutMs; private final int mRequestTimeoutMs; private final ComponentName mComponentName; RemoteAugmentedAutofillService(Context context, ComponentName serviceName, int userId, RemoteAugmentedAutofillServiceCallbacks callbacks, boolean bindInstantServiceAllowed, boolean verbose, int idleUnbindTimeoutMs, int requestTimeoutMs) { super(context, AugmentedAutofillService.SERVICE_INTERFACE, serviceName, userId, callbacks, context.getMainThreadHandler(), bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, verbose); super(context, new Intent(AugmentedAutofillService.SERVICE_INTERFACE).setComponent(serviceName), bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, userId, IAugmentedAutofillService.Stub::asInterface); mIdleUnbindTimeoutMs = idleUnbindTimeoutMs; mRequestTimeoutMs = requestTimeoutMs; mComponentName = serviceName; // Bind right away. scheduleBind(); connect(); } @Nullable Loading Loading @@ -93,219 +104,126 @@ final class RemoteAugmentedAutofillService return new Pair<>(serviceInfo, serviceComponent); } @Override // from RemoteService protected void handleOnConnectedStateChanged(boolean state) { if (state && getTimeoutIdleBindMillis() != PERMANENT_BOUND_TIMEOUT_MS) { scheduleUnbind(); public ComponentName getComponentName() { return mComponentName; } @Override // from ServiceConnector.Impl protected void onServiceConnectionStatusChanged( IAugmentedAutofillService service, boolean connected) { try { if (state) { mService.onConnected(sDebug, sVerbose); if (connected) { service.onConnected(sDebug, sVerbose); } else { mService.onDisconnected(); service.onDisconnected(); } } catch (Exception e) { Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e); } Slog.w(TAG, "Exception calling onServiceConnectionStatusChanged(" + connected + "): ", e); } @Override // from AbstractRemoteService protected IAugmentedAutofillService getServiceInterface(IBinder service) { return IAugmentedAutofillService.Stub.asInterface(service); } @Override // from AbstractRemoteService protected long getTimeoutIdleBindMillis() { protected long getAutoDisconnectTimeoutMs() { return mIdleUnbindTimeoutMs; } @Override // from AbstractRemoteService protected long getRemoteRequestMillis() { return mRequestTimeoutMs; } /** * Called by {@link Session} to request augmented autofill. */ public void onRequestAutofillLocked(int sessionId, @NonNull IAutoFillManagerClient client, int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue) { scheduleRequest(new PendingAutofillRequest(this, sessionId, client, taskId, activityComponent, focusedId, focusedValue)); } long requestTime = SystemClock.elapsedRealtime(); AtomicReference<ICancellationSignal> cancellationRef = new AtomicReference<>(); postAsync(service -> { AndroidFuture<Void> requestAutofill = new AndroidFuture<>(); // TODO(b/122728762): set cancellation signal, timeout (from both client and service), // cache IAugmentedAutofillManagerClient reference, etc... client.getAugmentedAutofillClient(new IResultReceiver.Stub() { @Override public String toString() { return "RemoteAugmentedAutofillService[" + ComponentName.flattenToShortString(getComponentName()) + "]"; } /** * Called by {@link Session} when it's time to destroy all augmented autofill requests. */ public void onDestroyAutofillWindowsRequest() { scheduleAsyncRequest((s) -> s.onDestroyAllFillWindowsRequest()); } private void dispatchOnFillTimeout(@NonNull ICancellationSignal cancellation) { mHandler.post(() -> { try { cancellation.cancel(); } catch (RemoteException e) { Slog.w(mTag, "Error calling cancellation signal: " + e); } }); } // TODO(b/123100811): inline into PendingAutofillRequest if it doesn't have any other subclass private abstract static class MyPendingRequest extends PendingRequest<RemoteAugmentedAutofillService, IAugmentedAutofillService> { protected final int mSessionId; private MyPendingRequest(@NonNull RemoteAugmentedAutofillService service, int sessionId) { super(service); mSessionId = sessionId; } } private static final class PendingAutofillRequest extends MyPendingRequest { private final @NonNull AutofillId mFocusedId; private final @Nullable AutofillValue mFocusedValue; private final @NonNull IAutoFillManagerClient mClient; private final @NonNull ComponentName mActivityComponent; private final int mTaskId; private final long mRequestTime = SystemClock.elapsedRealtime(); private final @NonNull IFillCallback mCallback; private ICancellationSignal mCancellation; protected PendingAutofillRequest(@NonNull RemoteAugmentedAutofillService service, int sessionId, @NonNull IAutoFillManagerClient client, int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue) { super(service, sessionId); mClient = client; mTaskId = taskId; mActivityComponent = activityComponent; mFocusedId = focusedId; mFocusedValue = focusedValue; mCallback = new IFillCallback.Stub() { public void send(int resultCode, Bundle resultData) throws RemoteException { final IBinder realClient = resultData .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT); service.onFillRequest(sessionId, realClient, taskId, activityComponent, focusedId, focusedValue, requestTime, new IFillCallback.Stub() { @Override public void onSuccess() { if (!finish()) return; // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks } @Override public void onCancellable(ICancellationSignal cancellation) { synchronized (mLock) { final boolean cancelled; synchronized (mLock) { mCancellation = cancellation; cancelled = isCancelledLocked(); } if (cancelled) { try { cancellation.cancel(); } catch (RemoteException e) { Slog.e(mTag, "Error requesting a cancellation", e); } } } requestAutofill.complete(null); } @Override public boolean isCompleted() { return isRequestCompleted(); return requestAutofill.isDone() && !requestAutofill.isCancelled(); } @Override public void cancel() { synchronized (mLock) { final boolean cancelled = isCancelledLocked(); final ICancellationSignal cancellation = mCancellation; if (!cancelled) { try { cancellation.cancel(); } catch (RemoteException e) { Slog.e(mTag, "Error requesting a cancellation", e); } } } public void onCancellable(ICancellationSignal cancellation) { if (requestAutofill.isCancelled()) { dispatchCancellation(cancellation); } else { cancellationRef.set(cancellation); } }; } @Override public void run() { synchronized (mLock) { if (isCancelledLocked()) { if (sDebug) Slog.d(mTag, "run() called after canceled"); return; } public void cancel() { requestAutofill.cancel(true); } final RemoteAugmentedAutofillService remoteService = getService(); if (remoteService == null) return; final IResultReceiver receiver = new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) throws RemoteException { final IBinder realClient = resultData .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT); remoteService.mService.onFillRequest(mSessionId, realClient, mTaskId, mActivityComponent, mFocusedId, mFocusedValue, mRequestTime, mCallback); }); } }; // TODO(b/122728762): set cancellation signal, timeout (from both mClient and service), // cache IAugmentedAutofillManagerClient reference, etc... try { mClient.getAugmentedAutofillClient(receiver); } catch (RemoteException e) { }); return requestAutofill; }).orTimeout(mRequestTimeoutMs, TimeUnit.MILLISECONDS) .whenComplete((res, err) -> { if (err instanceof CancellationException) { dispatchCancellation(cancellationRef.get()); } else if (err instanceof TimeoutException) { // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with // the timeout Slog.w(TAG, "PendingAutofillRequest timed out (" + mRequestTimeoutMs + "ms) for " + RemoteAugmentedAutofillService.this); // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks dispatchCancellation(cancellationRef.get()); } else if (err != null) { Slog.e(TAG, "exception handling getAugmentedAutofillClient() for " + mSessionId + ": " + e); finish(); } } @Override protected void onTimeout(RemoteAugmentedAutofillService remoteService) { // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the // timeout Slog.w(TAG, "PendingAutofillRequest timed out (" + remoteService.mRequestTimeoutMs + "ms) for " + remoteService); + sessionId + ": ", err); } else { // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks final ICancellationSignal cancellation; synchronized (mLock) { cancellation = mCancellation; } if (cancellation != null) { remoteService.dispatchOnFillTimeout(cancellation); } finish(); }); } @Override public boolean cancel() { if (!super.cancel()) return false; final ICancellationSignal cancellation; synchronized (mLock) { cancellation = mCancellation; void dispatchCancellation(@Nullable ICancellationSignal cancellation) { if (cancellation == null) { return; } if (cancellation != null) { Handler.getMain().post(() -> { try { cancellation.cancel(); } catch (RemoteException e) { Slog.e(mTag, "Error cancelling a fill request", e); Slog.e(TAG, "Error requesting a cancellation", e); } }); } return true; @Override public String toString() { return "RemoteAugmentedAutofillService[" + ComponentName.flattenToShortString(mComponentName) + "]"; } /** * Called by {@link Session} when it's time to destroy all augmented autofill requests. */ public void onDestroyAutofillWindowsRequest() { fireAndForget((s) -> s.onDestroyAllFillWindowsRequest()); } public interface RemoteAugmentedAutofillServiceCallbacks extends VultureCallback<RemoteAugmentedAutofillService> { extends AbstractRemoteService.VultureCallback<RemoteAugmentedAutofillService> { // NOTE: so far we don't need to notify the callback implementation (an inner class on // AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this // callback interface is empty. Loading Loading
core/java/android/util/DebugUtils.java +20 −0 Original line number Diff line number Diff line Loading @@ -24,7 +24,11 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; /** * <p>Various utilities for debugging and logging.</p> Loading Loading @@ -270,4 +274,20 @@ public class DebugUtils { private static String constNameWithoutPrefix(String prefix, Field field) { return field.getName().substring(prefix.length()); } /** * Returns method names from current stack trace, where {@link StackTraceElement#getClass} * starts with the given classes name * * @hide */ public static List<String> callersWithin(Class<?> cls, int offset) { List<String> result = Arrays.stream(Thread.currentThread().getStackTrace()) .skip(offset + 3) .filter(st -> st.getClassName().startsWith(cls.getName())) .map(StackTraceElement::getMethodName) .collect(Collectors.toList()); Collections.reverse(result); return result; } }
core/java/com/android/internal/infra/AndroidFuture.java 0 → 100644 +226 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.infra; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; import android.os.Message; import android.util.ExceptionUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; import java.util.function.Function; /** * A customized {@link CompletableFuture} with focus on reducing the number of allocations involved * in a typical future usage scenario for Android. * * In particular this involves allocations optimizations in: * <ul> * <li>{@link #thenCompose(Function)}</li> * <li>{@link #orTimeout(long, TimeUnit)}</li> * <li>{@link #whenComplete(BiConsumer)}</li> * </ul> * * @param <T> see {@link CompletableFuture} */ public class AndroidFuture<T> extends CompletableFuture<T> { private static final String LOG_TAG = AndroidFuture.class.getSimpleName(); @GuardedBy("this") private @Nullable BiConsumer<? super T, ? super Throwable> mListener; private @NonNull Handler mTimeoutHandler = Handler.getMain(); @Override public boolean complete(@Nullable T value) { boolean changed = super.complete(value); if (changed) { onCompleted(value, null); } return changed; } @Override public boolean completeExceptionally(@NonNull Throwable ex) { boolean changed = super.completeExceptionally(ex); if (changed) { onCompleted(null, ex); } return super.completeExceptionally(ex); } private void onCompleted(@Nullable T res, @Nullable Throwable err) { cancelTimeout(); BiConsumer<? super T, ? super Throwable> listener; synchronized (this) { listener = mListener; mListener = null; } if (listener != null) { callListener(listener, res, err); } } @Override public AndroidFuture<T> whenComplete( @NonNull BiConsumer<? super T, ? super Throwable> action) { Preconditions.checkNotNull(action); synchronized (this) { if (!isDone()) { BiConsumer<? super T, ? super Throwable> oldListener = mListener; mListener = oldListener == null ? action : (res, err) -> { callListener(oldListener, res, err); callListener(action, res, err); }; return this; } } // isDone() == true at this point T res = null; Throwable err = null; try { res = get(); } catch (ExecutionException e) { err = e.getCause(); } catch (Throwable e) { err = e; } callListener(action, res, err); return this; } /** * Calls the provided listener, handling any exceptions that may arise. */ // package-private to avoid synthetic method when called from lambda static <TT> void callListener( @NonNull BiConsumer<? super TT, ? super Throwable> listener, @Nullable TT res, @Nullable Throwable err) { try { try { listener.accept(res, err); } catch (Throwable t) { if (err == null) { // listener happy-case threw, but exception case might not throw, so report the // same exception thrown by listener's happy-path to it again listener.accept(null, t); } else { // listener exception-case threw // give up on listener but preserve the original exception when throwing up ExceptionUtils.getRootCause(t).initCause(err); throw t; } } } catch (Throwable t2) { // give up on listener and log the result & exception to logcat Log.e(LOG_TAG, "Failed to call whenComplete listener. res = " + res, t2); } } /** @inheritDoc */ //@Override //TODO uncomment once java 9 APIs are exposed to frameworks public AndroidFuture<T> orTimeout(long timeout, @NonNull TimeUnit unit) { Message msg = PooledLambda.obtainMessage(AndroidFuture::triggerTimeout, this); msg.obj = this; mTimeoutHandler.sendMessageDelayed(msg, unit.toMillis(timeout)); return this; } void triggerTimeout() { cancelTimeout(); if (!isDone()) { completeExceptionally(new TimeoutException()); } } protected void cancelTimeout() { mTimeoutHandler.removeCallbacksAndMessages(this); } /** * Specifies the handler on which timeout is to be triggered */ public AndroidFuture<T> setTimeoutHandler(@NonNull Handler h) { cancelTimeout(); mTimeoutHandler = Preconditions.checkNotNull(h); return this; } @Override public <U> AndroidFuture<U> thenCompose( @NonNull Function<? super T, ? extends CompletionStage<U>> fn) { return (AndroidFuture<U>) new ThenCompose<>(this, fn); } private static class ThenCompose<T, U> extends AndroidFuture<Object> implements BiConsumer<Object, Throwable> { private final AndroidFuture<T> mSource; private Function<? super T, ? extends CompletionStage<U>> mFn; ThenCompose(@NonNull AndroidFuture<T> source, @NonNull Function<? super T, ? extends CompletionStage<U>> fn) { mSource = source; mFn = Preconditions.checkNotNull(fn); // subscribe to first job completion source.whenComplete(this); } @Override public void accept(Object res, Throwable err) { Function<? super T, ? extends CompletionStage<U>> fn; synchronized (this) { fn = mFn; mFn = null; } if (fn != null) { // first job completed CompletionStage<U> secondJob; try { secondJob = Preconditions.checkNotNull(fn.apply((T) res)); } catch (Throwable t) { completeExceptionally(t); return; } // subscribe to second job completion secondJob.whenComplete(this); } else { // second job completed if (err != null) { completeExceptionally(err); } else { complete(res); } } } } }
core/java/com/android/internal/infra/ServiceConnector.java 0 → 100644 +699 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +2 −2 Original line number Diff line number Diff line Loading @@ -1112,7 +1112,7 @@ final class AutofillManagerServiceImpl final RemoteAugmentedAutofillService remoteService = mRemoteAugmentedAutofillService; if (remoteService != null) { remoteService.destroy(); remoteService.unbind(); } mRemoteAugmentedAutofillService = null; } Loading @@ -1135,7 +1135,7 @@ final class AutofillManagerServiceImpl Slog.v(TAG, "updateRemoteAugmentedAutofillService(): " + "destroying old remote service"); } mRemoteAugmentedAutofillService.destroy(); mRemoteAugmentedAutofillService.unbind(); mRemoteAugmentedAutofillService = null; mRemoteAugmentedAutofillServiceInfo = null; resetAugmentedAutofillWhitelistLocked(); Loading
services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +106 −188 Original line number Diff line number Diff line Loading @@ -25,9 +25,11 @@ import android.annotation.UserIdInt; import android.app.AppGlobals; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.ICancellationSignal; import android.os.RemoteException; Loading @@ -42,30 +44,39 @@ import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import com.android.internal.infra.AbstractSinglePendingRequestRemoteService; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; import com.android.internal.os.IResultReceiver; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; final class RemoteAugmentedAutofillService extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService, IAugmentedAutofillService> { extends ServiceConnector.Impl<IAugmentedAutofillService> { private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName(); private final int mIdleUnbindTimeoutMs; private final int mRequestTimeoutMs; private final ComponentName mComponentName; RemoteAugmentedAutofillService(Context context, ComponentName serviceName, int userId, RemoteAugmentedAutofillServiceCallbacks callbacks, boolean bindInstantServiceAllowed, boolean verbose, int idleUnbindTimeoutMs, int requestTimeoutMs) { super(context, AugmentedAutofillService.SERVICE_INTERFACE, serviceName, userId, callbacks, context.getMainThreadHandler(), bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, verbose); super(context, new Intent(AugmentedAutofillService.SERVICE_INTERFACE).setComponent(serviceName), bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, userId, IAugmentedAutofillService.Stub::asInterface); mIdleUnbindTimeoutMs = idleUnbindTimeoutMs; mRequestTimeoutMs = requestTimeoutMs; mComponentName = serviceName; // Bind right away. scheduleBind(); connect(); } @Nullable Loading Loading @@ -93,219 +104,126 @@ final class RemoteAugmentedAutofillService return new Pair<>(serviceInfo, serviceComponent); } @Override // from RemoteService protected void handleOnConnectedStateChanged(boolean state) { if (state && getTimeoutIdleBindMillis() != PERMANENT_BOUND_TIMEOUT_MS) { scheduleUnbind(); public ComponentName getComponentName() { return mComponentName; } @Override // from ServiceConnector.Impl protected void onServiceConnectionStatusChanged( IAugmentedAutofillService service, boolean connected) { try { if (state) { mService.onConnected(sDebug, sVerbose); if (connected) { service.onConnected(sDebug, sVerbose); } else { mService.onDisconnected(); service.onDisconnected(); } } catch (Exception e) { Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e); } Slog.w(TAG, "Exception calling onServiceConnectionStatusChanged(" + connected + "): ", e); } @Override // from AbstractRemoteService protected IAugmentedAutofillService getServiceInterface(IBinder service) { return IAugmentedAutofillService.Stub.asInterface(service); } @Override // from AbstractRemoteService protected long getTimeoutIdleBindMillis() { protected long getAutoDisconnectTimeoutMs() { return mIdleUnbindTimeoutMs; } @Override // from AbstractRemoteService protected long getRemoteRequestMillis() { return mRequestTimeoutMs; } /** * Called by {@link Session} to request augmented autofill. */ public void onRequestAutofillLocked(int sessionId, @NonNull IAutoFillManagerClient client, int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue) { scheduleRequest(new PendingAutofillRequest(this, sessionId, client, taskId, activityComponent, focusedId, focusedValue)); } long requestTime = SystemClock.elapsedRealtime(); AtomicReference<ICancellationSignal> cancellationRef = new AtomicReference<>(); postAsync(service -> { AndroidFuture<Void> requestAutofill = new AndroidFuture<>(); // TODO(b/122728762): set cancellation signal, timeout (from both client and service), // cache IAugmentedAutofillManagerClient reference, etc... client.getAugmentedAutofillClient(new IResultReceiver.Stub() { @Override public String toString() { return "RemoteAugmentedAutofillService[" + ComponentName.flattenToShortString(getComponentName()) + "]"; } /** * Called by {@link Session} when it's time to destroy all augmented autofill requests. */ public void onDestroyAutofillWindowsRequest() { scheduleAsyncRequest((s) -> s.onDestroyAllFillWindowsRequest()); } private void dispatchOnFillTimeout(@NonNull ICancellationSignal cancellation) { mHandler.post(() -> { try { cancellation.cancel(); } catch (RemoteException e) { Slog.w(mTag, "Error calling cancellation signal: " + e); } }); } // TODO(b/123100811): inline into PendingAutofillRequest if it doesn't have any other subclass private abstract static class MyPendingRequest extends PendingRequest<RemoteAugmentedAutofillService, IAugmentedAutofillService> { protected final int mSessionId; private MyPendingRequest(@NonNull RemoteAugmentedAutofillService service, int sessionId) { super(service); mSessionId = sessionId; } } private static final class PendingAutofillRequest extends MyPendingRequest { private final @NonNull AutofillId mFocusedId; private final @Nullable AutofillValue mFocusedValue; private final @NonNull IAutoFillManagerClient mClient; private final @NonNull ComponentName mActivityComponent; private final int mTaskId; private final long mRequestTime = SystemClock.elapsedRealtime(); private final @NonNull IFillCallback mCallback; private ICancellationSignal mCancellation; protected PendingAutofillRequest(@NonNull RemoteAugmentedAutofillService service, int sessionId, @NonNull IAutoFillManagerClient client, int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue) { super(service, sessionId); mClient = client; mTaskId = taskId; mActivityComponent = activityComponent; mFocusedId = focusedId; mFocusedValue = focusedValue; mCallback = new IFillCallback.Stub() { public void send(int resultCode, Bundle resultData) throws RemoteException { final IBinder realClient = resultData .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT); service.onFillRequest(sessionId, realClient, taskId, activityComponent, focusedId, focusedValue, requestTime, new IFillCallback.Stub() { @Override public void onSuccess() { if (!finish()) return; // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks } @Override public void onCancellable(ICancellationSignal cancellation) { synchronized (mLock) { final boolean cancelled; synchronized (mLock) { mCancellation = cancellation; cancelled = isCancelledLocked(); } if (cancelled) { try { cancellation.cancel(); } catch (RemoteException e) { Slog.e(mTag, "Error requesting a cancellation", e); } } } requestAutofill.complete(null); } @Override public boolean isCompleted() { return isRequestCompleted(); return requestAutofill.isDone() && !requestAutofill.isCancelled(); } @Override public void cancel() { synchronized (mLock) { final boolean cancelled = isCancelledLocked(); final ICancellationSignal cancellation = mCancellation; if (!cancelled) { try { cancellation.cancel(); } catch (RemoteException e) { Slog.e(mTag, "Error requesting a cancellation", e); } } } public void onCancellable(ICancellationSignal cancellation) { if (requestAutofill.isCancelled()) { dispatchCancellation(cancellation); } else { cancellationRef.set(cancellation); } }; } @Override public void run() { synchronized (mLock) { if (isCancelledLocked()) { if (sDebug) Slog.d(mTag, "run() called after canceled"); return; } public void cancel() { requestAutofill.cancel(true); } final RemoteAugmentedAutofillService remoteService = getService(); if (remoteService == null) return; final IResultReceiver receiver = new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) throws RemoteException { final IBinder realClient = resultData .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT); remoteService.mService.onFillRequest(mSessionId, realClient, mTaskId, mActivityComponent, mFocusedId, mFocusedValue, mRequestTime, mCallback); }); } }; // TODO(b/122728762): set cancellation signal, timeout (from both mClient and service), // cache IAugmentedAutofillManagerClient reference, etc... try { mClient.getAugmentedAutofillClient(receiver); } catch (RemoteException e) { }); return requestAutofill; }).orTimeout(mRequestTimeoutMs, TimeUnit.MILLISECONDS) .whenComplete((res, err) -> { if (err instanceof CancellationException) { dispatchCancellation(cancellationRef.get()); } else if (err instanceof TimeoutException) { // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with // the timeout Slog.w(TAG, "PendingAutofillRequest timed out (" + mRequestTimeoutMs + "ms) for " + RemoteAugmentedAutofillService.this); // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks dispatchCancellation(cancellationRef.get()); } else if (err != null) { Slog.e(TAG, "exception handling getAugmentedAutofillClient() for " + mSessionId + ": " + e); finish(); } } @Override protected void onTimeout(RemoteAugmentedAutofillService remoteService) { // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the // timeout Slog.w(TAG, "PendingAutofillRequest timed out (" + remoteService.mRequestTimeoutMs + "ms) for " + remoteService); + sessionId + ": ", err); } else { // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks final ICancellationSignal cancellation; synchronized (mLock) { cancellation = mCancellation; } if (cancellation != null) { remoteService.dispatchOnFillTimeout(cancellation); } finish(); }); } @Override public boolean cancel() { if (!super.cancel()) return false; final ICancellationSignal cancellation; synchronized (mLock) { cancellation = mCancellation; void dispatchCancellation(@Nullable ICancellationSignal cancellation) { if (cancellation == null) { return; } if (cancellation != null) { Handler.getMain().post(() -> { try { cancellation.cancel(); } catch (RemoteException e) { Slog.e(mTag, "Error cancelling a fill request", e); Slog.e(TAG, "Error requesting a cancellation", e); } }); } return true; @Override public String toString() { return "RemoteAugmentedAutofillService[" + ComponentName.flattenToShortString(mComponentName) + "]"; } /** * Called by {@link Session} when it's time to destroy all augmented autofill requests. */ public void onDestroyAutofillWindowsRequest() { fireAndForget((s) -> s.onDestroyAllFillWindowsRequest()); } public interface RemoteAugmentedAutofillServiceCallbacks extends VultureCallback<RemoteAugmentedAutofillService> { extends AbstractRemoteService.VultureCallback<RemoteAugmentedAutofillService> { // NOTE: so far we don't need to notify the callback implementation (an inner class on // AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this // callback interface is empty. Loading