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

Commit d19b17a8 authored by Automerger Merge Worker's avatar Automerger Merge Worker Committed by Android (Google) Code Review
Browse files

Merge "Merge "Fix potential deadlock in getAutofillOptions()." into rvc-dev...

Merge "Merge "Fix potential deadlock in getAutofillOptions()." into rvc-dev am: 291a32c9 am: db72226d" into rvc-d1-dev-plus-aosp
parents 5a7f5486 4cda575d
Loading
Loading
Loading
Loading
+238 −11
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -63,6 +64,7 @@ import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -151,6 +153,7 @@ public final class AutofillManagerService
    private final LocalLog mWtfHistory = new LocalLog(50);

    private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
    private final DisabledInfoCache mDisabledInfoCache = new DisabledInfoCache();

    private final LocalService mLocalService = new LocalService();
    private final ActivityManagerInternal mAm;
@@ -302,14 +305,15 @@ public final class AutofillManagerService
    @Override // from AbstractMasterSystemService
    protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
            boolean disabled) {
        return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory,
                mWtfHistory, resolvedUserId, mUi, mAutofillCompatState, disabled);
        return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory, mWtfHistory,
                resolvedUserId, mUi, mAutofillCompatState, disabled, mDisabledInfoCache);
    }

    @Override // AbstractMasterSystemService
    protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service,
            @UserIdInt int userId) {
        service.destroyLocked();
        mDisabledInfoCache.remove(userId);
        mAutofillCompatState.removeCompatibilityModeRequests(userId);
    }

@@ -835,15 +839,10 @@ public final class AutofillManagerService

        private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId,
                String packageName) {
            synchronized (mLock) {
                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                if (service != null) {
                    options.appDisabledExpiration = service.getAppDisabledExpirationLocked(
                            packageName);
                    options.disabledActivities = service.getAppDisabledActivitiesLocked(
                            packageName);
                }
            }
            options.appDisabledExpiration =
                    mDisabledInfoCache.getAppDisabledExpiration(userId, packageName);
            options.disabledActivities =
                    mDisabledInfoCache.getAppDisabledActivities(userId, packageName);
        }
    }

