Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 627a6d1b authored by Kai Li's avatar Kai Li
Browse files

Support autofilling a remote app.

This is only intended for system components like SystemUI.

Design: go/sysui-underlay

Bug: 403422950
Flag: android.view.contentcapture.flags.enable_system_ui_underlay
Change-Id: Ie58c804665d91f4e91a9fdf37687b63efac5ca54
Test: atest FrameworksCoreTests:ActivityThreadTest
parent 46f4150c
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -155,6 +155,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.autofill.AutofillClientController;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager.AutofillClient;
import android.view.autofill.AutofillValue;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
@@ -10109,4 +10110,19 @@ public class Activity extends ContextThemeWrapper
            mJankTracker.disableAppJankTracking();
        }
    }

    final void autofillViewIfAvailable(
            @NonNull AutofillId targetAutofillId, @NonNull AutofillValue autofillValue) {
        if (false) Log.v(TAG, "autofill view in client app Activity, id: " + targetAutofillId);
        runOnUiThread(
                () -> {
                    View view =
                            getAutofillClientController()
                                    .autofillClientFindViewByAutofillIdTraversal(targetAutofillId);
                    if (view != null) {
                        // TODO: b/410146465 support virtual views
                        view.autofill(autofillValue);
                    }
                });
    }
}
+15 −0
Original line number Diff line number Diff line
@@ -217,6 +217,8 @@ import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.view.contentcapture.IContentCaptureManager;
import android.view.contentcapture.IContentCaptureOptionsCallback;
import android.view.translation.TranslationSpec;
@@ -4672,6 +4674,19 @@ public final class ActivityThread extends ClientTransactionHandler
                resultCallback.sendResult(null);
                return;
            }

            if (actionId.equals(AutofillManager.DIRECT_ACTION_ID_REMOTE_AUTOFILL)) {
                AutofillId autofillId =
                        arguments.getParcelable(
                                AutofillManager.EXTRA_REMOTE_AUTOFILL_ID, AutofillId.class);
                AutofillValue autofillValue =
                        arguments.getParcelable(
                                AutofillManager.EXTRA_REMOTE_AUTOFILL_VALUE, AutofillValue.class);
                if (autofillId != null && autofillValue != null) {
                    r.activity.autofillViewIfAvailable(autofillId, autofillValue);
                }
            }

            final Bundle nonNullArguments = (arguments != null) ? arguments : Bundle.EMPTY;
            r.activity.onPerformDirectAction(actionId, nonNullArguments, cancellationSignal,
                    resultCallback::sendResult);
