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

Commit 7f401816 authored by Tom Natan's avatar Tom Natan Committed by Gerrit Code Review
Browse files

Merge "Fix deadlock on CompatConfig.mChanges"

parents 6933315f 3a6b89c8
Loading
Loading
Loading
Loading
+13 −19
Original line number Diff line number Diff line
@@ -26,9 +26,7 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;

import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeInfo;
@@ -161,15 +159,17 @@ public final class CompatChange extends CompatibilityChangeInfo {
     *
     * @param packageName Package name to tentatively enable the change for.
     * @param override The package override to be set
     * @param allowedState Whether the override is allowed.
     * @param versionCode The version code of the package.
     */
    void addPackageOverride(String packageName, PackageOverride override,
            OverrideAllowedState allowedState, Context context) {
            OverrideAllowedState allowedState, @Nullable Long versionCode) {
        if (getLoggingOnly()) {
            throw new IllegalArgumentException(
                    "Can't add overrides for a logging only change " + toString());
        }
        mRawOverrides.put(packageName, override);
        recheckOverride(packageName, allowedState, context);
        recheckOverride(packageName, allowedState, versionCode);
    }

    /**
@@ -179,32 +179,24 @@ public final class CompatChange extends CompatibilityChangeInfo {
     * overrides, check if they need to be demoted to deferred.</p>
     *
     * @param packageName Package name to apply deferred overrides for.
     * @param allowed Whether the override is allowed.
     * @param allowedState Whether the override is allowed.
     * @param versionCode The version code of the package.
     *
     * @return {@code true} if the recheck yielded a result that requires invalidating caches
     *         (a deferred override was consolidated or a regular override was removed).
     */
    boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
            Context context) {
            @Nullable Long versionCode) {
        boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED);

        Long version = null;
        try {
            ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(
                    packageName, 0);
            version = applicationInfo.longVersionCode;
        } catch (PackageManager.NameNotFoundException e) {
            // Do nothing
        }

        // If the app is not installed or no longer has raw overrides, evaluate to false
        if (version == null || !hasRawOverride(packageName) || !allowed) {
        if (versionCode == null || !hasRawOverride(packageName) || !allowed) {
            removePackageOverrideInternal(packageName);
            return false;
        }

        // Evaluate the override based on its version
        int overrideValue = mRawOverrides.get(packageName).evaluate(version);
        int overrideValue = mRawOverrides.get(packageName).evaluate(versionCode);
        switch (overrideValue) {
            case VALUE_UNDEFINED:
                removePackageOverrideInternal(packageName);
@@ -229,11 +221,13 @@ public final class CompatChange extends CompatibilityChangeInfo {
     * <p>Note, this method is not thread safe so callers must ensure thread safety.
     *
     * @param pname Package name to reset to defaults for.
     * @param allowedState Whether the override is allowed.
     * @param versionCode The version code of the package.
     */
    boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
            Context context) {
            @Nullable Long versionCode) {
        if (mRawOverrides.remove(pname) != null) {
            recheckOverride(pname, allowedState, context);
            recheckOverride(pname, allowedState, versionCode);
            return true;
        }
        return false;
+28 −18
Original line number Diff line number Diff line
@@ -16,11 +16,13 @@

package com.android.server.compat;

import android.annotation.Nullable;
import android.app.compat.ChangeIdStateCache;
import android.app.compat.PackageOverride;
import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.text.TextUtils;
import android.util.LongArray;
@@ -51,7 +53,6 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -249,6 +250,7 @@ final class CompatConfig {
        OverrideAllowedState allowedState =
                mOverrideValidator.getOverrideAllowedState(changeId, packageName);
        allowedState.enforce(changeId, packageName);
        Long versionCode = getVersionCodeOrNull(packageName);
        synchronized (mChanges) {
            CompatChange c = mChanges.get(changeId);
            if (c == null) {
@@ -256,7 +258,7 @@ final class CompatConfig {
                c = new CompatChange(changeId);
                addChange(c);
            }
            c.addPackageOverride(packageName, overrides, allowedState, mContext);
            c.addPackageOverride(packageName, overrides, allowedState, versionCode);
            invalidateCache();
        }
        return alreadyKnown;
@@ -336,6 +338,7 @@ final class CompatConfig {
     * It does not invalidate the cache nor save the overrides.
     */
    private boolean removeOverrideUnsafe(long changeId, String packageName) {
        Long versionCode = getVersionCodeOrNull(packageName);
        synchronized (mChanges) {
            CompatChange c = mChanges.get(changeId);
            if (c != null) {
@@ -343,7 +346,7 @@ final class CompatConfig {
                        mOverrideValidator.getOverrideAllowedState(changeId, packageName);
                if (c.hasPackageOverride(packageName)) {
                    allowedState.enforce(changeId, packageName);
                    c.removePackageOverride(packageName, allowedState, mContext);
                    c.removePackageOverride(packageName, allowedState, versionCode);
                    invalidateCache();
                    return true;
                }
@@ -653,28 +656,35 @@ final class CompatConfig {
     * Rechecks all the existing overrides for a package.
     */
    void recheckOverrides(String packageName) {
        // Local cache of compat changes. Holding a lock on mChanges for the whole duration of the
        // method will cause a deadlock.
        List<CompatChange> changes;
        Long versionCode = getVersionCodeOrNull(packageName);
        synchronized (mChanges) {
            changes = new ArrayList<>(mChanges.size());
            for (int idx = 0; idx < mChanges.size(); ++idx) {
                changes.add(mChanges.valueAt(idx));
            }
        }
            boolean shouldInvalidateCache = false;
        for (CompatChange c: changes) {
            for (int idx = 0; idx < mChanges.size(); ++idx) {
                CompatChange c = mChanges.valueAt(idx);
                if (!c.hasPackageOverride(packageName)) {
                    continue;
                }
                OverrideAllowedState allowedState =
                    mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(), packageName);
            shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext);
                        mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(),
                                packageName);
                shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, versionCode);
            }
            if (shouldInvalidateCache) {
                invalidateCache();
            }
        }
    }

    @Nullable
    private Long getVersionCodeOrNull(String packageName) {
        try {
            ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(
                    packageName, 0);
            return applicationInfo.longVersionCode;
        } catch (PackageManager.NameNotFoundException e) {
            return null;
        }
    }

    void registerContentObserver() {
        mOverrideValidator.registerContentObserver();