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

Commit a74c1f3c authored by Winson's avatar Winson
Browse files

Move withPackageSettingsSnapshot methods into PackageManagerInternal

For consumers who don't care about blocking package data updates and
only need data at a specific snapshot in time, these methods would
avoid locking PMS during the iteration process.

Bug: 183643808

Test: atest com.android.server.pm.test.verify.domain

Change-Id: Ia951381798d75f77c4ad5fc010a469b69992f074
parent 8d3b1656
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -247,6 +247,48 @@ public class FunctionalUtils {
        }
    }

    /**
     * A {@link Consumer} that allows the caller to specify a custom checked {@link Exception} that
     * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
     * different classes.
     *
     * @param <Input> Method parameter type
     * @param <ExceptionType> Checked exception type
     */
    @FunctionalInterface
    public interface ThrowingCheckedConsumer<Input, ExceptionType extends Exception> {
        void accept(Input input) throws ExceptionType;
    }

    /**
     * A {@link Consumer} that allows the caller to specify 2 different custom checked
     * {@link Exception}s that can be thrown by the implementer. This is usually used when
     * proxying/wrapping calls between different classes.
     *
     * @param <Input> Method parameter type
     * @param <ExceptionOne> First checked exception type
     * @param <ExceptionTwo> Second checked exception type
     */
    @FunctionalInterface
    public interface ThrowingChecked2Consumer<Input, ExceptionOne extends Exception,
            ExceptionTwo extends Exception> {
        void accept(Input input) throws ExceptionOne, ExceptionTwo;
    }

    /**
     * A {@link Function} that allows the caller to specify a custom checked {@link Exception} that
     * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
     * different classes.
     *
     * @param <Input> Method parameter type
     * @param <Output> Method return type
     * @param <ExceptionType> Checked exception type
     */
    @FunctionalInterface
    public interface ThrowingCheckedFunction<Input, Output, ExceptionType extends Exception> {
        Output apply(Input input) throws ExceptionType;
    }

    // TODO: add unit test
    /**
     * Gets a user-friendly name for a lambda function.
+7 −1
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ import java.util.function.Consumer;
 *
 * @hide Only for use within the system server.
 */