+58 −2
Original line number Diff line number Diff line
@@ -318,6 +318,32 @@ public final class AutofillManager {
    public static final String EXTRA_AUTOFILL_REQUEST_ID =
            "android.view.autofill.extra.AUTOFILL_REQUEST_ID";

    /**
     * Internal {@link DirectAction#getId()} used to pass to the {@link ActivityThread} to fill a
     * remote app.
     *
     * @hide
     */
    public static final String DIRECT_ACTION_ID_REMOTE_AUTOFILL = "android.REMOTE_AUTOFILL";

    /**
     * Internal extra used to pass an {@link AutofillId} to the {@link ActivityThread} to fill a
     * remote app.
     *
     * @hide
     */
    public static final String EXTRA_REMOTE_AUTOFILL_ID =
            "android.view.autofill.extra.REMOTE_AUTOFILL_ID";

    /**
     * Internal extra used to pass an {@link AutofillValue} to the {@link ActivityThread} to fill a
     * remote app.
     *
     * @hide
     */
    public static final String EXTRA_REMOTE_AUTOFILL_VALUE =
            "android.view.autofill.extra.REMOTE_AUTOFILL_VALUE";

    /**
     * Autofill Hint to indicate that it can match any field.
     *
@@ -3945,7 +3971,9 @@ public final class AutofillManager {
                pw.print(" ("); pw.print(client.autofillClientGetActivityToken()); pw.println(')');
            }
            pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
            pw.print(pfx); pw.print("enabledAugmentedOnly: "); pw.println(mForAugmentedAutofillOnly);
            pw.print(pfx);
            pw.print("enabledAugmentedOnly: ");
            pw.println(mForAugmentedAutofillOnly);
            pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
            pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
            pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
@@ -3958,7 +3986,9 @@ public final class AutofillManager {
                final String pfx2 = pfx + "  ";
                pw.println();
                pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
                pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
                pw.print(pfx2);
                pw.print("invisible:");
                pw.println(mTrackedViews.mInvisibleTrackedIds);
            }
            pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
            pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
@@ -4120,6 +4150,32 @@ public final class AutofillManager {
        return false;
    }

    /**
     * This method is used for a system component like SystemUI to autofill a remote app.
     *
     * @hide
     */
    public void autofillRemoteApp(
            @NonNull AutofillId autofillId,
            @NonNull String value,
            @NonNull IBinder shareableActivityToken,
            int taskId) {
        if (android.view.contentcapture.flags.Flags.enableSystemUiUnderlay() && mService != null) {
            final AutofillValue autofillValue = AutofillValue.forText(value);

            try {
                mService.autofillRemoteApp(
                        shareableActivityToken,
                        taskId,
                        autofillId,
                        autofillValue,
                        mContext.getUserId());
            } catch (Exception e) {
                Log.e(TAG, "autofillRemoteApp() - cannot autofill remote app", e);
            }
        }
    }

    /**
     * If autofill suggestions for a
     * <a href="{@docRoot}reference/android/service/autofill/Dataset.html#FillDialogUI">
+3 −0
Original line number Diff line number Diff line
@@ -72,4 +72,7 @@ oneway interface IAutoFillManager {
    void setAutofillIdsAttemptedForRefill(int sessionId, in List<AutofillId> ids, int userId);
    void notifyImeAnimationStart(int sessionId, long startTimeMs, int userId);
    void notifyImeAnimationEnd(int sessionId, long endTimeMs, int userId);
    // For SystemUI to notify AutofillManager that a remote fill has occurred.
    void autofillRemoteApp(IBinder activityToken, int taskId, in AutofillId id,
        in AutofillValue value, int userId);
}
+36 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ import android.hardware.display.VirtualDisplay;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteCallback;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -88,6 +89,10 @@ import android.util.MergedConfiguration;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.widget.EditText;
import android.window.ActivityWindowInfo;
import android.window.WindowContextInfo;
import android.window.WindowTokenClientController;
@@ -187,6 +192,33 @@ public class ActivityThreadTest {
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
    }

    @Test
    public void testPerformDirectAction_remoteAutofill() throws Exception {
        final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();

        InstrumentationRegistry.getInstrumentation().waitForIdleSync();

        EditText editText = activity.mEditText;
        AutofillId autofillId = editText.getAutofillId();
        AutofillValue autofillValue = AutofillValue.forText("test");

        Bundle bundle = new Bundle();
        bundle.putParcelable(AutofillManager.EXTRA_REMOTE_AUTOFILL_ID, autofillId);
        bundle.putParcelable(AutofillManager.EXTRA_REMOTE_AUTOFILL_VALUE, autofillValue);
        final RemoteCallback remoteCallback = new RemoteCallback((unused) -> {});
        appThread.performDirectAction(
                activity.getActivityToken(),
                AutofillManager.DIRECT_ACTION_ID_REMOTE_AUTOFILL,
                bundle,
                null,
                remoteCallback);

        InstrumentationRegistry.getInstrumentation().waitForIdleSync();

        assertEquals("test", editText.getText().toString());
    }

    @Test
    public void testResumeAfterRelaunch() throws Exception {
        final Activity activity = mActivityTestRule.launchActivity(new Intent());
@@ -1269,12 +1301,16 @@ public class ActivityThreadTest {
         */
        volatile CountDownLatch mPipUiStateLatch;

        private EditText mEditText;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            getWindow().getDecorView().setKeepScreenOn(true);
            setShowWhenLocked(true);
            setTurnScreenOn(true);
            mEditText = new EditText(this);
            setContentView(mEditText);
        }

        @Override
Loading