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

Commit db72226d authored by Joanne Chung's avatar Joanne Chung Committed by Automerger Merge Worker
Browse files

Merge "Fix potential deadlock in getAutofillOptions()." into rvc-dev am: 291a32c9

Change-Id: Ie845804f2e51a16aa30bb5ef88d1d795d9092700
parents 1b734738 291a32c9
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.