From 90451b716d8b42bed7454e9a3d891f7a6c10d192 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Mon, 17 Jul 2023 11:08:03 -0700 Subject: [PATCH 1/2] [pm] fix SCAN_AS_STOPPED_SYSTEM_APP for restoreDisabledSystemPackage Use ParsedPackage to directly check if the package has a launcher entry or not. This way, we can unify the system-apps-stopped code path for 1) installing new system apps after an OTA, and 2) restoring disabled system apps after uninstalling the update. BUG: 286459841 Test: manual with a system app that doesn't have a launcher entry Test: also manually checked that a system app with a launcher entry will be stopped by default Merged-In: Ic51a9ceb7db6d887cebc390bb2878db8810cddbf Change-Id: Ic51a9ceb7db6d887cebc390bb2878db8810cddbf (cherry picked from commit c3415b9f2ff755cde0403eb121e99de924007edd) --- .../server/pm/InstallPackageHelper.java | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 50f1673cae443..2b55a612a7ad9 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -110,6 +110,7 @@ import android.app.backup.IBackupManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.DataLoaderType; @@ -119,7 +120,6 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; -import android.content.pm.ResolveInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.Signature; import android.content.pm.SigningDetails; @@ -184,7 +184,9 @@ import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.SharedLibraryWrapper; import com.android.server.pm.pkg.component.ComponentMutateUtils; +import com.android.server.pm.pkg.component.ParsedActivity; import com.android.server.pm.pkg.component.ParsedInstrumentation; +import com.android.server.pm.pkg.component.ParsedIntentInfo; import com.android.server.pm.pkg.component.ParsedPermission; import com.android.server.pm.pkg.component.ParsedPermissionGroup; import com.android.server.pm.pkg.parsing.ParsingPackageUtils; @@ -207,6 +209,7 @@ import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -3925,23 +3928,6 @@ final class InstallPackageHelper { } } - // If this is a system app we hadn't seen before, and this is a first boot or OTA, - // we need to unstop it if it doesn't have a launcher entry. - if (mPm.mShouldStopSystemPackagesByDefault && scanResult.mRequest.mPkgSetting == null - && ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) - && ((scanFlags & SCAN_AS_SYSTEM) != 0)) { - final Intent launcherIntent = new Intent(Intent.ACTION_MAIN); - launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER); - launcherIntent.setPackage(parsedPackage.getPackageName()); - final List launcherActivities = - mPm.snapshotComputer().queryIntentActivitiesInternal(launcherIntent, null, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0); - if (launcherActivities.isEmpty()) { - scanResult.mPkgSetting.setStopped(false, 0); - } - } - if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) { if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isLoading()) { // Continue monitoring loading progress of active incremental packages @@ -4314,6 +4300,7 @@ final class InstallPackageHelper { // - It's an APEX or overlay package since stopped state does not affect them. // - It is enumerated with a tag having the stopped attribute // set to false + // - It doesn't have a launcher entry which means the user doesn't have a way to unstop it final boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0; if (mPm.mShouldStopSystemPackagesByDefault && scanSystemPartition @@ -4322,8 +4309,9 @@ final class InstallPackageHelper { && !parsedPackage.isOverlayIsStatic() ) { String packageName = parsedPackage.getPackageName(); - if (!mPm.mInitialNonStoppedSystemPackages.contains(packageName) - && !"android".contentEquals(packageName)) { + if (!"android".contentEquals(packageName) + && !mPm.mInitialNonStoppedSystemPackages.contains(packageName) + && hasLauncherEntry(parsedPackage)) { scanFlags |= SCAN_AS_STOPPED_SYSTEM_APP; } } @@ -4333,6 +4321,22 @@ final class InstallPackageHelper { return new Pair<>(scanResult, shouldHideSystemApp); } + private static boolean hasLauncherEntry(ParsedPackage parsedPackage) { + final HashSet categories = new HashSet<>(); + categories.add(Intent.CATEGORY_LAUNCHER); + final List activities = parsedPackage.getActivities(); + for (int indexActivity = 0; indexActivity < activities.size(); indexActivity++) { + final List intents = activities.get(indexActivity).getIntents(); + for (int indexIntent = 0; indexIntent < intents.size(); indexIntent++) { + final IntentFilter intentFilter = intents.get(indexIntent).getIntentFilter(); + if (intentFilter != null && intentFilter.matchCategories(categories) == null) { + return true; + } + } + } + return false; + } + /** * Returns if forced apk verification can be skipped for the whole package, including splits. */ From e9c83eae3c6c2db5866053adc060725959fa0a2c Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Tue, 18 Jul 2023 16:33:32 -0700 Subject: [PATCH 2/2] [pm] do not stop system apps with disabled a launcher activity If an app has a disabled/unexported launcher activity, it is effectively the same as not having a launcher activity. We should not stop such system apps by default. BUG: 286459841 Test: manual with a system app that has disabled launcher activity and verify that it is in the `stopped=false` state after first boot Test: also tested updating such a system app and verified that it remains in the un-stopped state after the update is uninstalled Merged-In: Ie865aba7325e3442c6610ef962fce3a3a65f0b70 Change-Id: Ie865aba7325e3442c6610ef962fce3a3a65f0b70 (cherry picked from commit a1c1f2e29f0fbb6f7ea61db445906c4bfc5e4f33) --- .../java/com/android/server/pm/InstallPackageHelper.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 2b55a612a7ad9..fbd54555dbbf1 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -4300,7 +4300,8 @@ final class InstallPackageHelper { // - It's an APEX or overlay package since stopped state does not affect them. // - It is enumerated with a tag having the stopped attribute // set to false - // - It doesn't have a launcher entry which means the user doesn't have a way to unstop it + // - It doesn't have an enabled and exported launcher activity, which means the user + // wouldn't have a way to un-stop it final boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0; if (mPm.mShouldStopSystemPackagesByDefault && scanSystemPartition @@ -4326,7 +4327,11 @@ final class InstallPackageHelper { categories.add(Intent.CATEGORY_LAUNCHER); final List activities = parsedPackage.getActivities(); for (int indexActivity = 0; indexActivity < activities.size(); indexActivity++) { - final List intents = activities.get(indexActivity).getIntents(); + final ParsedActivity activity = activities.get(indexActivity); + if (!activity.isEnabled() || !activity.isExported()) { + continue; + } + final List intents = activity.getIntents(); for (int indexIntent = 0; indexIntent < intents.size(); indexIntent++) { final IntentFilter intentFilter = intents.get(indexIntent).getIntentFilter(); if (intentFilter != null && intentFilter.matchCategories(categories) == null) {