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

Commit e41e4c98 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add PackageManagerLocal snapshot APIs"

parents a37cf954 c25df89c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -187,6 +187,8 @@ public interface Computer extends PackageDataSnapshot {

    PackageStateInternal getPackageStateInternal(String packageName);
    PackageStateInternal getPackageStateInternal(String packageName, int callingUid);
    PackageStateInternal getPackageStateFiltered(@NonNull String packageName, int callingUid,
            @UserIdInt int userId);
    ParceledListSlice<PackageInfo> getInstalledPackages(long flags, int userId);
    ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
            int sourceUserId, int targetUserId);
+13 −0
Original line number Diff line number Diff line
@@ -1816,6 +1816,19 @@ public class ComputerEngine implements Computer {
        return mSettings.getPackage(packageName);
    }

    @Override
    public PackageStateInternal getPackageStateFiltered(@NonNull String packageName,
            int callingUid, @UserIdInt int userId) {
        packageName = resolveInternalPackageNameInternalLocked(
                packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
        var packageState = mSettings.getPackage(packageName);
        if (shouldFilterApplication(packageState, callingUid, userId)) {
            return null;
        } else {
            return packageState;
        }
    }

    public final ParceledListSlice<PackageInfo> getInstalledPackages(long flags, int userId) {
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
+104 −2
Original line number Diff line number Diff line
@@ -20,11 +20,17 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Binder;
import android.os.UserHandle;

import com.android.server.pm.pkg.PackageState;

import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * In-process API for server side PackageManager related infrastructure.
@@ -57,10 +63,10 @@ public interface PackageManagerLocal {
            FLAG_STORAGE_CE,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface StorageFlags {}
    @interface StorageFlags {}

    /**
     * Reconcile sdk data sub-directories for the given {@code packagName}.
     * Reconcile sdk data sub-directories for the given {@code packageName}.
     *
     * Sub directories are created if they do not exist already. If there is an existing per-
     * sdk directory that is missing from {@code subDirNames}, then it is removed.
@@ -80,4 +86,100 @@ public interface PackageManagerLocal {
    void reconcileSdkData(@Nullable String volumeUuid, @NonNull String packageName,
            @NonNull List<String> subDirNames, int userId, int appId, int previousAppId,
            @NonNull String seInfo, @StorageFlags int flags) throws IOException;

    /**
     * Provides a snapshot scoped class to access snapshot-aware APIs. Should be short-term use and
     * closed as soon as possible.
     * <p/>
     * All reachable types in the snapshot are read-only.
     * <p/>
     * The snapshot assumes the caller is acting on behalf of the system and will not filter any
     * results.
     *
     * @hide
     */
    @NonNull
    UnfilteredSnapshot withUnfilteredSnapshot();

    /**
     * {@link #withFilteredSnapshot(int, UserHandle)} that infers the UID and user from the
     * caller through {@link Binder#getCallingUid()} and {@link Binder#getCallingUserHandle()}.
     *
     * @see #withFilteredSnapshot(int, UserHandle)
     * @hide
     */
    @NonNull
    FilteredSnapshot withFilteredSnapshot();

    /**
     * Provides a snapshot scoped class to access snapshot-aware APIs. Should be short-term use and
     * closed as soon as possible.
     * <p/>
     * All reachable types in the snapshot are read-only.
     *
     * @param callingUid The caller UID to filter results based on. This includes package visibility
     *                   and permissions, including cross-user enforcement.
     * @param user       The user to query as, should usually be the user that the caller was
     *                   invoked from.
     * @hide
     */
    @SuppressWarnings("UserHandleName") // Ignore naming convention, not invoking action as user
    @NonNull
    FilteredSnapshot withFilteredSnapshot(int callingUid, @NonNull UserHandle user);

    /**
     * @hide
     */
    interface UnfilteredSnapshot extends AutoCloseable {

        /**
         * Allows re-use of this snapshot, but in a filtered context. This allows a caller to invoke
         * itself as multiple other actual callers without having to re-take a snapshot.
         * <p/>
         * Note that closing the parent snapshot closes any filtered children generated from it.
         *
         * @return An isolated instance of {@link FilteredSnapshot} which can be closed without
         * affecting this parent snapshot or any sibling snapshots.
         */
        @SuppressWarnings("UserHandleName") // Ignore naming convention, not invoking action as user
        @NonNull
        FilteredSnapshot filtered(int callingUid, @NonNull UserHandle user);

        /**
         * Returns a map of all {@link PackageState PackageStates} on the device.
         *
         * @return Mapping of package name to {@link PackageState}.
         */
        @NonNull
        Map<String, PackageState> getPackageStates();

        @Override
        void close();
    }

    /**
     * @hide
     */
    interface FilteredSnapshot extends AutoCloseable {

        /**
         * @return {@link PackageState} for the {@code packageName}, filtered if applicable.
         */
        @Nullable
        PackageState getPackageState(@NonNull String packageName);

        /**
         * Iterates on all states. This should only be used when either the target package name
         * is not known or the large majority of the states are expected to be used.
         *
         * This will cause app visibility filtering to be invoked on each state on the device,
         * which can be expensive.
         *
         * @param consumer Block to accept each state as it becomes available post-filtering.
         */
        void forAllPackageStates(@NonNull Consumer<PackageState> consumer);

        @Override
        void close();
    }
}
+40 −24
Original line number Diff line number Diff line
@@ -200,6 +200,7 @@ import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.ArtUtils;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.ViewCompiler;
import com.android.server.pm.local.PackageManagerLocalImpl;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageInternal;
@@ -1071,11 +1072,28 @@ public class PackageManagerService implements PackageSender, TestUtilityService
    @VisibleForTesting(visibility = Visibility.PACKAGE)
    @NonNull
    public Computer snapshotComputer() {
        return snapshotComputer(true /*allowLiveComputer*/);
    }

    /**
     * This method should only ever be called from {@link PackageManagerLocal#snapshot()}.
     *
     * @param allowLiveComputer Whether to allow a live computer instance based on caller {@link
     *                          #mLock} hold state. In certain cases, like for {@link
     *                          PackageManagerLocal} API, it must be enforced that the caller gets
     *                          a snapshot at time, and never the live variant.
     * @deprecated Use {@link #snapshotComputer()}
     */
    @Deprecated
    @NonNull
    public Computer snapshotComputer(boolean allowLiveComputer) {
        if (allowLiveComputer) {
            if (Thread.holdsLock(mLock)) {
                // If the current thread holds mLock then it may have modified state but not
                // yet invalidated the snapshot.  Always give the thread the live computer.
                return mLiveComputer;
            }
        }

        var oldSnapshot = sSnapshot.get();
        var pendingVersion = sSnapshotPendingVersion.get();
@@ -1531,7 +1549,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
        ServiceManager.addService("package", iPackageManager);
        final PackageManagerNative pmn = new PackageManagerNative(m);
        ServiceManager.addService("package_native", pmn);
        LocalManagerRegistry.addManager(PackageManagerLocal.class, m.new PackageManagerLocalImpl());
        LocalManagerRegistry.addManager(PackageManagerLocal.class,
                new PackageManagerLocalImpl(m));
        return Pair.create(m, iPackageManager);
    }

@@ -6017,25 +6036,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
        }
    }

    private class PackageManagerLocalImpl implements PackageManagerLocal {
        @Override
        public void reconcileSdkData(@Nullable String volumeUuid, @NonNull String packageName,
                @NonNull List<String> subDirNames, int userId, int appId, int previousAppId,
                @NonNull String seInfo, int flags) throws IOException {
            synchronized (mInstallLock) {
                ReconcileSdkDataArgs args = mInstaller.buildReconcileSdkDataArgs(volumeUuid,
                        packageName, subDirNames, userId, appId, seInfo,
                        flags);
                args.previousAppId = previousAppId;
                try {
                    mInstaller.reconcileSdkData(args);
                } catch (InstallerException e) {
                    throw new IOException(e.getMessage());
                }
            }
        }
    }

    private class PackageManagerInternalImpl extends PackageManagerInternalBase {

        public PackageManagerInternalImpl() {
@@ -7296,4 +7296,20 @@ public class PackageManagerService implements PackageSender, TestUtilityService
            mSettings.addInstallerPackageNames(installSource);
        }
    }

    public void reconcileSdkData(@Nullable String volumeUuid, @NonNull String packageName,
            @NonNull List<String> subDirNames, int userId, int appId, int previousAppId,
            @NonNull String seInfo, int flags) throws IOException {
        synchronized (mInstallLock) {
            ReconcileSdkDataArgs args = mInstaller.buildReconcileSdkDataArgs(volumeUuid,
                    packageName, subDirNames, userId, appId, seInfo,
                    flags);
            args.previousAppId = previousAppId;
            try {
                mInstaller.reconcileSdkData(args);
            } catch (InstallerException e) {
                throw new IOException(e.getMessage());
            }
        }
    }
}
+184 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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 com.android.server.pm.local;

