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.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
22 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
23 import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
24 import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
25 import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
26 import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
27 import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
28 import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
29 import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
30 import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
31 import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
32 import static com.android.server.pm.PackageManagerService.TAG;
33 import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX;
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.util.FrameworkStatsLog;
50 import com.android.server.EventLogTags;
51 import com.android.server.pm.parsing.PackageCacher;
52 import com.android.server.pm.parsing.PackageParser2;
53 import com.android.server.pm.pkg.AndroidPackage;
54 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
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             for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
184                 apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
185             }
186         }
187         final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
188                 consumer -> mPm.forEachPackageState(mPm.snapshotComputer(),
189                         packageState -> {
190                             var pkg = packageState.getPkg();
191                             if (pkg != null) {
192                                 consumer.accept(pkg, packageState.isSystem(),
193                                         apkInApexPreInstalledPaths.get(pkg.getPackageName()));
194                             }
195                         }));
196 
197         // do this first before mucking with mPackages for the "expecting better" case
198         updateStubSystemAppsList(mStubSystemApps);
199         mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
200                 mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
201 
202         logSystemAppsScanningTime(startTime);
203         return overlayConfig;
204     }
205 
206     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
logSystemAppsScanningTime(long startTime)207     private void logSystemAppsScanningTime(long startTime) {
208         mCachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
209 
210         // Remove any shared userIDs that have no associated packages
211         mPm.mSettings.pruneSharedUsersLPw();
212         mSystemScanTime = SystemClock.uptimeMillis() - startTime;
213         mSystemPackagesCount = mPm.mPackages.size();
214         Slog.i(TAG, "Finished scanning system apps. Time: " + mSystemScanTime
215                 + " ms, packageCount: " + mSystemPackagesCount
216                 + " , timePerPackage: "
217                 + (mSystemPackagesCount == 0 ? 0 : mSystemScanTime / mSystemPackagesCount)
218                 + " , cached: " + mCachedSystemApps);
219         if (mIsDeviceUpgrading && mSystemPackagesCount > 0) {
220             //CHECKSTYLE:OFF IndentationCheck
221             FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
222                     BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
223                     mSystemScanTime / mSystemPackagesCount);
224             //CHECKSTYLE:ON IndentationCheck
225         }
226     }
227 
228     /**
229      * Fix up the previously-installed app directory mode - they can't be readable by non-system
230      * users to prevent them from listing the dir to discover installed package names.
231      */
fixInstalledAppDirMode()232     void fixInstalledAppDirMode() {
233         try (var files = Files.newDirectoryStream(mPm.getAppInstallDir().toPath())) {
234             files.forEach(dir -> {
235                 try {
236                     Os.chmod(dir.toString(), 0771);
237                 } catch (ErrnoException e) {
238                     Slog.w(TAG, "Failed to fix an installed app dir mode", e);
239                 }
240             });
241         } catch (Exception e) {
242             Slog.w(TAG, "Failed to walk the app install directory to fix the modes", e);
243         }
244     }
245 
246     /**
247      * Install apps/updates from data dir and fix system apps that are affected.
248      */
249     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
initNonSystemApps(PackageParser2 packageParser, @NonNull int[] userIds, long startTime)250     public void initNonSystemApps(PackageParser2 packageParser, @NonNull int[] userIds,
251             long startTime) {
252         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
253                 SystemClock.uptimeMillis());
254 
255         if ((mScanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) == SCAN_FIRST_BOOT_OR_UPGRADE) {
256             fixInstalledAppDirMode();
257         }
258 
259         scanDirTracedLI(mPm.getAppInstallDir(), 0,
260                 mScanFlags | SCAN_REQUIRE_KNOWN, packageParser, mExecutorService, null);
261 
262         List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
263         if (!unfinishedTasks.isEmpty()) {
264             throw new IllegalStateException("Not all tasks finished before calling close: "
265                     + unfinishedTasks);
266         }
267         fixSystemPackages(userIds);
268         logNonSystemAppScanningTime(startTime);
269         mExpectingBetter.clear();
270         mPm.mSettings.pruneRenamedPackagesLPw();
271     }
272 
273     /**
274      * Clean up system packages now that some system package updates have been installed from
275      * the data dir. Also install system stub packages as the last step.
276      */
277     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
fixSystemPackages(@onNull int[] userIds)278     private void fixSystemPackages(@NonNull int[] userIds) {
279         mInstallPackageHelper.cleanupDisabledPackageSettings(mPossiblyDeletedUpdatedSystemApps,
280                 userIds, mScanFlags);
281         mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
282                 mStubSystemApps, mSystemScanFlags, mSystemParseFlags);
283 
284         // Uncompress and install any stubbed system applications.
285         // This must be done last to ensure all stubs are replaced or disabled.
286         mInstallPackageHelper.installSystemStubPackages(mStubSystemApps, mScanFlags);
287     }
288 
289     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
logNonSystemAppScanningTime(long startTime)290     private void logNonSystemAppScanningTime(long startTime) {
291         final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
292                 - mCachedSystemApps;
293 
294         final long dataScanTime = SystemClock.uptimeMillis() - mSystemScanTime - startTime;
295         final int dataPackagesCount = mPm.mPackages.size() - mSystemPackagesCount;
296         Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
297                 + " ms, packageCount: " + dataPackagesCount
298                 + " , timePerPackage: "
299                 + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
300                 + " , cached: " + cachedNonSystemApps);
301         if (mIsDeviceUpgrading && dataPackagesCount > 0) {
302             //CHECKSTYLE:OFF IndentationCheck
303             FrameworkStatsLog.write(
304                     FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
305                     BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
306                     dataScanTime / dataPackagesCount);
307             //CHECKSTYLE:OFF IndentationCheck
308         }
309     }
310 
311     /**
312      * First part of init dir scanning
313      */
314     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService)315     private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {
316         File frameworkDir = new File(Environment.getRootDirectory(), "framework");
317 
318         // Collect vendor/product/system_ext overlay packages. (Do this before scanning
319         // any apps.)
320         // For security and version matching reason, only consider overlay packages if they
321         // reside in the right directory.
322         for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
323             final ScanPartition partition = mDirsToScanAsSystem.get(i);
324             if (partition.getOverlayFolder() == null) {
325                 continue;
326             }
327             scanDirTracedLI(partition.getOverlayFolder(),
328                     mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
329                     packageParser, executorService, partition.apexInfo);
330         }
331 
332         scanDirTracedLI(frameworkDir,
333                 mSystemParseFlags, mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
334                 packageParser, executorService, null);
335         if (!mPm.mPackages.containsKey("android")) {
336             throw new IllegalStateException(
337                     "Failed to load frameworks package; check log for warnings");
338         }
339 
340         for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
341             final ScanPartition partition = mDirsToScanAsSystem.get(i);
342             if (partition.getPrivAppFolder() != null) {
343                 scanDirTracedLI(partition.getPrivAppFolder(),
344                         mSystemParseFlags,
345                         mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
346                         packageParser, executorService, partition.apexInfo);
347             }
348             scanDirTracedLI(partition.getAppFolder(),
349                     mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
350                     packageParser, executorService, partition.apexInfo);
351         }
352     }
353 
354     @GuardedBy("mPm.mLock")
updateStubSystemAppsList(List<String> stubSystemApps)355     private void updateStubSystemAppsList(List<String> stubSystemApps) {
356         final int numPackages = mPm.mPackages.size();
357         for (int index = 0; index < numPackages; index++) {
358             final AndroidPackage pkg = mPm.mPackages.valueAt(index);
359             if (pkg.isStub()) {
360                 stubSystemApps.add(pkg.getPackageName());
361             }
362         }
363     }
364 
365     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
scanDirTracedLI(File scanDir, int parseFlags, int scanFlags, PackageParser2 packageParser, ExecutorService executorService, @Nullable ApexManager.ActiveApexInfo apexInfo)366     private void scanDirTracedLI(File scanDir, int parseFlags, int scanFlags,
367             PackageParser2 packageParser, ExecutorService executorService,
368             @Nullable ApexManager.ActiveApexInfo apexInfo) {
369         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
370         try {
371             if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
372                 // when scanning apk in apexes, we want to check the maxSdkVersion
373                 parseFlags |= PARSE_APK_IN_APEX;
374             }
375             mInstallPackageHelper.installPackagesFromDir(scanDir, parseFlags,
376                     scanFlags, packageParser, executorService, apexInfo);
377         } finally {
378             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
379         }
380     }
381 
isExpectingBetter(String packageName)382     public boolean isExpectingBetter(String packageName) {
383         return mExpectingBetter.containsKey(packageName);
384     }
385 
getDirsToScanAsSystem()386     public List<ScanPartition> getDirsToScanAsSystem() {
387         return mDirsToScanAsSystem;
388     }
389 
390     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
getSystemScanFlags()391     public int getSystemScanFlags() {
392         return mSystemScanFlags;
393     }
394 }
395