OSDN Git Service

Preserve order of shared library files
authorPaul Duffin <paulduffin@google.com>
Mon, 2 Oct 2017 09:23:25 +0000 (10:23 +0100)
committerPaul Duffin <paulduffin@google.com>
Thu, 5 Oct 2017 09:03:04 +0000 (10:03 +0100)
Shared libraries are stored in a list so order is preserved. However,
when they are resolved to files, e.g. for use as a class path the
file names are added to an ArraySet which loses the order. Presumably
they are added to a Set to eliminate duplicates. This switches to a
LinkedHashSet which will preserve the order in which the files are
added while still avoiding duplicates.

It is possible that this could cause app compatibility issues as the
order in which shared libraries is being added is changing. Problems can
only arise if two libraries whose order changes have duplicate classes
and/or resources. In that case the app was only working by luck, as the
order provided by ArraySet is based on the numerical order of hash
codes.

This was found while investigating performance regressions in
GoogleDialer, unfortunately it does not fix the regressions.

Bug: 65552462
Test: flash -w and systrace GoogleDialer to ensure correct order
Change-Id: Ia01ce4821fa53e4785716b72c4f87a0b0ab4dcc8

services/core/java/com/android/server/pm/PackageManagerService.java

index d05c95f..e7b7d1f 100644 (file)
@@ -338,6 +338,7 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -10255,7 +10256,8 @@ public class PackageManagerService extends IPackageManager.Stub
         }
     }
 
-    private void addSharedLibraryLPr(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
+    private void addSharedLibraryLPr(Set<String> usesLibraryFiles,
+            SharedLibraryEntry file,
             PackageParser.Package changingLib) {
         if (file.path != null) {
             usesLibraryFiles.add(file.path);
@@ -10284,7 +10286,10 @@ public class PackageManagerService extends IPackageManager.Stub
         if (pkg == null) {
             return;
         }
-        ArraySet<String> usesLibraryFiles = null;
+        // The collection used here must maintain the order of addition (so
+        // that libraries are searched in the correct order) and must have no
+        // duplicates.
+        Set<String> usesLibraryFiles = null;
         if (pkg.usesLibraries != null) {
             usesLibraryFiles = addSharedLibrariesLPw(pkg.usesLibraries,
                     null, null, pkg.packageName, changingLib, true,
@@ -10308,10 +10313,10 @@ public class PackageManagerService extends IPackageManager.Stub
         }
     }
 
-    private ArraySet<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
+    private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
             @Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests,
             @NonNull String packageName, @Nullable PackageParser.Package changingLib,
-            boolean required, int targetSdk, @Nullable ArraySet<String> outUsedLibraries)
+            boolean required, int targetSdk, @Nullable Set<String> outUsedLibraries)
             throws PackageManagerException {
         final int libCount = requestedLibraries.size();
         for (int i = 0; i < libCount; i++) {
@@ -10377,7 +10382,9 @@ public class PackageManagerService extends IPackageManager.Stub
                 }
 
                 if (outUsedLibraries == null) {
-                    outUsedLibraries = new ArraySet<>();
+                    // Use LinkedHashSet to preserve the order of files added to
+                    // usesLibraryFiles while eliminating duplicates.
+                    outUsedLibraries = new LinkedHashSet<>();
                 }
                 addSharedLibraryLPr(outUsedLibraries, libEntry, changingLib);
             }