@@ -866,6 +865,234 @@ public final class AutofillManagerService
        }
    }

    /**
     * Stores autofill disable information, i.e. {@link AutofillDisabledInfo},  keyed by user id.
     * The information is cleaned up when the service is removed.
     */
    static final class DisabledInfoCache {

        private final Object mLock = new Object();

        @GuardedBy("mLock")
        private final SparseArray<AutofillDisabledInfo> mCache = new SparseArray<>();

        void remove(@UserIdInt int userId) {
            synchronized (mLock) {
                mCache.remove(userId);
            }
        }

        void addDisabledAppLocked(@UserIdInt int userId, @NonNull String packageName,
                long expiration) {
            Preconditions.checkNotNull(packageName);
            synchronized (mLock) {
                AutofillDisabledInfo info =
                        getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
                info.putDisableAppsLocked(packageName, expiration);
            }
        }

        void addDisabledActivityLocked(@UserIdInt int userId, @NonNull ComponentName componentName,
                long expiration) {
            Preconditions.checkNotNull(componentName);
            synchronized (mLock) {
                AutofillDisabledInfo info =
                        getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
                info.putDisableActivityLocked(componentName, expiration);
            }
        }

        boolean isAutofillDisabledLocked(@UserIdInt int userId,
                @NonNull ComponentName componentName) {
            Preconditions.checkNotNull(componentName);
            final boolean disabled;
            synchronized (mLock) {
                final AutofillDisabledInfo info = mCache.get(userId);
                disabled = info != null ? info.isAutofillDisabledLocked(componentName) : false;
            }
            return disabled;
        }

        long getAppDisabledExpiration(@UserIdInt int userId, @NonNull String packageName) {
            Preconditions.checkNotNull(packageName);
            final Long expiration;
            synchronized (mLock) {
                final AutofillDisabledInfo info = mCache.get(userId);
                expiration = info != null ? info.getAppDisabledExpirationLocked(packageName) : 0;
            }
            return expiration;
        }

        @Nullable
        ArrayMap<String, Long> getAppDisabledActivities(@UserIdInt int userId,
                @NonNull String packageName) {
            Preconditions.checkNotNull(packageName);
            final ArrayMap<String, Long> disabledList;
            synchronized (mLock) {
                final AutofillDisabledInfo info = mCache.get(userId);
                disabledList =
                        info != null ? info.getAppDisabledActivitiesLocked(packageName) : null;
            }
            return disabledList;
        }

        void dump(@UserIdInt int userId, String prefix, PrintWriter pw) {
            synchronized (mLock) {
                final AutofillDisabledInfo info = mCache.get(userId);
                if (info != null) {
                    info.dumpLocked(prefix, pw);
                }
            }
        }

        @NonNull
        private AutofillDisabledInfo getOrCreateAutofillDisabledInfoByUserIdLocked(
                @UserIdInt int userId) {
            AutofillDisabledInfo info = mCache.get(userId);
            if (info == null) {
                info = new AutofillDisabledInfo();
                mCache.put(userId, info);
            }
            return info;
        }
    }

    /**
     * The autofill disable information.
     * <p>
     * This contains disable information set by the AutofillService, e.g. disabled application
     * expiration, disable activity expiration.
     */
    private static final class AutofillDisabledInfo {
        /**
         * Apps disabled by the service; key is package name, value is when they will be enabled
         * again.
         */
        private ArrayMap<String, Long> mDisabledApps;
        /**
         * Activities disabled by the service; key is component name, value is when they will be
         * enabled again.
         */
        private ArrayMap<ComponentName, Long> mDisabledActivities;

        void putDisableAppsLocked(@NonNull String packageName, long expiration) {
            if (mDisabledApps == null) {
                mDisabledApps = new ArrayMap<>(1);
            }
            mDisabledApps.put(packageName, expiration);
        }

        void putDisableActivityLocked(@NonNull ComponentName componentName, long expiration) {
            if (mDisabledActivities == null) {
                mDisabledActivities = new ArrayMap<>(1);
            }
            mDisabledActivities.put(componentName, expiration);
        }

        long getAppDisabledExpirationLocked(@NonNull String packageName) {
            if (mDisabledApps == null) {
                return 0;
            }
            final Long expiration = mDisabledApps.get(packageName);
            return expiration != null ? expiration : 0;
        }

        ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
            if (mDisabledActivities != null) {
                final int size = mDisabledActivities.size();
                ArrayMap<String, Long> disabledList = null;
                for (int i = 0; i < size; i++) {
                    final ComponentName component = mDisabledActivities.keyAt(i);
                    if (packageName.equals(component.getPackageName())) {
                        if (disabledList == null) {
                            disabledList = new ArrayMap<>();
                        }
                        final long expiration = mDisabledActivities.valueAt(i);
                        disabledList.put(component.flattenToShortString(), expiration);
                    }
                }
                return disabledList;
            }
            return null;
        }

        boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
            // Check activities first.
            long elapsedTime = 0;
            if (mDisabledActivities != null) {
                elapsedTime = SystemClock.elapsedRealtime();
                final Long expiration = mDisabledActivities.get(componentName);
                if (expiration != null) {
                    if (expiration >= elapsedTime) return true;
                    // Restriction expired - clean it up.
                    if (sVerbose) {
                        Slog.v(TAG, "Removing " + componentName.toShortString()
                                + " from disabled list");
                    }
                    mDisabledActivities.remove(componentName);
                }
            }

            // Then check apps.
            final String packageName = componentName.getPackageName();
            if (mDisabledApps == null) return false;

            final Long expiration = mDisabledApps.get(packageName);
            if (expiration == null) return false;

            if (elapsedTime == 0) {
                elapsedTime = SystemClock.elapsedRealtime();
            }

            if (expiration >= elapsedTime) return true;

            // Restriction expired - clean it up.
            if (sVerbose)  Slog.v(TAG, "Removing " + packageName + " from disabled list");
            mDisabledApps.remove(packageName);
            return false;
        }

        void dumpLocked(String prefix, PrintWriter pw) {
            pw.print(prefix); pw.print("Disabled apps: ");
            if (mDisabledApps == null) {
                pw.println("N/A");
            } else {
                final int size = mDisabledApps.size();
                pw.println(size);
                final StringBuilder builder = new StringBuilder();
                final long now = SystemClock.elapsedRealtime();
                for (int i = 0; i < size; i++) {
                    final String packageName = mDisabledApps.keyAt(i);
                    final long expiration = mDisabledApps.valueAt(i);
                    builder.append(prefix).append(prefix)
                            .append(i).append(". ").append(packageName).append(": ");
                    TimeUtils.formatDuration((expiration - now), builder);
                    builder.append('\n');
                }
                pw.println(builder);
            }

            pw.print(prefix); pw.print("Disabled activities: ");
            if (mDisabledActivities == null) {
                pw.println("N/A");
            } else {
                final int size = mDisabledActivities.size();
                pw.println(size);
                final StringBuilder builder = new StringBuilder();
                final long now = SystemClock.elapsedRealtime();
                for (int i = 0; i < size; i++) {
                    final ComponentName component = mDisabledActivities.keyAt(i);
                    final long expiration = mDisabledActivities.valueAt(i);
                    builder.append(prefix).append(prefix)
                            .append(i).append(". ").append(component).append(": ");
                    TimeUtils.formatDuration((expiration - now), builder);
                    builder.append('\n');
                }
                pw.println(builder);
            }
        }
    }

    /**
     * Compatibility mode metadata associated with all services.
     *
+10 −127
Original line number Diff line number Diff line
@@ -67,7 +67,6 @@ import android.util.LocalLog;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -80,6 +79,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.AutofillManagerService.DisabledInfoCache;
import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
@@ -90,7 +90,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
 * app's {@link IAutoFillService} implementation.
@@ -124,19 +123,6 @@ final class AutofillManagerServiceImpl
    @Nullable
    private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService;

    /**
     * Apps disabled by the service; key is package name, value is when they will be enabled again.
     */
    @GuardedBy("mLock")
    private ArrayMap<String, Long> mDisabledApps;

    /**
     * Activities disabled by the service; key is component name, value is when they will be enabled
     * again.
     */
    @GuardedBy("mLock")
    private ArrayMap<ComponentName, Long> mDisabledActivities;

    /**
     * Data used for field classification.
     */
