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