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

Commit 8d7f7f4f authored by Felipe Leme's avatar Felipe Leme
Browse files

Added new API to support multi-activity autofill workflows.

For example, when an app uses one activity to enter username, another for
password.

Bug: 112051762

Test: atest MultiScreenLoginTest # new API
Test: atest CtsAutoFillServiceTestCases # to make sure it doesn't break anything
Test: m -j update-api

Change-Id: Ia95888901ab72f75938efb87103cd76f5ec8ce09
parent ff661430
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -39151,6 +39151,7 @@ package android.service.autofill {
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
    field public static final int FLAG_DELAY_SAVE = 4; // 0x4
    field public static final int FLAG_DONT_SAVE_ON_FINISH = 2; // 0x2
    field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
    field public static final int NEGATIVE_BUTTON_STYLE_CANCEL = 0; // 0x0
@@ -57736,14 +57737,14 @@ package java.lang {
    method public void setContextClassLoader(java.lang.ClassLoader);
    method public final void setDaemon(boolean);
    method public static void setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler);
    method public final void setName(java.lang.String);
    method public final synchronized void setName(java.lang.String);
    method public final void setPriority(int);
    method public void setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler);
    method public static void sleep(long) throws java.lang.InterruptedException;
    method public static void sleep(long, int) throws java.lang.InterruptedException;
    method public synchronized void start();
    method public final deprecated void stop();
    method public final deprecated void stop(java.lang.Throwable);
    method public final deprecated synchronized void stop(java.lang.Throwable);
    method public final deprecated void suspend();
    method public static void yield();
    field public static final int MAX_PRIORITY = 10; // 0xa
+3 −8
Original line number Diff line number Diff line
@@ -18,21 +18,13 @@ package android.app;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.service.voice.IVoiceInteractionSession;
import android.util.SparseIntArray;
import android.view.RemoteAnimationAdapter;

import com.android.internal.app.IVoiceInteractor;

import java.util.ArrayList;
import java.util.List;