@@ -186,10 +172,12 @@ final class AutofillManagerServiceImpl

    private final ContentCaptureManagerInternal mContentCaptureManagerInternal;

    private final DisabledInfoCache mDisabledInfoCache;

    AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
            LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
            AutofillCompatState autofillCompatState,
            boolean disabled) {
            boolean disabled, DisabledInfoCache disableCache) {
        super(master, lock, userId);

        mUiLatencyHistory = uiLatencyHistory;
@@ -200,7 +188,7 @@ final class AutofillManagerServiceImpl
        mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
        mContentCaptureManagerInternal = LocalServices.getService(
                ContentCaptureManagerInternal.class);

        mDisabledInfoCache = disableCache;
        updateLocked(disabled);
    }

@@ -1045,45 +1033,7 @@ final class AutofillManagerServiceImpl
        pw.println(isInlineSuggestionsEnabled());
        pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);

        pw.print(prefix); pw.print("Disabled apps: ");

        if (mDisabledApps == null) {
            pw.println("N/A");
        } else {
            final int size = mDisabledApps.size();
            pw.println(size);
            final StringBuilder builder = new StringBuilder();
            final long now = SystemClock.elapsedRealtime();
            for (int i = 0; i < size; i++) {
                final String packageName = mDisabledApps.keyAt(i);
                final long expiration = mDisabledApps.valueAt(i);
                builder.append(prefix).append(prefix)
                        .append(i).append(". ").append(packageName).append(": ");
                TimeUtils.formatDuration((expiration - now), builder);
                builder.append('\n');
            }
            pw.println(builder);
        }

        pw.print(prefix); pw.print("Disabled activities: ");

        if (mDisabledActivities == null) {
            pw.println("N/A");
        } else {
            final int size = mDisabledActivities.size();
            pw.println(size);
            final StringBuilder builder = new StringBuilder();
            final long now = SystemClock.elapsedRealtime();
            for (int i = 0; i < size; i++) {
                final ComponentName component = mDisabledActivities.keyAt(i);
                final long expiration = mDisabledActivities.valueAt(i);
                builder.append(prefix).append(prefix)
                        .append(i).append(". ").append(component).append(": ");
                TimeUtils.formatDuration((expiration - now), builder);
                builder.append('\n');
            }
            pw.println(builder);
        }
        mDisabledInfoCache.dump(mUserId, prefix, pw);

        final int size = mSessions.size();
        if (size == 0) {
@@ -1480,15 +1430,13 @@ final class AutofillManagerServiceImpl
    void disableAutofillForApp(@NonNull String packageName, long duration, int sessionId,
            boolean compatMode) {
        synchronized (mLock) {
            if (mDisabledApps == null) {
                mDisabledApps = new ArrayMap<>(1);
            }
            long expiration = SystemClock.elapsedRealtime() + duration;
            // Protect it against overflow
            if (expiration < 0) {
                expiration = Long.MAX_VALUE;
            }
            mDisabledApps.put(packageName, expiration);
            mDisabledInfoCache.addDisabledAppLocked(mUserId, packageName, expiration);

            int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
            mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP,
                    packageName, getServicePackageName(), sessionId, compatMode)
@@ -1502,15 +1450,12 @@ final class AutofillManagerServiceImpl
    void disableAutofillForActivity(@NonNull ComponentName componentName, long duration,
            int sessionId, boolean compatMode) {
        synchronized (mLock) {
            if (mDisabledActivities == null) {
                mDisabledActivities = new ArrayMap<>(1);
            }
            long expiration = SystemClock.elapsedRealtime() + duration;
            // Protect it against overflow
            if (expiration < 0) {
                expiration = Long.MAX_VALUE;
            }
            mDisabledActivities.put(componentName, expiration);
            mDisabledInfoCache.addDisabledActivityLocked(mUserId, componentName, expiration);
            final int intDuration = duration > Integer.MAX_VALUE
                    ? Integer.MAX_VALUE
                    : (int) duration;
@@ -1528,74 +1473,12 @@ final class AutofillManagerServiceImpl
        }
    }

    // Called by AutofillManagerService
    long getAppDisabledExpirationLocked(@NonNull String packageName) {
        if (mDisabledApps == null) {
            return 0;
        }
        final Long expiration = mDisabledApps.get(packageName);
        return expiration != null ? expiration : 0;
    }

    // Called by AutofillManagerService
    @Nullable
    ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
        if (mDisabledActivities != null) {
            final int size = mDisabledActivities.size();
            ArrayMap<String, Long> disabledList = null;
            for (int i = 0; i < size; i++) {
                final ComponentName component = mDisabledActivities.keyAt(i);
                if (packageName.equals(component.getPackageName())) {
                    if (disabledList == null) {
                        disabledList = new ArrayMap<>();
                    }
                    final long expiration = mDisabledActivities.valueAt(i);
                    disabledList.put(component.flattenToShortString(), expiration);
                }
            }
            return disabledList;
        }
        return null;
    }

    /**
     * Checks if autofill is disabled by service to the given activity.
     */
    @GuardedBy("mLock")
    private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
        // Check activities first.
        long elapsedTime = 0;
        if (mDisabledActivities != null) {
            elapsedTime = SystemClock.elapsedRealtime();
            final Long expiration = mDisabledActivities.get(componentName);
            if (expiration != null) {
                if (expiration >= elapsedTime) return true;
                // Restriction expired - clean it up.
                if (sVerbose) {
                    Slog.v(TAG, "Removing " + componentName.toShortString()
                        + " from disabled list");
                }
                mDisabledActivities.remove(componentName);
            }
        }

        // Then check apps.
        final String packageName = componentName.getPackageName();
        if (mDisabledApps == null) return false;

        final Long expiration = mDisabledApps.get(packageName);
        if (expiration == null) return false;

        if (elapsedTime == 0) {
            elapsedTime = SystemClock.elapsedRealtime();
        }

        if (expiration >= elapsedTime) return true;

        // Restriction expired - clean it up.
        if (sVerbose)  Slog.v(TAG, "Removing " + packageName + " from disabled list");
        mDisabledApps.remove(packageName);
        return false;
        return mDisabledInfoCache.isAutofillDisabledLocked(mUserId, componentName);
    }

    // Called by AutofillManager, checks UID.