import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.Binder;
import android.os.UserHandle;

import com.android.server.pm.Computer;
import com.android.server.pm.PackageManagerLocal;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.snapshot.PackageDataSnapshot;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/** @hide */
public class PackageManagerLocalImpl implements PackageManagerLocal {

    private final PackageManagerService mService;

    public PackageManagerLocalImpl(PackageManagerService service) {
        mService = service;
    }

    @Override
    public void reconcileSdkData(@Nullable String volumeUuid, @NonNull String packageName,
            @NonNull List<String> subDirNames, int userId, int appId, int previousAppId,
            @NonNull String seInfo, int flags) throws IOException {
        mService.reconcileSdkData(volumeUuid, packageName, subDirNames, userId, appId,
                previousAppId, seInfo, flags);
    }

    @NonNull
    @Override
    public UnfilteredSnapshotImpl withUnfilteredSnapshot() {
        return new UnfilteredSnapshotImpl(mService.snapshotComputer(false /*allowLiveComputer*/));
    }

    @NonNull
    @Override
    public FilteredSnapshotImpl withFilteredSnapshot() {
        return withFilteredSnapshot(Binder.getCallingUid(), Binder.getCallingUserHandle());
    }

    @NonNull
    @Override
    public FilteredSnapshotImpl withFilteredSnapshot(int callingUid, @NonNull UserHandle user) {
        return new FilteredSnapshotImpl(callingUid, user,
                mService.snapshotComputer(false /*allowLiveComputer*/), null);
    }