public abstract class PackageManagerInternal {
public abstract class PackageManagerInternal implements PackageSettingsSnapshotProvider {
    @IntDef(prefix = "PACKAGE_", value = {
            PACKAGE_SYSTEM,
            PACKAGE_SETUP_WIZARD,
@@ -795,6 +795,9 @@ public abstract class PackageManagerInternal {
     * Perform the given action for each package.
     * Note that packages lock will be held while performing the actions.
     *
     * If the caller does not need all packages, prefer the potentially non-locking
     * {@link #withPackageSettingsSnapshot(Consumer)}.
     *
     * @param actionLocked action to be performed
     */
    public abstract void forEachPackage(Consumer<AndroidPackage> actionLocked);
@@ -803,6 +806,9 @@ public abstract class PackageManagerInternal {
     * Perform the given action for each {@link PackageSetting}.
     * Note that packages lock will be held while performing the actions.
     *
     * If the caller does not need all packages, prefer the potentially non-locking
     * {@link #withPackageSettingsSnapshot(Consumer)}.
     *
     * @param actionLocked action to be performed
     */
    public abstract void forEachPackageSetting(Consumer<PackageSetting> actionLocked);
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.content.pm;

import android.annotation.NonNull;

import com.android.internal.util.FunctionalUtils;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageSetting;

import java.util.function.Consumer;
import java.util.function.Function;

/** @hide */
public interface PackageSettingsSnapshotProvider {

    /**
     * Run a function block that requires access to {@link PackageSetting} data. This will
     * ensure the {@link PackageManagerService} lock is taken before any caller's internal lock
     * to avoid deadlock. Note that this method may or may not lock. If a snapshot is available
     * and valid, it will iterate the snapshot set of data.
     */
    void withPackageSettingsSnapshot(
            @NonNull Consumer<Function<String, PackageSetting>> block);

    /**
     * Variant which returns a value to the caller.
     * @see #withPackageSettingsSnapshot(Consumer)
     */
    <Output> Output withPackageSettingsSnapshotReturning(
            @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
                    block);

    /**
     * Variant which throws.
     * @see #withPackageSettingsSnapshot(Consumer)
     */
    <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
            @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageSetting>,
                    ExceptionType> block) throws ExceptionType;

    /**
     * Variant which throws 2 exceptions.
     * @see #withPackageSettingsSnapshot(Consumer)
     */
    <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
            withPackageSettingsSnapshotThrowing2(
                    @NonNull FunctionalUtils.ThrowingChecked2Consumer<
                            Function<String, PackageSetting>, ExceptionOne, ExceptionTwo> block)
            throws ExceptionOne, ExceptionTwo;

    /**
     * Variant which returns a value to the caller and throws.
     * @see #withPackageSettingsSnapshot(Consumer)
     */
    <Output, ExceptionType extends Exception> Output
            withPackageSettingsSnapshotReturningThrowing(
                    @NonNull FunctionalUtils.ThrowingCheckedFunction<
                            Function<String, PackageSetting>, Output, ExceptionType> block)
            throws ExceptionType;
}
+118 −77
Original line number Diff line number Diff line
@@ -1731,104 +1731,57 @@ public class PackageManagerService extends IPackageManager.Stub
            return PackageManagerService.this.getPackage(packageName);
        }
        @NonNull
        @Override
        public void withPackageSettings(@NonNull Consumer<Function<String, PackageSetting>> block) {
            final Computer snapshot = snapshotComputer();
            // This method needs to either lock or not lock consistently throughout the method,
            // so if the live computer is returned, force a wrapping sync block.
            if (snapshot == mLiveComputer) {
                synchronized (mLock) {
                    block.accept(snapshot::getPackageSetting);
                }
            } else {
                block.accept(snapshot::getPackageSetting);
            }
        public boolean filterAppAccess(String packageName, int callingUid, int userId) {
            return mPmInternal.filterAppAccess(packageName, callingUid, userId);
        }
        @Override
        public <Output> Output withPackageSettingsReturning(
                @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
                        block) {
            final Computer snapshot = snapshotComputer();
            // This method needs to either lock or not lock consistently throughout the method,
            // so if the live computer is returned, force a wrapping sync block.
            if (snapshot == mLiveComputer) {
                synchronized (mLock) {
                    return block.apply(snapshot::getPackageSetting);
                }
            } else {
                return block.apply(snapshot::getPackageSetting);
            }
        public int[] getAllUserIds() {
            return mUserManager.getUserIds();
        }
        @Override
        public <ExceptionType extends Exception> void withPackageSettingsThrowing(
                @NonNull ThrowingConsumer<Function<String, PackageSetting>, ExceptionType> block)
                throws ExceptionType {
            final Computer snapshot = snapshotComputer();
            // This method needs to either lock or not lock consistently throughout the method,
            // so if the live computer is returned, force a wrapping sync block.
            if (snapshot == mLiveComputer) {
                synchronized (mLock) {
                    block.accept(snapshot::getPackageSetting);
                }
            } else {
                block.accept(snapshot::getPackageSetting);
            }
        public boolean doesUserExist(@UserIdInt int userId) {
            return mUserManager.exists(userId);
        }
        @Override
        public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
                withPackageSettingsThrowing2(
                        @NonNull Throwing2Consumer<Function<String, PackageSetting>, ExceptionOne,
                                ExceptionTwo> block) throws ExceptionOne, ExceptionTwo {
            final Computer snapshot = snapshotComputer();
            // This method needs to either lock or not lock consistently throughout the method,
            // so if the live computer is returned, force a wrapping sync block.
            if (snapshot == mLiveComputer) {
                synchronized (mLock) {
                    block.accept(snapshot::getPackageSetting);
                }
            } else {
                block.accept(snapshot::getPackageSetting);
            }
        public void withPackageSettingsSnapshot(
                @NonNull Consumer<Function<String, PackageSetting>> block) {
            mPmInternal.withPackageSettingsSnapshot(block);
        }
        @Override
        public <Output, ExceptionType extends Exception> Output
                withPackageSettingsReturningThrowing(@NonNull ThrowingFunction<Function<String,
                PackageSetting>, Output, ExceptionType> block) throws ExceptionType {
            final Computer snapshot = snapshotComputer();
            // This method needs to either lock or not lock consistently throughout the method,
            // so if the live computer is returned, force a wrapping sync block.
            if (snapshot == mLiveComputer) {
                synchronized (mLock) {
                    return block.apply(snapshot::getPackageSetting);
                }
            } else {
                return block.apply(snapshot::getPackageSetting);
            }
        public <Output> Output withPackageSettingsSnapshotReturning(
                @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
                        block) {
            return mPmInternal.withPackageSettingsSnapshotReturning(block);
        }
        @Override
        public boolean filterAppAccess(String packageName, int callingUid, int userId) {
            return mPmInternal.filterAppAccess(packageName, callingUid, userId);
        public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
                @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageSetting>,
                        ExceptionType> block) throws ExceptionType {
            mPmInternal.withPackageSettingsSnapshotThrowing(block);
        }
        @Override
        public int[] getAllUserIds() {
            return mUserManager.getUserIds();
        public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
                withPackageSettingsSnapshotThrowing2(
                        @NonNull FunctionalUtils.ThrowingChecked2Consumer<
                                Function<String, PackageSetting>, ExceptionOne, ExceptionTwo> block)
                throws ExceptionOne, ExceptionTwo {
            mPmInternal.withPackageSettingsSnapshotThrowing2(block);
        }
        @Override
        public boolean doesUserExist(@UserIdInt int userId) {
            return mUserManager.exists(userId);
        public <Output, ExceptionType extends Exception> Output
                withPackageSettingsSnapshotReturningThrowing(
                        @NonNull FunctionalUtils.ThrowingCheckedFunction<
                                Function<String, PackageSetting>, Output, ExceptionType> block)
                throws ExceptionType {
            return mPmInternal.withPackageSettingsSnapshotReturningThrowing(block);
        }
    }
@@ -27290,6 +27243,94 @@ public class PackageManagerService extends IPackageManager.Stub
        public void deleteOatArtifactsOfPackage(String packageName) {
            PackageManagerService.this.deleteOatArtifactsOfPackage(packageName);
        }
        @Override
        public void withPackageSettingsSnapshot(
                @NonNull Consumer<Function<String, PackageSetting>> block) {
            final Computer snapshot = snapshotComputer();
            // This method needs to either lock or not lock consistently throughout the method,
            // so if the live computer is returned, force a wrapping sync block.
            if (snapshot == mLiveComputer) {
                synchronized (mLock) {
                    block.accept(snapshot::getPackageSetting);
                }
            } else {
                block.accept(snapshot::getPackageSetting);
            }
        }
        @Override
        public <Output> Output withPackageSettingsSnapshotReturning(
                @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
                        block) {
            final Computer snapshot = snapshotComputer();
            // This method needs to either lock or not lock consistently throughout the method,
            // so if the live computer is returned, force a wrapping sync block.
            if (snapshot == mLiveComputer) {
                synchronized (mLock) {
                    return block.apply(snapshot::getPackageSetting);
                }
            } else {
                return block.apply(snapshot::getPackageSetting);
            }
        }
        @Override
        public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
                @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageSetting>,
                        ExceptionType> block) throws ExceptionType {
            final Computer snapshot = snapshotComputer();
            // This method needs to either lock or not lock consistently throughout the method,
            // so if the live computer is returned, force a wrapping sync block.
            if (snapshot == mLiveComputer) {
                synchronized (mLock) {
                    block.accept(snapshot::getPackageSetting);
                }
            } else {
                block.accept(snapshot::getPackageSetting);
            }
        }
        @Override
        public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
                withPackageSettingsSnapshotThrowing2(
                        @NonNull FunctionalUtils.ThrowingChecked2Consumer<
                                Function<String, PackageSetting>, ExceptionOne, ExceptionTwo> block)
                throws ExceptionOne, ExceptionTwo {
            final Computer snapshot = snapshotComputer();
            // This method needs to either lock or not lock consistently throughout the method,
            // so if the live computer is returned, force a wrapping sync block.
            if (snapshot == mLiveComputer) {
                synchronized (mLock) {
                    block.accept(snapshot::getPackageSetting);
                }
            } else {
                block.accept(snapshot::getPackageSetting);
            }
        }
        @Override
        public <Output, ExceptionType extends Exception> Output
                withPackageSettingsSnapshotReturningThrowing(
                        @NonNull FunctionalUtils.ThrowingCheckedFunction<
                                Function<String, PackageSetting>, Output, ExceptionType> block)
                throws ExceptionType {
            final Computer snapshot = snapshotComputer();
            // This method needs to either lock or not lock consistently throughout the method,
            // so if the live computer is returned, force a wrapping sync block.
            if (snapshot == mLiveComputer) {
                synchronized (mLock) {
                    return block.apply(snapshot::getPackageSetting);
                }
            } else {
                return block.apply(snapshot::getPackageSetting);
            }
        }
    }
    @GuardedBy("mLock")
+3 −56
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.content.Intent;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageSettingsSnapshotProvider;
import android.content.pm.ResolveInfo;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
@@ -36,7 +37,6 @@ import android.util.Pair;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;

import com.android.internal.util.FunctionalUtils;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.Settings;
@@ -49,7 +49,6 @@ import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;

public interface DomainVerificationManagerInternal {
@@ -406,7 +405,8 @@ public interface DomainVerificationManagerInternal {
            @NonNull Set<String> domains, int state) throws NameNotFoundException;


    interface Connection extends DomainVerificationEnforcer.Callback {
    interface Connection extends DomainVerificationEnforcer.Callback,
            PackageSettingsSnapshotProvider {

        /**
         * Notify that a settings change has been made and that eventually
@@ -430,60 +430,7 @@ public interface DomainVerificationManagerInternal {
         */
        void schedule(int code, @Nullable Object object);

        /**
         * Run a function block that requires access to {@link PackageSetting} data. This will
         * ensure the {@link PackageManagerService} is taken before
         * {@link DomainVerificationManagerInternal}'s lock is taken to avoid deadlock.
         */
        void withPackageSettings(@NonNull Consumer<Function<String, PackageSetting>> block);

        /**
         * Variant which returns a value to the caller.
         * @see #withPackageSettings(Consumer)
         */
        <Output> Output withPackageSettingsReturning(
                @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
                        block);

        /**
         * Variant which throws.
         * @see #withPackageSettings(Consumer)
         */
        <ExceptionType extends Exception> void withPackageSettingsThrowing(
                @NonNull ThrowingConsumer<Function<String, PackageSetting>, ExceptionType> block)
                throws ExceptionType;

        /**
         * Variant which throws 2 exceptions.
         * @see #withPackageSettings(Consumer)
         */
        <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
                withPackageSettingsThrowing2(
                        @NonNull Throwing2Consumer<Function<String, PackageSetting>, ExceptionOne,
                                ExceptionTwo> block) throws ExceptionOne, ExceptionTwo;

        /**
         * Variant which returns a value to the caller and throws.
         * @see #withPackageSettings(Consumer)
         */
        <Output, ExceptionType extends Exception> Output withPackageSettingsReturningThrowing(
                @NonNull ThrowingFunction<Function<String, PackageSetting>, Output, ExceptionType>
                        block) throws ExceptionType;

        @UserIdInt
        int[] getAllUserIds();

        interface ThrowingConsumer<Input, ExceptionType extends Exception> {
            void accept(Input input) throws ExceptionType;
        }

        interface Throwing2Consumer<Input, ExceptionOne extends Exception,
                ExceptionTwo extends Exception> {
            void accept(Input input) throws ExceptionOne, ExceptionTwo;
        }

        interface ThrowingFunction<Input, Output, ExceptionType extends Exception> {
            Output apply(Input input) throws ExceptionType;
        }
    }
}
Loading