Loading services/core/java/com/android/server/pm/Computer.java +2 −0 Original line number Diff line number Diff line Loading @@ -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); Loading services/core/java/com/android/server/pm/ComputerEngine.java +13 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading services/core/java/com/android/server/pm/PackageManagerLocal.java +104 −2 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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. Loading @@ -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(); } } services/core/java/com/android/server/pm/PackageManagerService.java +40 −24 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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); } Loading Loading @@ -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() { Loading Loading @@ -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()); } } } } services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java 0 → 100644 +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
services/core/java/com/android/server/pm/Computer.java +2 −0 Original line number Diff line number Diff line Loading @@ -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); Loading
services/core/java/com/android/server/pm/ComputerEngine.java +13 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading
services/core/java/com/android/server/pm/PackageManagerLocal.java +104 −2 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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. Loading @@ -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(); } }
services/core/java/com/android/server/pm/PackageManagerService.java +40 −24 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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); } Loading Loading @@ -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() { Loading Loading @@ -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()); } } } }
services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java 0 → 100644 +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); } } } }