    private abstract static class BaseSnapshotImpl implements AutoCloseable {

        private boolean mClosed;

        @NonNull
        protected Computer mSnapshot;

        private BaseSnapshotImpl(@NonNull PackageDataSnapshot snapshot) {
            mSnapshot = (Computer) snapshot;
        }

        @Override
        public void close() {
            mClosed = true;
            mSnapshot = null;
            // TODO: Recycle snapshots?
        }

        @CallSuper
        protected void checkClosed() {
            if (mClosed) {
                throw new IllegalStateException("Snapshot already closed");
            }
        }
    }

    private static class UnfilteredSnapshotImpl extends BaseSnapshotImpl implements
            UnfilteredSnapshot {

        private UnfilteredSnapshotImpl(@NonNull PackageDataSnapshot snapshot) {
            super(snapshot);
        }

        @Override
        public FilteredSnapshot filtered(int callingUid, @NonNull UserHandle user) {
            return new FilteredSnapshotImpl(callingUid, user, mSnapshot, this);
        }

        @SuppressWarnings("RedundantSuppression")
        @NonNull
        @Override
        public Map<String, PackageState> getPackageStates() {
            checkClosed();

            //noinspection unchecked, RedundantCast
            return (Map<String, PackageState>) (Map<?, ?>) mSnapshot.getPackageStates();
        }
    }

    private static class FilteredSnapshotImpl extends BaseSnapshotImpl implements
            FilteredSnapshot {

        private final int mCallingUid;

        @UserIdInt
        private final int mUserId;

        @Nullable
        private ArrayList<PackageState> mFilteredPackageStates;

        @Nullable
        private final UnfilteredSnapshotImpl mParentSnapshot;

        private FilteredSnapshotImpl(int callingUid, @NonNull UserHandle user,
                @NonNull PackageDataSnapshot snapshot,
                @Nullable UnfilteredSnapshotImpl parentSnapshot) {
            super(snapshot);
            mCallingUid = callingUid;
            mUserId = user.getIdentifier();
            mParentSnapshot = parentSnapshot;
        }

        @Override
        protected void checkClosed() {
            if (mParentSnapshot != null) {
                mParentSnapshot.checkClosed();
            }

            super.checkClosed();
        }

        @Nullable
        @Override
        public PackageState getPackageState(@NonNull String packageName) {
            checkClosed();
            return mSnapshot.getPackageStateFiltered(packageName, mCallingUid, mUserId);
        }

        @Override
        public void forAllPackageStates(@NonNull Consumer<PackageState> consumer) {
            checkClosed();

            if (mFilteredPackageStates == null) {
                var packageStates = mSnapshot.getPackageStates();
                var filteredPackageStates = new ArrayList<PackageState>();
                for (int index = 0, size = packageStates.size(); index < size; index++) {
                    var packageState = packageStates.valueAt(index);
                    if (!mSnapshot.shouldFilterApplication(packageState, mCallingUid, mUserId)) {
                        filteredPackageStates.add(packageState);
                    }
                }
                mFilteredPackageStates = filteredPackageStates;
            }

            for (int index = 0, size = mFilteredPackageStates.size(); index < size; index++) {
                var packageState = mFilteredPackageStates.get(index);
                consumer.accept(packageState);
            }
        }
    }
}
Loading