1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm;
18 
19 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
20 
21 import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX;
22 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
23 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
24 import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
25 import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
26 import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
27 import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
28 import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
29 import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
30 import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
31 import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
32 import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
33 import static com.android.server.pm.PackageManagerService.TAG;
34 
35 import android.annotation.NonNull;
36 import android.annotation.Nullable;
37 import android.os.Environment;
38 import android.os.SystemClock;
39 import android.os.Trace;
40 import android.system.ErrnoException;
41 import android.system.Os;
42 import android.util.ArrayMap;
43 import android.util.EventLog;
44 import android.util.Slog;
45 
46 import com.android.internal.annotations.GuardedBy;
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.content.om.OverlayConfig;
49 import com.android.internal.pm.parsing.PackageParser2;
50 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
51 import com.android.internal.util.FrameworkStatsLog;
52 import com.android.server.EventLogTags;
53 import com.android.server.pm.parsing.PackageCacher;
54 import com.android.server.pm.pkg.AndroidPackage;
55 import com.android.server.utils.WatchedArrayMap;
56 
57 import java.io.File;
58 import java.nio.file.Files;
59 import java.util.ArrayList;
60 import java.util.List;
61 import java.util.concurrent.ExecutorService;
62 
63 /**
64  * Part of PackageManagerService that handles init and system packages. This class still needs
65  * further cleanup and eventually all the installation/scanning related logic will go to another
66  * class.
67  */
68 final class InitAppsHelper {
69     private final PackageManagerService mPm;
70     private final List<ScanPartition> mDirsToScanAsSystem;
71     private final int mScanFlags;
72     private final int mSystemParseFlags;
73     private final int mSystemScanFlags;
74     private final InstallPackageHelper mInstallPackageHelper;
75     private final ApexManager mApexManager;
76     private final ExecutorService mExecutorService;
77     /* Tracks how long system scan took */
78     private long mSystemScanTime;
79     /* Track of the number of cached system apps */
80     private int mCachedSystemApps;
81     /* Track of the number of system apps */
82     private int mSystemPackagesCount;
83     private final boolean mIsDeviceUpgrading;
84     private final List<ScanPartition> mSystemPartitions;
85 
86     /**
87      * Tracks new system packages [received in an OTA] that we expect to
88      * find updated user-installed versions. Keys are package name, values
89      * are package location.
90      */
91     private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
92     /* Tracks of any system packages that no longer exist that needs to be pruned. */
93     private final List<String> mPossiblyDeletedUpdatedSystemApps = new ArrayList<>();
94     // Tracks of stub packages that must either be replaced with full versions in the /data
95     // partition or be disabled.
96     private final List<String> mStubSystemApps = new ArrayList<>();
97 
98     // TODO(b/198166813): remove PMS dependency
InitAppsHelper(PackageManagerService pm, ApexManager apexManager, InstallPackageHelper installPackageHelper, List<ScanPartition> systemPartitions)99     InitAppsHelper(PackageManagerService pm, ApexManager apexManager,
100             InstallPackageHelper installPackageHelper,
101             List<ScanPartition> systemPartitions) {
102         mPm = pm;
103         mApexManager = apexManager;
104         mInstallPackageHelper = installPackageHelper;
105         mSystemPartitions = systemPartitions;
106         mDirsToScanAsSystem = getSystemScanPartitions();
107         mIsDeviceUpgrading = mPm.isDeviceUpgrading();
108         // Set flag to monitor and not change apk file paths when scanning install directories.
109         int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
110         if (mIsDeviceUpgrading || mPm.isFirstBoot()) {
111             mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
112         } else {
113             mScanFlags = scanFlags;
114         }
115         mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
116         mSystemScanFlags = mScanFlags | SCAN_AS_SYSTEM;
117         mExecutorService = ParallelPackageParser.makeExecutorService();
118     }
119 
getSystemScanPartitions()120     private List<ScanPartition> getSystemScanPartitions() {
121         final List<ScanPartition> scanPartitions = new ArrayList<>();
122         scanPartitions.addAll(mSystemPartitions);
123         scanPartitions.addAll(getApexScanPartitions());
124         Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions);
125         return scanPartitions;
126     }
127 
getApexScanPartitions()128     private List<ScanPartition> getApexScanPartitions() {
129         final List<ScanPartition> scanPartitions = new ArrayList<>();
130         final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos();
131         for (int i = 0; i < activeApexInfos.size(); i++) {
132             final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
133             if (scanPartition != null) {
134                 scanPartitions.add(scanPartition);
135             }
136         }
137         return scanPartitions;
138     }
139 
resolveApexToScanPartition( ApexManager.ActiveApexInfo apexInfo)140     private static @Nullable ScanPartition resolveApexToScanPartition(
141             ApexManager.ActiveApexInfo apexInfo) {
142         for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
143             ScanPartition sp = SYSTEM_PARTITIONS.get(i);
144             if (apexInfo.preInstalledApexPath.getAbsolutePath().equals(
145                     sp.getFolder().getAbsolutePath())
146                     || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
147                     sp.getFolder().getAbsolutePath() + File.separator)) {
148                 return new ScanPartition(apexInfo.apexDirectory, sp, apexInfo);
149             }
150         }
151         return null;
152     }
153 
154     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
scanApexPackagesTraced(PackageParser2 packageParser)155     private List<ApexManager.ScanResult> scanApexPackagesTraced(PackageParser2 packageParser) {
156         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanApexPackages");
157 
158         try {
159             return mInstallPackageHelper.scanApexPackages(mApexManager.getAllApexInfos(),
160                     mSystemParseFlags, mSystemScanFlags, packageParser, mExecutorService);
161         } finally {
162             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
163         }
164     }
165 
166     /**
167      * Install apps from system dirs.
168      */
169     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
initSystemApps(PackageParser2 packageParser, WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds, long startTime)170     public OverlayConfig initSystemApps(PackageParser2 packageParser,
171             WatchedArrayMap<String, PackageSetting> packageSettings,
172             int[] userIds, long startTime) {
173         // Prepare apex package info before scanning APKs, this information is needed when
174         // scanning apk in apex.
175         final List<ApexManager.ScanResult> apexScanResults = scanApexPackagesTraced(packageParser);
176         mApexManager.notifyScanResult(apexScanResults);
177 
178         scanSystemDirs(packageParser, mExecutorService);
179         // Parse overlay configuration files to set default enable state, mutability, and
180         // priority of system overlays.
181         final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
182         for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
183             final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
184                     apexInfo.apexModuleName);
185             for (String packageName : mApexManager.getApksInApex(apexPackageName)) {
186                 apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
187             }
188         }
189         final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
190                 consumer -> mPm.forEachPackageState(mPm.snapshotComputer(),
191                         packageState -> {
192                             var pkg = packageState.getPkg();
193                             if (pkg != null) {
194                                 consumer.accept(pkg, packageState.isSystem(),
195                                         apkInApexPreInstalledPaths.get(pkg.getPackageName()));
196                             }
197                         }));
198 
199         // do this first before mucking with mPackages for the "expecting better" case
200         updateStubSystemAppsList(mStubSystemApps);
201         mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
202                 mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
203 
204         logSystemAppsScanningTime(startTime);
205         return overlayConfig;
206     }
207 
208     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
logSystemAppsScanningTime(long startTime)209     private void logSystemAppsScanningTime(long startTime) {
210         mCachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
211 
212         // Remove any shared userIDs that have no associated packages
213         mPm.mSettings.pruneSharedUsersLPw();
214         mSystemScanTime = SystemClock.uptimeMillis() - startTime;
215         mSystemPackagesCount = mPm.mPackages.size();
216         Slog.i(TAG, "Finished scanning system apps. Time: " + mSystemScanTime
217                 + " ms, packageCount: " + mSystemPackagesCount
218                 + " , timePerPackage: "
219                 + (mSystemPackagesCount == 0 ? 0 : mSystemScanTime / mSystemPackagesCount)
220                 + " , cached: " + mCachedSystemApps);
221         if (mIsDeviceUpgrading && mSystemPackagesCount > 0) {
222             //CHECKSTYLE:OFF IndentationCheck
223             FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
224                     BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
225                     mSystemScanTime / mSystemPackagesCount);
226             //CHECKSTYLE:ON IndentationCheck
227         }
228     }
229 
230     /**
231      * Fix up the previously-installed app directory mode - they can't be readable by non-system
232      * users to prevent them from listing the dir to discover installed package names.
233      */
fixInstalledAppDirMode()234     void fixInstalledAppDirMode() {
235         try (var files = Files.newDirectoryStream(mPm.getAppInstallDir().toPath())) {
236             files.forEach(dir -> {
237                 try {
238                     Os.chmod(dir.toString(), 0771);
239                 } catch (ErrnoException e) {
240                     Slog.w(TAG, "Failed to fix an installed app dir mode", e);
241                 }
242             });
243         } catch (Exception e) {
244             Slog.w(TAG, "Failed to walk the app install directory to fix the modes", e);
245         }
246     }
247 
248     /**
249      * Install apps/updates from data dir and fix system apps that are affected.
250      */
251     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
initNonSystemApps(PackageParser2 packageParser, @NonNull int[] userIds, long startTime)252     public void initNonSystemApps(PackageParser2 packageParser, @NonNull int[] userIds,
253             long startTime) {
254         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
255                 SystemClock.uptimeMillis());
256 
257         if ((mScanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) == SCAN_FIRST_BOOT_OR_UPGRADE) {
258             fixInstalledAppDirMode();
259         }
260 
261         scanDirTracedLI(mPm.getAppInstallDir(), 0,
262                 mScanFlags | SCAN_REQUIRE_KNOWN, packageParser, mExecutorService, null);
263 
264         List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
265         if (!unfinishedTasks.isEmpty()) {
266             throw new IllegalStateException("Not all tasks finished before calling close: "
267                     + unfinishedTasks);
268         }
269         fixSystemPackages(userIds);
270         logNonSystemAppScanningTime(startTime);
271         mExpectingBetter.clear();
272         mPm.mSettings.pruneRenamedPackagesLPw();
273     }
274 
275     /**
276      * Clean up system packages now that some system package updates have been installed from
277      * the data dir. Also install system stub packages as the last step.
278      */
279     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
fixSystemPackages(@onNull int[] userIds)280     private void fixSystemPackages(@NonNull int[] userIds) {
281         mInstallPackageHelper.cleanupDisabledPackageSettings(mPossiblyDeletedUpdatedSystemApps,
282                 userIds, mScanFlags);
283         mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
284                 mStubSystemApps, mSystemScanFlags, mSystemParseFlags);
285 
286         // Uncompress and install any stubbed system applications.
287         // This must be done last to ensure all stubs are replaced or disabled.
288         mInstallPackageHelper.installSystemStubPackages(mStubSystemApps, mScanFlags);
289     }
290 
291     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
logNonSystemAppScanningTime(long startTime)292     private void logNonSystemAppScanningTime(long startTime) {
293         final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
294                 - mCachedSystemApps;
295 
296         final long dataScanTime = SystemClock.uptimeMillis() - mSystemScanTime - startTime;
297         final int dataPackagesCount = mPm.mPackages.size() - mSystemPackagesCount;
298         Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
299                 + " ms, packageCount: " + dataPackagesCount
300                 + " , timePerPackage: "
301                 + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
302                 + " , cached: " + cachedNonSystemApps);
303         if (mIsDeviceUpgrading && dataPackagesCount > 0) {
304             //CHECKSTYLE:OFF IndentationCheck
305             FrameworkStatsLog.write(
306                     FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
307                     BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
308                     dataScanTime / dataPackagesCount);
309             //CHECKSTYLE:OFF IndentationCheck
310         }
311     }
312 
313     /**
314      * First part of init dir scanning
315      */
316     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService)317     private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {
318         File frameworkDir = new File(Environment.getRootDirectory(), "framework");
319 
320         // Collect vendor/product/system_ext overlay packages. (Do this before scanning
321         // any apps.)
322         // For security and version matching reason, only consider overlay packages if they
323         // reside in the right directory.
324         for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
325             final ScanPartition partition = mDirsToScanAsSystem.get(i);
326             if (partition.getOverlayFolder() == null) {
327                 continue;
328             }
329             scanDirTracedLI(partition.getOverlayFolder(),
330                     mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
331                     packageParser, executorService, partition.apexInfo);
332         }
333 
334         scanDirTracedLI(frameworkDir,
335                 mSystemParseFlags, mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
336                 packageParser, executorService, null);
337         if (!mPm.mPackages.containsKey("android")) {
338             throw new IllegalStateException(
339                     "Failed to load frameworks package; check log for warnings");
340         }
341 
342         for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
343             final ScanPartition partition = mDirsToScanAsSystem.get(i);
344             if (partition.getPrivAppFolder() != null) {
345                 scanDirTracedLI(partition.getPrivAppFolder(),
346                         mSystemParseFlags,
347                         mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
348                         packageParser, executorService, partition.apexInfo);
349             }
350             scanDirTracedLI(partition.getAppFolder(),
351                     mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
352                     packageParser, executorService, partition.apexInfo);
353         }
354     }
355 
356     @GuardedBy("mPm.mLock")
updateStubSystemAppsList(List<String> stubSystemApps)357     private void updateStubSystemAppsList(List<String> stubSystemApps) {
358         final int numPackages = mPm.mPackages.size();
359         for (int index = 0; index < numPackages; index++) {
360             final AndroidPackage pkg = mPm.mPackages.valueAt(index);
361             if (pkg.isStub()) {
362                 stubSystemApps.add(pkg.getPackageName());
363             }
364         }
365     }
366 
367     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
scanDirTracedLI(File scanDir, int parseFlags, int scanFlags, PackageParser2 packageParser, ExecutorService executorService, @Nullable ApexManager.ActiveApexInfo apexInfo)368     private void scanDirTracedLI(File scanDir, int parseFlags, int scanFlags,
369             PackageParser2 packageParser, ExecutorService executorService,
370             @Nullable ApexManager.ActiveApexInfo apexInfo) {
371         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
372         try {
373             if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
374                 // when scanning apk in apexes, we want to check the maxSdkVersion
375                 parseFlags |= PARSE_APK_IN_APEX;
376             }
377             mInstallPackageHelper.installPackagesFromDir(scanDir, parseFlags,
378                     scanFlags, packageParser, executorService, apexInfo);
379         } finally {
380             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
381         }
382     }
383 
isExpectingBetter(String packageName)384     public boolean isExpectingBetter(String packageName) {
385         return mExpectingBetter.containsKey(packageName);
386     }
387 
getDirsToScanAsSystem()388     public List<ScanPartition> getDirsToScanAsSystem() {
389         return mDirsToScanAsSystem;
390     }
391 
392     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
getSystemScanFlags()393     public int getSystemScanFlags() {
394         return mSystemScanFlags;
395     }
396 }
397