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

Commit 908f09e7 authored by Songchun Fan's avatar Songchun Fan
Browse files

[pm] move storage and private app related code into a separate helper

Test: builds
BUG: 198177734
Change-Id: Ib9e12cafaa8f0f5e48f19dd434c3bfb1e2ec0035
parent b765c352
Loading
Loading
Loading
Loading
+10 −413

File changed.

Preview size limit exceeded, changes collapsed.

+5 −3
Original line number Diff line number Diff line
@@ -551,6 +551,7 @@ final class InstallParams extends HandlerParams {
                new ArrayMap<>(requests.size());
        final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
        boolean success = false;
        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
            for (InstallRequest request : requests) {
@@ -579,7 +580,7 @@ final class InstallParams extends HandlerParams {
                installResults.put(packageName, request.mInstallResult);
                installArgs.put(packageName, request.mArgs);
                try {
                    final ScanResult result = mPm.scanPackageTracedLI(
                    final ScanResult result = scanPackageHelper.scanPackageTracedLI(
                            prepareResult.mPackageToScan, prepareResult.mParseFlags,
                            prepareResult.mScanFlags, System.currentTimeMillis(),
                            request.mArgs.mUser, request.mArgs.mAbiOverride);
@@ -591,7 +592,8 @@ final class InstallParams extends HandlerParams {
                                        + " in multi-package install request.");
                        return;
                    }
                    createdAppId.put(packageName, mPm.optimisticallyRegisterAppId(result));
                    createdAppId.put(packageName,
                            scanPackageHelper.optimisticallyRegisterAppId(result));
                    versionInfos.put(result.mPkgSetting.pkg.getPackageName(),
                            mPm.getSettingsVersionForPackage(result.mPkgSetting.pkg));
                    if (result.mStaticSharedLibraryInfo != null) {
@@ -668,7 +670,7 @@ final class InstallParams extends HandlerParams {
                for (ScanResult result : preparedScans.values()) {
                    if (createdAppId.getOrDefault(result.mRequest.mParsedPackage.getPackageName(),
                            false)) {
                        mPm.cleanUpAppIdCreation(result);
                        scanPackageHelper.cleanUpAppIdCreation(result);
                    }
                }
                // TODO(b/194319951): create a more descriptive reason than unknown
+14 −1614

File changed.

Preview size limit exceeded, changes collapsed.

+1822 −0

File added.

Preview size limit exceeded, changes collapsed.

+344 −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 com.android.server.pm;

import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;

import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;

import android.app.ResourcesManager;
import android.content.IIntentReceiver;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.os.UserHandle;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;

import com.android.internal.policy.AttributeCache;
import com.android.server.pm.parsing.pkg.AndroidPackage;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/** Helper class to handle storage events and private apps loading */
public class StorageEventHelper extends StorageEventListener {
    final PackageManagerService mPm;

    // TODO(b/198166813): remove PMS dependency
    public StorageEventHelper(PackageManagerService pm) {
        mPm = pm;
    }

    @Override
    public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
        if (vol.type == VolumeInfo.TYPE_PRIVATE) {
            if (vol.state == VolumeInfo.STATE_MOUNTED) {
                final String volumeUuid = vol.getFsUuid();

                // Clean up any users or apps that were removed or recreated
                // while this volume was missing
                mPm.mUserManager.reconcileUsers(volumeUuid);
                reconcileApps(volumeUuid);

                // Clean up any install sessions that expired or were
                // cancelled while this volume was missing
                mPm.mInstallerService.onPrivateVolumeMounted(volumeUuid);

                loadPrivatePackages(vol);

            } else if (vol.state == VolumeInfo.STATE_EJECTING) {
                unloadPrivatePackages(vol);
            }
        }
    }

    @Override
    public void onVolumeForgotten(String fsUuid) {
        if (TextUtils.isEmpty(fsUuid)) {
            Slog.e(TAG, "Forgetting internal storage is probably a mistake; ignoring");
            return;
        }

        // Remove any apps installed on the forgotten volume
        synchronized (mPm.mLock) {
            final List<PackageSetting> packages = mPm.mSettings.getVolumePackagesLPr(fsUuid);
            for (PackageSetting ps : packages) {
                Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
                mPm.deletePackageVersioned(new VersionedPackage(ps.name,
                                PackageManager.VERSION_CODE_HIGHEST),
                        new PackageManager.LegacyPackageDeleteObserver(null).getBinder(),
                        UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS);
                // Try very hard to release any references to this package
                // so we don't risk the system server being killed due to
                // open FDs
                AttributeCache.instance().removePackage(ps.name);
            }

            mPm.mSettings.onVolumeForgotten(fsUuid);
            mPm.writeSettingsLPrTEMP();
        }
    }

    private void loadPrivatePackages(final VolumeInfo vol) {
        mPm.mHandler.post(() -> loadPrivatePackagesInner(vol));
    }

    private void loadPrivatePackagesInner(VolumeInfo vol) {
        final String volumeUuid = vol.fsUuid;
        if (TextUtils.isEmpty(volumeUuid)) {
            Slog.e(TAG, "Loading internal storage is probably a mistake; ignoring");
            return;
        }

        final ArrayList<PackageFreezer> freezers = new ArrayList<>();
        final ArrayList<AndroidPackage> loaded = new ArrayList<>();
        final int parseFlags = mPm.mDefParseFlags | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;

        final Settings.VersionInfo ver;
        final List<PackageSetting> packages;
        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
        synchronized (mPm.mLock) {
            ver = mPm.mSettings.findOrCreateVersion(volumeUuid);
            packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid);
        }

        for (PackageSetting ps : packages) {
            freezers.add(mPm.freezePackage(ps.name, "loadPrivatePackagesInner"));
            synchronized (mPm.mInstallLock) {
                final AndroidPackage pkg;
                try {
                    pkg = scanPackageHelper.scanPackageTracedLI(
                            ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
                    loaded.add(pkg);

                } catch (PackageManagerException e) {
                    Slog.w(TAG, "Failed to scan " + ps.getPath() + ": " + e.getMessage());
                }

                if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
                    mPm.clearAppDataLIF(
                            ps.pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
                            | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
                            | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
                }
            }
        }

        // Reconcile app data for all started/unlocked users
        final StorageManager sm = mPm.mInjector.getSystemService(StorageManager.class);
        UserManagerInternal umInternal = mPm.mInjector.getUserManagerInternal();
        for (UserInfo user : mPm.mUserManager.getUsers(false /* includeDying */)) {
            final int flags;
            if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
                flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
            } else if (umInternal.isUserRunning(user.id)) {
                flags = StorageManager.FLAG_STORAGE_DE;
            } else {
                continue;
            }

            try {
                sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
                synchronized (mPm.mInstallLock) {
                    mPm.reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */,
                            null);
                }
            } catch (IllegalStateException e) {
                // Device was probably ejected, and we'll process that event momentarily
                Slog.w(TAG, "Failed to prepare storage: " + e);
            }
        }

        synchronized (mPm.mLock) {
            final boolean isUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
            if (isUpgrade) {
                logCriticalInfo(Log.INFO, "Build fingerprint changed from " + ver.fingerprint
                        + " to " + Build.FINGERPRINT + "; regranting permissions for "
                        + volumeUuid);
            }
            mPm.mPermissionManager.onStorageVolumeMounted(volumeUuid, isUpgrade);

            // Yay, everything is now upgraded
            ver.forceCurrent();

            mPm.writeSettingsLPrTEMP();
        }

        for (PackageFreezer freezer : freezers) {
            freezer.close();
        }

        if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
        sendResourcesChangedBroadcast(true, false, loaded, null);
        synchronized (mPm.mLoadedVolumes) {
            mPm.mLoadedVolumes.add(vol.getId());
        }
    }

    private void unloadPrivatePackages(final VolumeInfo vol) {
        mPm.mHandler.post(() -> unloadPrivatePackagesInner(vol));
    }

    private void unloadPrivatePackagesInner(VolumeInfo vol) {
        final String volumeUuid = vol.fsUuid;
        if (TextUtils.isEmpty(volumeUuid)) {
            Slog.e(TAG, "Unloading internal storage is probably a mistake; ignoring");
            return;
        }

        final int[] userIds = mPm.mUserManager.getUserIds();
        final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
        synchronized (mPm.mInstallLock) {
            synchronized (mPm.mLock) {
                final List<PackageSetting> packages =
                        mPm.mSettings.getVolumePackagesLPr(volumeUuid);
                for (PackageSetting ps : packages) {
                    if (ps.pkg == null) continue;

                    final AndroidPackage pkg = ps.pkg;
                    final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
                    final PackageRemovedInfo outInfo = new PackageRemovedInfo(mPm);

                    try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.name, deleteFlags,
                            "unloadPrivatePackagesInner")) {
                        if (mPm.deletePackageLIF(ps.name, null, false, userIds, deleteFlags,
                                outInfo, false)) {
                            unloaded.add(pkg);
                        } else {
                            Slog.w(TAG, "Failed to unload " + ps.getPath());
                        }
                    }

                    // Try very hard to release any references to this package
                    // so we don't risk the system server being killed due to
                    // open FDs
                    AttributeCache.instance().removePackage(ps.name);
                }

                mPm.writeSettingsLPrTEMP();
            }
        }

        if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
        sendResourcesChangedBroadcast(false, false, unloaded, null);
        synchronized (mPm.mLoadedVolumes) {
            mPm.mLoadedVolumes.remove(vol.getId());
        }

        // Try very hard to release any references to this path so we don't risk
        // the system server being killed due to open FDs
        ResourcesManager.getInstance().invalidatePath(vol.getPath().getAbsolutePath());

        for (int i = 0; i < 3; i++) {
            System.gc();
            System.runFinalization();
        }
    }

    private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
            ArrayList<AndroidPackage> packages, IIntentReceiver finishedReceiver) {
        final int size = packages.size();
        final String[] packageNames = new String[size];
        final int[] packageUids = new int[size];
        for (int i = 0; i < size; i++) {
            final AndroidPackage pkg = packages.get(i);
            packageNames[i] = pkg.getPackageName();
            packageUids[i] = pkg.getUid();
        }
        mPm.sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
                finishedReceiver);
    }

    /**
     * Examine all apps present on given mounted volume, and destroy apps that
     * aren't expected, either due to uninstallation or reinstallation on
     * another volume.
     */
    public void reconcileApps(String volumeUuid) {
        List<String> absoluteCodePaths = collectAbsoluteCodePaths();
        List<File> filesToDelete = null;

        final File[] files = FileUtils.listFilesOrEmpty(
                Environment.getDataAppDirectory(volumeUuid));
        for (File file : files) {
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }

            String absolutePath = file.getAbsolutePath();

            boolean pathValid = false;
            final int absoluteCodePathCount = absoluteCodePaths.size();
            for (int i = 0; i < absoluteCodePathCount; i++) {
                String absoluteCodePath = absoluteCodePaths.get(i);
                if (absoluteCodePath.startsWith(absolutePath)) {
                    pathValid = true;
                    break;
                }
            }

            if (!pathValid) {
                if (filesToDelete == null) {
                    filesToDelete = new ArrayList<>();
                }
                filesToDelete.add(file);
            }
        }

        if (filesToDelete != null) {
            final int fileToDeleteCount = filesToDelete.size();
            for (int i = 0; i < fileToDeleteCount; i++) {
                File fileToDelete = filesToDelete.get(i);
                logCriticalInfo(Log.WARN, "Destroying orphaned at " + fileToDelete);
                synchronized (mPm.mInstallLock) {
                    mPm.removeCodePathLI(fileToDelete);
                }
            }
        }
    }

    private List<String> collectAbsoluteCodePaths() {
        synchronized (mPm.mLock) {
            List<String> codePaths = new ArrayList<>();
            final int packageCount = mPm.mSettings.getPackagesLocked().size();
            for (int i = 0; i < packageCount; i++) {
                final PackageSetting ps = mPm.mSettings.getPackagesLocked().valueAt(i);
                codePaths.add(ps.getPath().getAbsolutePath());
            }
            return codePaths;
        }
    }
}
Loading