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

Commit f539389e authored by Philip P. Moltmann's avatar Philip P. Moltmann
Browse files

Prune abandoned autofill sessions

Regularly check if the sessions are still valid and clear the ones that
are not.

Test: 1. Started session in dialer
      2. Home button (dialer goes in background)
      3. kill -9 dialer
      4. Wait 30 seconds
      5. Start session is messenger
      6. dialer session stays

      1. Started session in dialer
      2. Home button (dialer goes in background)
      3. kill -9 dialer
      4. Swipe dialer from recents
      5. Wait 30 seconds
      5. Start session is messenger
      6. dialer session is removed
Fixes: 38005472

Change-Id: I8199ce44777b313141ee1eab6c8de5ad5089474a
parent 7c95e968
Loading
Loading
Loading
Loading
+89 −2
Original line number Diff line number Diff line
@@ -19,17 +19,21 @@ package com.android.server.autofill;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.NO_SESSION;

import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -46,6 +50,7 @@ import android.service.autofill.FillEventHistory.Event;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -72,6 +77,9 @@ final class AutofillManagerServiceImpl {
    private static final String TAG = "AutofillManagerServiceImpl";
    private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;

    /** Minimum interval to prune abandoned sessions */
    private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;

    static final int MSG_SERVICE_SAVE = 1;

    private final int mUserId;
@@ -104,10 +112,10 @@ final class AutofillManagerServiceImpl {
            mHandlerCallback, true);

    /**
     * Cache of pending {@link Session}s, keyed by {@code activityToken}.
     * Cache of pending {@link Session}s, keyed by sessionId.
     *
     * <p>They're kept until the {@link AutofillService} finished handling a request, an error
     * occurs, or the session times out.
     * occurs, or the session is abandoned.
     */
    @GuardedBy("mLock")
    private final SparseArray<Session> mSessions = new SparseArray<>();
@@ -116,6 +124,9 @@ final class AutofillManagerServiceImpl {
    @GuardedBy("mLock")
    private FillEventHistory mEventHistory;

    /** When was {@link PruneTask} last executed? */
    private long mLastPrune = 0;

    AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
            int userId, AutoFillUI ui, boolean disabled) {
        mContext = context;
@@ -260,6 +271,9 @@ final class AutofillManagerServiceImpl {
            return 0;
        }

        // Occasionally clean up abandoned sessions
        pruneAbandonedSessionsLocked();

        final Session newSession = createSessionByTokenLocked(activityToken, uid, windowToken,
                appCallbackToken, hasCallback, flags, packageName);
        if (newSession == null) {
@@ -277,6 +291,20 @@ final class AutofillManagerServiceImpl {
        return newSession.id;
    }

    /**
     * Remove abandoned sessions if needed.
     */
    private void pruneAbandonedSessionsLocked() {
        long now = System.currentTimeMillis();
        if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) {
            mLastPrune = now;

            if (mSessions.size() > 0) {
                (new PruneTask()).execute();
            }
        }
    }

    void finishSessionLocked(int sessionId, int uid) {
        if (!isEnabled()) {
            return;
@@ -513,6 +541,7 @@ final class AutofillManagerServiceImpl {
        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("Last prune: "); pw.println(mLastPrune);

        final int size = mSessions.size();
        if (size == 0) {
@@ -604,4 +633,62 @@ final class AutofillManagerServiceImpl {
                + ", component=" + (mInfo != null
                ? mInfo.getServiceInfo().getComponentName() : null) + "]";
    }

    /** Task used to prune abandoned session */
    private class PruneTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... ignored) {
            int numSessionsToRemove;
            ArrayMap<IBinder, Integer> sessionsToRemove;

            synchronized (mLock) {
                numSessionsToRemove = mSessions.size();
                sessionsToRemove = new ArrayMap<>(numSessionsToRemove);

                for (int i = 0; i < numSessionsToRemove; i++) {
                    Session session = mSessions.valueAt(i);

                    sessionsToRemove.put(session.getActivityTokenLocked(), session.id);
                }
            }

            IActivityManager am = ActivityManager.getService();

            // Only remove sessions which's activities are not known to the activity manager anymore
            for (int i = 0; i < numSessionsToRemove; i++) {
                try {
                    // The activity manager cannot resolve activities that have been removed
                    if (am.getActivityClassForToken(sessionsToRemove.keyAt(i)) != null) {
                        sessionsToRemove.removeAt(i);
                        i--;
                        numSessionsToRemove--;
                    }
                } catch (RemoteException e) {
                    Slog.w(TAG, "Cannot figure out if activity is finished", e);
                }
            }

            synchronized (mLock) {
                for (int i = 0; i < numSessionsToRemove; i++) {
                    Session sessionToRemove = mSessions.get(sessionsToRemove.valueAt(i));

                    if (sessionToRemove != null) {
                        if (sessionToRemove.isSavingLocked()) {
                            if (sVerbose) {
                                Slog.v(TAG, "Session " + sessionToRemove.id + " is saving");
                            }
                        } else {
                            if (sDebug) {
                                Slog.i(TAG, "Prune session " + sessionToRemove.id + " ("
                                    + sessionToRemove.getActivityTokenLocked() + ")");
                            }
                            sessionToRemove.removeSelfLocked();
                        }
                    }
                }
            }

            return null;
        }
    }
}
+21 −1
Original line number Diff line number Diff line
@@ -177,6 +177,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
    @GuardedBy("mLock")
    private boolean mDestroyed;

    /** Whether the session is currently saving */
    @GuardedBy("mLock")
    private boolean mIsSaving;

    /**
     * Receiver of assist data from the app's {@link Activity}.
     */
@@ -361,7 +365,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
     *
     * @return The activity token
     */
    IBinder getActivityTokenLocked() {
    @NonNull IBinder getActivityTokenLocked() {
        return mActivityToken;
    }

@@ -474,6 +478,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
    @Override
    public void onSaveRequestSuccess(@NonNull String servicePackageName) {
        synchronized (mLock) {
            mIsSaving = false;

            if (mDestroyed) {
                Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: "
                        + id + " destroyed");
@@ -496,6 +502,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
    public void onSaveRequestFailure(@Nullable CharSequence message,
            @NonNull String servicePackageName) {
        synchronized (mLock) {
            mIsSaving = false;

            if (mDestroyed) {
                Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: "
                        + id + " destroyed");
@@ -596,6 +604,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
    @Override
    public void cancelSave() {
        synchronized (mLock) {
            mIsSaving = false;

            if (mDestroyed) {
                Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
                        + id + " destroyed");
@@ -831,6 +841,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            if (atLeastOneChanged) {
                mService.setSaveShown();
                getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName);

                mIsSaving = true;
                return false;
            }
        }
@@ -843,6 +855,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        return true;
    }

    /**
     * Returns whether the session is currently showing the save UI
     */
    boolean isSavingLocked() {
        return mIsSaving;
    }

    /**
     * Calls service when user requested save.
     */
@@ -1349,6 +1368,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId);
        pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());
        pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
        pw.print(prefix); pw.print("mIsSaving: "); pw.println(mIsSaving);
        final String prefix2 = prefix + "  ";
        for (Map.Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
            pw.print(prefix); pw.print("State for id "); pw.println(entry.getKey());
+4 −0
Original line number Diff line number Diff line
@@ -264,6 +264,10 @@ public final class AutoFillUI {
                public void onDestroy() {
                    if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) {
                        log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);

                        if (mCallback != null) {
                            mCallback.cancelSave();
                        }
                    }
                    mMetricsLogger.write(log);
                }