From d150569b030aab4e8fe1c3d1cc3cd14422d9fcbf Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Mon, 15 Nov 2021 20:07:29 +0800 Subject: [PATCH] Fix PackageManager did not recognize OTA when only upgrade product - Introduce the PackagePartitions.FINGERPRINT to represent the fingerprint of the build and all system partitions. Using it to determine whether the system update has occurred. - Update PackageManager relating modules to replace Build.FINGERPRINT with the PackagePartitions.FINGERPRINT. Bug: 135224411 Test: atest PackagePartitionsTest Change-Id: I4c6244cd71cd0954b4d551f62b81b6ce3e2c4b17 --- .../android/content/pm/PackagePartitions.java | 64 ++++++++++++++++--- core/java/android/os/Build.java | 20 +++++- .../content/pm/PackagePartitionsTest.java | 49 ++++++++++++++ .../com/android/server/am/UserController.java | 4 +- .../server/pm/PackageManagerService.java | 13 ++-- .../server/pm/PackageManagerServiceUtils.java | 3 +- .../java/com/android/server/pm/Settings.java | 5 +- .../android/server/pm/StorageEventHelper.java | 8 +-- .../android/server/pm/UserManagerService.java | 11 ++-- 9 files changed, 148 insertions(+), 29 deletions(-) create mode 100644 core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java index d1577684aac6c..ff80e614be58b 100644 --- a/core/java/android/content/pm/PackagePartitions.java +++ b/core/java/android/content/pm/PackagePartitions.java @@ -19,8 +19,11 @@ package android.content.pm; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Build; +import android.os.Build.Partition; import android.os.Environment; import android.os.FileUtils; +import android.os.SystemProperties; import com.android.internal.annotations.VisibleForTesting; @@ -64,19 +67,33 @@ public class PackagePartitions { */ private static final ArrayList SYSTEM_PARTITIONS = new ArrayList<>(Arrays.asList( - new SystemPartition(Environment.getRootDirectory(), PARTITION_SYSTEM, + new SystemPartition(Environment.getRootDirectory(), + PARTITION_SYSTEM, Partition.PARTITION_NAME_SYSTEM, true /* containsPrivApp */, false /* containsOverlay */), - new SystemPartition(Environment.getVendorDirectory(), PARTITION_VENDOR, + new SystemPartition(Environment.getVendorDirectory(), + PARTITION_VENDOR, Partition.PARTITION_NAME_VENDOR, true /* containsPrivApp */, true /* containsOverlay */), - new SystemPartition(Environment.getOdmDirectory(), PARTITION_ODM, + new SystemPartition(Environment.getOdmDirectory(), + PARTITION_ODM, Partition.PARTITION_NAME_ODM, true /* containsPrivApp */, true /* containsOverlay */), - new SystemPartition(Environment.getOemDirectory(), PARTITION_OEM, + new SystemPartition(Environment.getOemDirectory(), + PARTITION_OEM, Partition.PARTITION_NAME_OEM, false /* containsPrivApp */, true /* containsOverlay */), - new SystemPartition(Environment.getProductDirectory(), PARTITION_PRODUCT, + new SystemPartition(Environment.getProductDirectory(), + PARTITION_PRODUCT, Partition.PARTITION_NAME_PRODUCT, true /* containsPrivApp */, true /* containsOverlay */), - new SystemPartition(Environment.getSystemExtDirectory(), PARTITION_SYSTEM_EXT, + new SystemPartition(Environment.getSystemExtDirectory(), + PARTITION_SYSTEM_EXT, Partition.PARTITION_NAME_SYSTEM_EXT, true /* containsPrivApp */, true /* containsOverlay */))); + /** + * A string to represent the fingerprint of this build and all package partitions. Using it to + * determine whether the system update has occurred. Different from {@link Build#FINGERPRINT}, + * this string is digested from the fingerprints of the build and all package partitions to + * help detect the partition update. + */ + public static final String FINGERPRINT = getFingerprint(); + /** * Returns a list in which the elements are products of the specified function applied to the * list of {@link #SYSTEM_PARTITIONS} in increasing specificity order. @@ -101,12 +118,32 @@ public class PackagePartitions { } } + /** + * Returns a fingerprint string for this build and all package partitions. The string is + * digested from the fingerprints of the build and all package partitions. + * + * @return A string to represent the fingerprint of this build and all package partitions. + */ + @NonNull + private static String getFingerprint() { + final String[] digestProperties = new String[SYSTEM_PARTITIONS.size() + 1]; + for (int i = 0; i < SYSTEM_PARTITIONS.size(); i++) { + final String partitionName = SYSTEM_PARTITIONS.get(i).getName(); + digestProperties[i] = "ro." + partitionName + ".build.fingerprint"; + } + digestProperties[SYSTEM_PARTITIONS.size()] = "ro.build.fingerprint"; // build fingerprint + return SystemProperties.digestOf(digestProperties); + } + /** Represents a partition that contains application packages. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public static class SystemPartition { @PartitionType public final int type; + @NonNull + private final String mName; + @NonNull private final DeferredCanonicalFile mFolder; @@ -122,9 +159,10 @@ public class PackagePartitions { @NonNull private final File mNonConicalFolder; - private SystemPartition(@NonNull File folder, @PartitionType int type, + private SystemPartition(@NonNull File folder, @PartitionType int type, String name, boolean containsPrivApp, boolean containsOverlay) { this.type = type; + this.mName = name; this.mFolder = new DeferredCanonicalFile(folder); this.mAppFolder = new DeferredCanonicalFile(folder, "app"); this.mPrivAppFolder = containsPrivApp ? new DeferredCanonicalFile(folder, "priv-app") @@ -136,6 +174,7 @@ public class PackagePartitions { public SystemPartition(@NonNull SystemPartition original) { this.type = original.type; + this.mName = original.mName; this.mFolder = new DeferredCanonicalFile(original.mFolder.getFile()); this.mAppFolder = original.mAppFolder; this.mPrivAppFolder = original.mPrivAppFolder; @@ -148,10 +187,19 @@ public class PackagePartitions { * different root folder. */ public SystemPartition(@NonNull File rootFolder, @NonNull SystemPartition partition) { - this(rootFolder, partition.type, partition.mPrivAppFolder != null, + this(rootFolder, partition.type, partition.mName, partition.mPrivAppFolder != null, partition.mOverlayFolder != null); } + /** + * Returns the name identifying the partition. + * @see Partition + */ + @NonNull + public String getName() { + return mName; + } + /** Returns the canonical folder of the partition. */ @NonNull public File getFolder() { diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 743468a6dd088..35b9ccc83e0b9 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1294,6 +1294,18 @@ public class Build { public static class Partition { /** The name identifying the system partition. */ public static final String PARTITION_NAME_SYSTEM = "system"; + /** @hide */ + public static final String PARTITION_NAME_BOOTIMAGE = "bootimage"; + /** @hide */ + public static final String PARTITION_NAME_ODM = "odm"; + /** @hide */ + public static final String PARTITION_NAME_OEM = "oem"; + /** @hide */ + public static final String PARTITION_NAME_PRODUCT = "product"; + /** @hide */ + public static final String PARTITION_NAME_SYSTEM_EXT = "system_ext"; + /** @hide */ + public static final String PARTITION_NAME_VENDOR = "vendor"; private final String mName; private final String mFingerprint; @@ -1350,8 +1362,12 @@ public class Build { ArrayList partitions = new ArrayList(); String[] names = new String[] { - "bootimage", "odm", "product", "system_ext", Partition.PARTITION_NAME_SYSTEM, - "vendor" + Partition.PARTITION_NAME_BOOTIMAGE, + Partition.PARTITION_NAME_ODM, + Partition.PARTITION_NAME_PRODUCT, + Partition.PARTITION_NAME_SYSTEM_EXT, + Partition.PARTITION_NAME_SYSTEM, + Partition.PARTITION_NAME_VENDOR }; for (String name : names) { String fingerprint = SystemProperties.get("ro." + name + ".build.fingerprint"); diff --git a/core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java b/core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java new file mode 100644 index 0000000000000..570b713479eed --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import static com.google.common.truth.Truth.assertThat; + +import static java.util.function.Function.identity; + +import android.content.pm.PackagePartitions.SystemPartition; +import android.os.SystemProperties; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +@RunWith(AndroidJUnit4.class) +public class PackagePartitionsTest { + + @Test + public void testPackagePartitionsFingerprint() { + final ArrayList partitions = PackagePartitions.getOrderedPartitions( + identity()); + final String[] properties = new String[partitions.size() + 1]; + for (int i = 0; i < partitions.size(); i++) { + final String name = partitions.get(i).getName(); + properties[i] = "ro." + name + ".build.fingerprint"; + } + properties[partitions.size()] = "ro.build.fingerprint"; + + assertThat(SystemProperties.digestOf(properties)).isEqualTo(PackagePartitions.FINGERPRINT); + } +} diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index a075a13bc64ab..ffedb11070fb4 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -67,10 +67,10 @@ import android.content.Intent; import android.content.PermissionChecker; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.PackagePartitions; import android.content.pm.UserInfo; import android.os.BatteryStats; import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Handler; @@ -716,7 +716,7 @@ class UserController implements Handler.Callback { // purposefully block sending BOOT_COMPLETED until after all // PRE_BOOT receivers are finished to avoid ANR'ing apps final UserInfo info = getUserInfo(userId); - if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT) + if (!Objects.equals(info.lastLoggedInFingerprint, PackagePartitions.FINGERPRINT) || SystemProperties.getBoolean("persist.pm.mock-upgrade", false)) { // Suppress double notifications for managed profiles that // were unlocked automatically as part of their parent user diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 5f56d4e100369..efbcf0b9b9a67 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1493,8 +1493,8 @@ public class PackageManagerService extends IPackageManager.Stub } PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest, - Build.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT, - Build.VERSION.INCREMENTAL, SNAPSHOT_ENABLED); + PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, + Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL, SNAPSHOT_ENABLED); t.traceEnd(); // "create package manager" final CompatChange.ChangeListener selinuxChangeListener = packageName -> { @@ -1904,8 +1904,8 @@ public class PackageManagerService extends IPackageManager.Stub mIsUpgrade = !buildFingerprint.equals(ver.fingerprint); if (mIsUpgrade) { - PackageManagerServiceUtils.logCriticalInfo(Log.INFO, - "Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT); + PackageManagerServiceUtils.logCriticalInfo(Log.INFO, "Upgrading from " + + ver.fingerprint + " to " + PackagePartitions.FINGERPRINT); } // when upgrading from pre-M, promote system app permissions from install to runtime @@ -2003,7 +2003,8 @@ public class PackageManagerService extends IPackageManager.Stub // this situation. if (mIsUpgrade) { Slog.i(TAG, "Build fingerprint changed from " + ver.fingerprint + " to " - + Build.FINGERPRINT + "; regranting permissions for internal storage"); + + PackagePartitions.FINGERPRINT + + "; regranting permissions for internal storage"); } mPermissionManager.onStorageVolumeMounted( StorageManager.UUID_PRIVATE_INTERNAL, mIsUpgrade); @@ -2037,7 +2038,7 @@ public class PackageManagerService extends IPackageManager.Stub | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES); } } - ver.fingerprint = Build.FINGERPRINT; + ver.fingerprint = PackagePartitions.FINGERPRINT; } // Legacy existing (installed before Q) non-system apps to hide diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index d2134696e8612..585c9af74894f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -41,6 +41,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; +import android.content.pm.PackagePartitions; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; @@ -1209,7 +1210,7 @@ public class PackageManagerServiceUtils { // identify cached items. In particular, changing the value of certain // feature flags should cause us to invalidate any caches. final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug" - : SystemProperties.digestOf("ro.build.fingerprint"); + : PackagePartitions.FINGERPRINT; // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 4a410473e10a9..1f5d79ca30936 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -42,6 +42,7 @@ import android.content.pm.ComponentInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.content.pm.PackagePartitions; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.Signature; @@ -437,7 +438,7 @@ public final class Settings implements Watchable, Snappable { public void forceCurrent() { sdkVersion = Build.VERSION.SDK_INT; databaseVersion = CURRENT_DATABASE_VERSION; - fingerprint = Build.FINGERPRINT; + fingerprint = PackagePartitions.FINGERPRINT; } } @@ -5357,7 +5358,7 @@ public final class Settings implements Watchable, Snappable { } private String getExtendedFingerprint(long version) { - return Build.FINGERPRINT + "?pc_version=" + version; + return PackagePartitions.FINGERPRINT + "?pc_version=" + version; } public void writeStateForUserAsyncLPr(int userId) { diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index 6b88081380a73..1433abd1b6c93 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -29,10 +29,10 @@ import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import android.app.ResourcesManager; import android.content.IIntentReceiver; import android.content.pm.PackageManager; +import android.content.pm.PackagePartitions; import android.content.pm.UserInfo; import android.content.pm.VersionedPackage; import android.content.pm.parsing.ParsingPackageUtils; -import android.os.Build; import android.os.Environment; import android.os.FileUtils; import android.os.UserHandle; @@ -157,7 +157,7 @@ public final class StorageEventHelper extends StorageEventListener { Slog.w(TAG, "Failed to scan " + ps.getPath() + ": " + e.getMessage()); } - if (!Build.FINGERPRINT.equals(ver.fingerprint)) { + if (!PackagePartitions.FINGERPRINT.equals(ver.fingerprint)) { appDataHelper.clearAppDataLIF( ps.getPkg(), UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY @@ -195,10 +195,10 @@ public final class StorageEventHelper extends StorageEventListener { } synchronized (mPm.mLock) { - final boolean isUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); + final boolean isUpgrade = !PackagePartitions.FINGERPRINT.equals(ver.fingerprint); if (isUpgrade) { logCriticalInfo(Log.INFO, "Build fingerprint changed from " + ver.fingerprint - + " to " + Build.FINGERPRINT + "; regranting permissions for " + + " to " + PackagePartitions.FINGERPRINT + "; regranting permissions for " + volumeUuid); } mPm.mPermissionManager.onStorageVolumeMounted(volumeUuid, isUpgrade); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 302826ff12949..bcfa650f538cb 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -44,6 +44,7 @@ import android.content.IntentSender; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; +import android.content.pm.PackagePartitions; import android.content.pm.ShortcutServiceInternal; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; @@ -3680,7 +3681,7 @@ public class UserManagerService extends IUserManager.Stub { userInfo.creationTime = getCreationTime(); userInfo.partial = true; userInfo.preCreated = preCreate; - userInfo.lastLoggedInFingerprint = Build.FINGERPRINT; + userInfo.lastLoggedInFingerprint = PackagePartitions.FINGERPRINT; if (userTypeDetails.hasBadge() && parentId != UserHandle.USER_NULL) { userInfo.profileBadge = getFreeProfileBadgeLU(parentId, userType); } @@ -4811,7 +4812,8 @@ public class UserManagerService extends IUserManager.Stub { t.traceBegin("onBeforeStartUser-" + userId); final int userSerial = userInfo.serialNumber; // Migrate only if build fingerprints mismatch - boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint); + boolean migrateAppsData = !PackagePartitions.FINGERPRINT.equals( + userInfo.lastLoggedInFingerprint); t.traceBegin("prepareUserData"); mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE); t.traceEnd(); @@ -4841,7 +4843,8 @@ public class UserManagerService extends IUserManager.Stub { } final int userSerial = userInfo.serialNumber; // Migrate only if build fingerprints mismatch - boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint); + boolean migrateAppsData = !PackagePartitions.FINGERPRINT.equals( + userInfo.lastLoggedInFingerprint); final TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("prepareUserData-" + userId); @@ -4885,7 +4888,7 @@ public class UserManagerService extends IUserManager.Stub { if (now > EPOCH_PLUS_30_YEARS) { userData.info.lastLoggedInTime = now; } - userData.info.lastLoggedInFingerprint = Build.FINGERPRINT; + userData.info.lastLoggedInFingerprint = PackagePartitions.FINGERPRINT; scheduleWriteUser(userData); }