@@ -231,4 +223,7 @@ public abstract class ActivityManagerInternal {

    /** Schedule the execution of all pending app GCs. */
    public abstract void scheduleAppGcs();

    /** Gets the task id for a given activity. */
    public abstract int getTaskIdForActivity(@NonNull IBinder token, boolean onlyRoot);
}
+28 −8
Original line number Diff line number Diff line
@@ -119,7 +119,8 @@ import java.util.Arrays;
 *
 * <p>But it is only triggered when all conditions below are met:
 * <ul>
 *   <li>The {@link SaveInfo} associated with the {@link FillResponse} is not {@code null}.
 *   <li>The {@link SaveInfo} associated with the {@link FillResponse} is not {@code null} neither
 *       has the {@link #FLAG_DELAY_SAVE} flag.
 *   <li>The {@link AutofillValue}s of all required views (as set by the {@code requiredIds} passed
 *       to the {@link SaveInfo.Builder} constructor are not empty.
 *   <li>The {@link AutofillValue} of at least one view (be it required or optional) has changed
@@ -234,10 +235,26 @@ public final class SaveInfo implements Parcelable {
     */
    public static final int FLAG_DONT_SAVE_ON_FINISH = 0x2;

    /**
     * Don't trigger the autofill save UI when the autofill context associated with the response
     * associated with this {@link SaveInfo} is {@link AutofillManager#commit() committed},
     * but keep its {@link FillContext} so it's delivered in a future
     * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback) save request} of an
     * activity belonging to the same task.
     *
     * <p>This flag should be used when the service detects that the application uses
     * multiple screens to implement an autofillable workflow (for example, one screen for the
     * username field, another for password).
     */
    // TODO(b/112051762): improve documentation: add example, document relationship with other
    // flagss, etc...
    public static final int FLAG_DELAY_SAVE = 0x4;

    /** @hide */
    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
            FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE,
            FLAG_DONT_SAVE_ON_FINISH
            FLAG_DONT_SAVE_ON_FINISH,
            FLAG_DELAY_SAVE
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface SaveInfoFlags{}
@@ -410,14 +427,15 @@ public final class SaveInfo implements Parcelable {
         * Sets flags changing the save behavior.
         *
         * @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE},
         * {@link #FLAG_DONT_SAVE_ON_FINISH}, or {@code 0}.
         * {@link #FLAG_DONT_SAVE_ON_FINISH}, {@link #FLAG_DELAY_SAVE}, or {@code 0}.
         * @return This builder.
         */
        public @NonNull Builder setFlags(@SaveInfoFlags int flags) {
            throwIfDestroyed();

            mFlags = Preconditions.checkFlagsArgument(flags,
                    FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE | FLAG_DONT_SAVE_ON_FINISH);
                    FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE | FLAG_DONT_SAVE_ON_FINISH
                            | FLAG_DELAY_SAVE);
            return this;
        }

@@ -663,14 +681,16 @@ public final class SaveInfo implements Parcelable {
         * Builds a new {@link SaveInfo} instance.
         *
         * @throws IllegalStateException if no
         * {@link #SaveInfo.Builder(int, AutofillId[]) required ids}
         * or {@link #setOptionalIds(AutofillId[]) optional ids} were set
         * {@link #SaveInfo.Builder(int, AutofillId[]) required ids},
         * or {@link #setOptionalIds(AutofillId[]) optional ids}, or {@link #FLAG_DELAY_SAVE}
         * were set
         */
        public SaveInfo build() {
            throwIfDestroyed();
            Preconditions.checkState(
                    !ArrayUtils.isEmpty(mRequiredIds) || !ArrayUtils.isEmpty(mOptionalIds),
                    "must have at least one required or optional id");
                    !ArrayUtils.isEmpty(mRequiredIds) || !ArrayUtils.isEmpty(mOptionalIds)
                            || (mFlags & FLAG_DELAY_SAVE) != 0,
                    "must have at least one required or optional id or FLAG_DELAYED_SAVE");
            mDestroyed = true;
            return new SaveInfo(this);
        }
+6 −0
Original line number Diff line number Diff line
@@ -52,6 +52,12 @@ public final class SaveRequest implements Parcelable {
    }

    /**
     * Gets the contexts associated with each previous fill request.
     *
     * <p><b>Note:</b> Starting on Android {@link android.os.Build.VERSION_CODES#Q}, it could also
     * include contexts from requests whose {@link SaveInfo} had the
     * {@link SaveInfo#FLAG_DELAY_SAVE} flag.
     *
     * @return The contexts associated with each previous fill request.
     */
    public @NonNull List<FillContext> getFillContexts() {
+12 −4
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -42,7 +43,6 @@ import android.database.ContentObserver;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -963,12 +963,20 @@ public final class AutofillManagerService extends SystemService {
                throw new IllegalArgumentException(packageName + " is not a valid package", e);
            }

            // TODO(b/112051762): rather than always call AM here, call it on demand on
            // getPreviousSessionsLocked()? That way we save space / time here, and don't set
            // a callback on AM unnecessarily (see TODO below :-)
            final ActivityManagerInternal am = LocalServices
                    .getService(ActivityManagerInternal.class);
            // TODO(b/112051762): add a callback method on AM to be notified when a task is finished
            // so we can clean up sessions kept alive
            final int taskId = am.getTaskIdForActivity(activityToken, false);
            final int sessionId;
            synchronized (mLock) {
                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
                sessionId = service.startSessionLocked(activityToken, getCallingUid(), appCallback,
                        autofillId, bounds, value, hasCallback, componentName, compatMode,
                        mAllowInstantService, flags);
                sessionId = service.startSessionLocked(activityToken, taskId, getCallingUid(),
                        appCallback, autofillId, bounds, value, hasCallback, componentName,
                        compatMode, mAllowInstantService, flags);
            }
            send(receiver, sessionId);
        }
Loading