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

Commit ea95a6d7 authored by Felipe Leme's avatar Felipe Leme
Browse files

Refactored AutofillManager service classes into common code.

Autofill has the following workflow:

- AutofillManager talks to an IAutofillManagerService service located on
  system_server
- AutofillManagerService implements the IAutofillManagerService and delegates
  each call to the AutofillManagerServiceImpl associated with the given Android user
- AutofillManagerServiceImpl uses a RemoteFillService class that is
  responsible for binding to the service that is provided by a 3rd-party
- Plus a lot of other plumbing, like getting the name of the package that
  provides the service using Settings, listening to Settings changes, etc...

A lot of this "plumbing" is common to other system services
(like TextClassificationManager), so it makes sense to move this logic to common
code that can be used.

This CL refactors the "main" service classes (AutofillManagerService and
AutofillManagerServiceImpl), while RemoteFillService will be refactored later.

Bug: 117779333
Test: atest CtsAutoFillServiceTestCases

Change-Id: I19bae47b72096e66bd51c3cd6b68f9d3cf8ea708
parent c8c634f3
Loading
Loading
Loading
Loading
+176 −373

File changed.

Preview size limit exceeded, changes collapsed.

+39 −162
Original line number Diff line number Diff line
@@ -25,19 +25,14 @@ import static com.android.server.autofill.Helper.sVerbose;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
import android.os.AsyncTask;
import android.os.Binder;
@@ -49,7 +44,6 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
@@ -60,7 +54,6 @@ import android.service.autofill.FillEventHistory.Event;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.service.autofill.UserData;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
@@ -77,6 +70,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.AbstractPerUserSystemService;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.ui.AutoFillUI;
@@ -91,7 +85,8 @@ import java.util.Random;
 * app's {@link IAutoFillService} implementation.
 *
 */
final class AutofillManagerServiceImpl {
final class AutofillManagerServiceImpl
        extends AbstractPerUserSystemService<AutofillManagerServiceImpl> {

    private static final String TAG = "AutofillManagerServiceImpl";
    private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
@@ -99,9 +94,6 @@ final class AutofillManagerServiceImpl {
    /** Minimum interval to prune abandoned sessions */
    private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;

    private final int mUserId;
    private final Context mContext;
    private final Object mLock;
    private final AutoFillUI mUi;
    private final MetricsLogger mMetricsLogger = new MetricsLogger();

@@ -131,24 +123,12 @@ final class AutofillManagerServiceImpl {
    @GuardedBy("mLock")
    private ArrayMap<ComponentName, Long> mDisabledActivities;

    /**
     * Whether service was disabled for user due to {@link UserManager} restrictions.
     */
    @GuardedBy("mLock")
    private boolean mDisabled;

    /**
     * Data used for field classification.
     */
    @GuardedBy("mLock")
    private UserData mUserData;

    /**
     * Caches whether the setup completed for the current user.
     */
    @GuardedBy("mLock")
    private boolean mSetupComplete;

    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);

    /**
@@ -170,116 +150,27 @@ final class AutofillManagerServiceImpl {
    /** When was {@link PruneTask} last executed? */
    private long mLastPrune = 0;

    AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
    AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog requestsHistory,
            LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
            AutofillCompatState autofillCompatState, boolean disabled) {
        mContext = context;
        mLock = lock;
        super(master, lock, userId);

        mRequestsHistory = requestsHistory;
        mUiLatencyHistory = uiLatencyHistory;
        mWtfHistory = wtfHistory;
        mUserId = userId;
        mUi = ui;
        mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId);
        mFieldClassificationStrategy = new FieldClassificationStrategy(getContext(), userId);
        mAutofillCompatState = autofillCompatState;
        updateLocked(disabled);
    }

    @GuardedBy("mLock")
    private int getServiceUidLocked() {
        if (mInfo == null) {
            Slog.w(TAG,  "getServiceUidLocked(): no mInfo");
            return -1;
        }
        return mInfo.getServiceInfo().applicationInfo.uid;
    }


