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