    @Nullable
    String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
        return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
    }

    @Nullable
    String getServicePackageName() {
        final ComponentName serviceComponent = getServiceComponentName();
        if (serviceComponent != null) {
            return serviceComponent.getPackageName();
        }
        return null;
    }

    @Nullable
    ComponentName getServiceComponentName() {
        synchronized (mLock) {
            if (mInfo == null) {
                return null;
            }
            return mInfo.getServiceInfo().getComponentName();
        }
    }

    int getTargedSdkLocked() {
        if (mInfo == null) {
            return 0;
        }
        return mInfo.getServiceInfo().applicationInfo.targetSdkVersion;
    }

    private boolean isSetupCompletedLocked() {
        final String setupComplete = Settings.Secure.getStringForUser(
                mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
        return "1".equals(setupComplete);
    }

    private String getComponentNameFromSettings() {
        return Settings.Secure.getStringForUser(
                mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
    }

    @GuardedBy("mLock")
    void updateLocked(boolean disabled) {
        final boolean wasEnabled = isEnabledLocked();
        if (sVerbose) {
            Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
                    + ", mSetupComplete= " + mSetupComplete
                    + ", disabled=" + disabled + ", mDisabled=" + mDisabled);
        }
        mSetupComplete = isSetupCompletedLocked();
        mDisabled = disabled;
        ComponentName serviceComponent = null;
        ServiceInfo serviceInfo = null;
        final String componentName = getComponentNameFromSettings();
        if (!TextUtils.isEmpty(componentName)) {
            try {
                serviceComponent = ComponentName.unflattenFromString(componentName);
                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
                        0, mUserId);
                if (serviceInfo == null) {
                    Slog.e(TAG, "Bad AutofillService name: " + componentName);
                }
            } catch (RuntimeException | RemoteException e) {
                Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
                serviceInfo = null;
            }
        }
        try {
            if (serviceInfo != null) {
                mInfo = new AutofillServiceInfo(mContext, serviceComponent, mUserId);
                if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
            } else {
                mInfo = null;
                if (sDebug) {
                    Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")");
                }
            }
        } catch (Exception e) {
            Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
            mInfo = null;
        }
        final boolean isEnabled = isEnabledLocked();
        if (wasEnabled != isEnabled) {
            if (!isEnabled) {
    @Override // from PerUserSystemService
    protected boolean updateLocked(boolean disabled) {
        destroySessionsLocked();
        final boolean enabledChanged = super.updateLocked(disabled);
        if (enabledChanged) {
            if (!isEnabledLocked()) {
                final int sessionCount = mSessions.size();
                for (int i = sessionCount - 1; i >= 0; i--) {
                    final Session session = mSessions.valueAt(i);
@@ -288,6 +179,19 @@ final class AutofillManagerServiceImpl {
            }
            sendStateToClients(false);
        }
        return enabledChanged;
    }

    @Override // from PerUserSystemService
    protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
            throws NameNotFoundException {
        mInfo = new AutofillServiceInfo(getContext(), serviceComponent, mUserId);
        return mInfo.getServiceInfo();
    }

    @Nullable
    String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
        return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
    }

    @GuardedBy("mLock")
@@ -469,7 +373,7 @@ final class AutofillManagerServiceImpl {
            if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
                mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
                        componentName.getPackageName());
                Settings.Secure.putStringForUser(mContext.getContentResolver(),
                Settings.Secure.putStringForUser(getContext().getContentResolver(),
                        Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
                destroySessionsLocked();
            } else {
@@ -501,7 +405,7 @@ final class AutofillManagerServiceImpl {

        assertCallerLocked(componentName, compatMode);

        final Session newSession = new Session(this, mUi, mContext, mHandler, mUserId, mLock,
        final Session newSession = new Session(this, mUi, getContext(), mHandler, mUserId, mLock,
                sessionId, taskId, uid, activityToken, appCallbackToken, hasCallback,
                mUiLatencyHistory, mWtfHistory, mInfo.getServiceInfo().getComponentName(),
                componentName, compatMode, bindInstantServiceAllowed, flags);
@@ -515,7 +419,7 @@ final class AutofillManagerServiceImpl {
     */
    private void assertCallerLocked(@NonNull ComponentName componentName, boolean compatMode) {
        final String packageName = componentName.getPackageName();
        final PackageManager pm = mContext.getPackageManager();
        final PackageManager pm = getContext().getPackageManager();
        final int callingUid = Binder.getCallingUid();
        final int packageUid;
        try {
@@ -651,7 +555,8 @@ final class AutofillManagerServiceImpl {
    }

    @GuardedBy("mLock")
    void handlePackageUpdateLocked(String packageName) {
    @Override // from PerUserSystemService
    protected void handlePackageUpdateLocked(@NonNull String packageName) {
        final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
        if (serviceInfo != null && serviceInfo.packageName.equals(packageName)) {
            resetExtServiceLocked();
@@ -690,29 +595,6 @@ final class AutofillManagerServiceImpl {
        }
    }

    /**
     * Gets the user-visibile name of the service this service binds to, or {@code null} if the
     * service is disabled.
     */
    @Nullable
    @GuardedBy("mLock")
    public CharSequence getServiceLabelLocked() {
        return mInfo == null ? null : mInfo.getServiceInfo().loadSafeLabel(
                mContext.getPackageManager(), 0 /* do not ellipsize */,
                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
    }

    /**
     * Gets the icon of the service this service binds to, or {@code null} if the service is
     * disabled.
     */
    @NonNull
    @Nullable
    @GuardedBy("mLock")
    Drawable getServiceIconLocked() {
        return mInfo == null ? null : mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
    }

    /**
     * Initializes the last fill selection after an autofill service returned a new
     * {@link FillResponse}.
@@ -941,11 +823,13 @@ final class AutofillManagerServiceImpl {
        return true;
    }

    @Override
    @GuardedBy("mLock")
    void dumpLocked(String prefix, PrintWriter pw) {
    protected void dumpLocked(String prefix, PrintWriter pw) {
        super.dumpLocked(prefix, pw);

        final String prefix2 = prefix + "  ";

        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
        pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked());
        pw.print(prefix); pw.print("Autofill Service Info: ");
        if (mInfo == null) {
@@ -958,9 +842,8 @@ final class AutofillManagerServiceImpl {
        }
        pw.print(prefix); pw.print("Component from settings: ");
            pw.println(getComponentNameFromSettings());
        pw.print(prefix); pw.print("Default component: ");
            pw.println(mContext.getString(R.string.config_defaultAutofillService));
        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
        pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
                .getString(R.string.config_defaultAutofillService));
        pw.print(prefix); pw.print("Field classification enabled: ");
            pw.println(isFieldClassificationEnabledLocked());
        pw.print(prefix); pw.print("Compat pkgs: ");
@@ -970,7 +853,6 @@ final class AutofillManagerServiceImpl {
        } else {
            pw.println(compatPkgs);
        }
        pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
        pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);

        pw.print(prefix); pw.print("Disabled apps: ");
@@ -1158,11 +1040,6 @@ final class AutofillManagerServiceImpl {
        return true;
    }

    @GuardedBy("mLock")
    boolean isEnabledLocked() {
        return mSetupComplete && mInfo != null && !mDisabled;
    }

    /**
     * Called by {@link Session} when service asked to disable autofill for an app.
     */
@@ -1270,7 +1147,7 @@ final class AutofillManagerServiceImpl {
    // Called by internally, no need to check UID.
    boolean isFieldClassificationEnabledLocked() {
        return Settings.Secure.getIntForUser(
                mContext.getContentResolver(),
                getContext().getContentResolver(),
                Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1,
                mUserId) == 1;
    }
+4 −20
Original line number Diff line number Diff line
@@ -28,20 +28,21 @@ import android.util.ArraySet;
import android.util.Slog;
import android.view.WindowManager;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;

import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;

import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.LinkedList;

public final class Helper {

    private static final String TAG = "AutofillHelper";

    // TODO(b/117779333): get rid of sDebug / sVerbose and always use the service variables instead

    /**
     * Defines a logging flag that can be dynamically changed at runtime using
     * {@code cmd autofill set log_level debug} or through
@@ -56,23 +57,6 @@ public final class Helper {
     */
    public static boolean sVerbose = false;

    /**
     * Maximum number of partitions that can be allowed in a session.
     *
     * <p>Can be modified using {@code cmd autofill set max_partitions} or through
     * {@link android.provider.Settings.Global#AUTOFILL_MAX_PARTITIONS_SIZE}.
     */
    static int sPartitionMaxCount = AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE;

    /**
     * Maximum number of visible datasets in the dataset picker UI, or {@code 0} to use default
     * value from resources.
     *
     * <p>Can be modified using {@code cmd autofill set max_visible_datasets} or through
     * {@link android.provider.Settings.Global#AUTOFILL_MAX_VISIBLE_DATASETS}.
     */
    public static int sVisibleDatasetsMaxCount = 0;

    /**
     * When non-null, overrides whether the UI should be shown on full-screen mode.
     *
@@ -162,7 +146,7 @@ public final class Helper {

    private static ViewNode findViewNode(@NonNull AssistStructure structure,
            @NonNull ViewNodeFilter filter) {
        final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
        final ArrayDeque<ViewNode> nodesToProcess = new ArrayDeque<>();
        final int numWindowNodes = structure.getWindowNodeCount();
        for (int i = 0; i < numWindowNodes; i++) {
            nodesToProcess.add(structure.getWindowNodeAt(i).getRootViewNode());
+8 −8
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.Helper.getNumericValue;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sPartitionMaxCount;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.toArray;
import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
@@ -65,7 +64,6 @@ import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
import android.service.autofill.FieldClassification;
import android.service.autofill.FieldClassification.Match;
import android.text.TextUtils;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
@@ -75,10 +73,10 @@ import android.service.autofill.SaveInfo;
import android.service.autofill.SaveRequest;
import android.service.autofill.UserData;
import android.service.autofill.ValueFinder;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -2095,9 +2093,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        }

        final int numResponses = mResponses.size();
        if (numResponses >= sPartitionMaxCount) {
        if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
            Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
                    + " reached maximum of " + sPartitionMaxCount);
                    + " reached maximum of " + AutofillManagerService.getPartitionMaxCount());
            return false;
        }

@@ -2289,7 +2287,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);

                // Remove the UI if the ViewState has changed.
                if (mCurrentViewId != viewState.id) {
                if (!Objects.equals(mCurrentViewId, viewState.id)) {
                    mUi.hideFillUi(this);
                    mCurrentViewId = viewState.id;
                }
@@ -2298,7 +2296,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                viewState.update(value, virtualBounds, flags);
                break;
            case ACTION_VIEW_EXITED:
                if (mCurrentViewId == viewState.id) {
                if (Objects.equals(mCurrentViewId, viewState.id)) {
                    if (sVerbose) Slog.d(TAG, "Exiting view " + id);
                    mUi.hideFillUi(this);
                    mCurrentViewId = null;
@@ -3077,7 +3075,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState

    private void wtf(@Nullable Exception e, String fmt, Object...args) {
        final String message = String.format(fmt, args);
        synchronized (mLock) {
            mWtfHistory.log(message);
        }

        if (e != null) {
            Slog.wtf(TAG, message, e);
+5 −7
Original line number Diff line number Diff line
@@ -19,25 +19,22 @@ import static com.android.server.autofill.Helper.paramsToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.ContextThemeWrapper;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.service.autofill.Dataset;
import android.service.autofill.Dataset.DatasetFieldFilter;
import android.service.autofill.FillResponse;
import android.text.TextUtils;
import android.util.Slog;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -60,6 +57,7 @@ import android.widget.TextView;

import com.android.internal.R;
import com.android.server.UiThread;
import com.android.server.autofill.AutofillManagerService;
import com.android.server.autofill.Helper;

import java.io.PrintWriter;
@@ -193,8 +191,8 @@ final class FillUi {
            }
        });

        if (sVisibleDatasetsMaxCount > 0) {
            mVisibleDatasetsMaxCount = sVisibleDatasetsMaxCount;
        if (AutofillManagerService.getVisibleDatasetsMaxCount() > 0) {
            mVisibleDatasetsMaxCount = AutofillManagerService.getVisibleDatasetsMaxCount();
            if (sVerbose) {
                Slog.v(TAG, "overriding maximum visible datasets to " + mVisibleDatasetsMaxCount);
            }
Loading