Fix 0x10000 resource not found exception

An application "consumer" needs a static library "provider" that
needs another static library "recursive". The recursive static library
will change its code path to another path although Install Process
uses the same apk to install twice.

The "recursive" library directory change its installation
directories. And, the old installation directory has been removed.
The remove operation reveals an error that resources use the
old recursive static library apk path.

Root cause: "consumer" reference "recursive" indirectly. PackageSetting
of "consumer"  doesn't notify the changes when its library files are
updated as a different list.

Solution: It calls PackageSetting.onChanged() when the parameter
usesLibraryFiles is different from
PackageSetting.getUsesLibraryFiles().

Fixes: 201750896

Test: TP="FrameworksServicesTests"; \
    atest ${TP}:com.android.server.pm.PackageManagerSettingsTests
Test: atest \
    CtsOsHostTestCases:android.os.cts.StaticSharedLibsHostTests
Change-Id: Ia8ab7a67ca2d5729d8b4c0f9e53a7deaa760f914
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7f1ac15..df217ec 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5226,7 +5226,8 @@
                 addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib,
                         changingLibSetting);
             }
-            pkgSetting.getPkgState().setUsesLibraryFiles(new ArrayList<>(usesLibraryFiles));
+            pkgSetting.setPkgStateLibraryFiles(usesLibraryFiles);
+
             // let's make sure we mark all static shared libraries as installed for the same users
             // that its dependent packages are installed for.
             int[] installedUsers = new int[allUsers.length];
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index b8e354e..db16a9b 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -443,6 +443,22 @@
         return this;
     }
 
+    /**
+     * Notify {@link #onChanged()}  if the parameter {@code usesLibraryFiles} is different from
+     * {@link #getUsesLibraryFiles()}.
+     * @param usesLibraryFiles the new uses library files
+     * @return {@code this}
+     */
+    public PackageSetting setPkgStateLibraryFiles(@NonNull Collection<String> usesLibraryFiles) {
+        final Collection<String> oldUsesLibraryFiles = getUsesLibraryFiles();
+        if (oldUsesLibraryFiles.size() != usesLibraryFiles.size()
+                || !oldUsesLibraryFiles.containsAll(usesLibraryFiles)) {
+            pkgState.setUsesLibraryFiles(new ArrayList<>(usesLibraryFiles));
+            onChanged();
+        }
+        return this;
+    }
+
     public PackageSetting setPrimaryCpuAbi(String primaryCpuAbiString) {
         this.mPrimaryCpuAbi = primaryCpuAbiString;
         onChanged();
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 24377a9..cc1a141 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -29,9 +29,9 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.when;
@@ -63,8 +63,10 @@
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.permission.LegacyPermissionDataProvider;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.utils.Watchable;
 import com.android.server.utils.WatchableTester;
 import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.Watcher;
 
 import com.google.common.truth.Truth;
 
@@ -83,6 +85,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -874,6 +877,95 @@
                 false /*notLaunched*/, false /*stopped*/, true /*installed*/);
     }
 
+    @Test
+    public void testSetPkgStateLibraryFiles_addNewFiles() {
+        final PackageSetting packageSetting = createPackageSetting("com.foo");
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        packageSetting.registerObserver(new Watcher() {
+            @Override
+            public void onChange(Watchable what) {
+                countDownLatch.countDown();
+            }
+        });
+
+        final List<String> newUsesLibraryFiles = new ArrayList<>();
+        newUsesLibraryFiles.add("code/path/A.apk");
+        newUsesLibraryFiles.add("code/path/B.apk");
+        packageSetting.setPkgStateLibraryFiles(newUsesLibraryFiles);
+
+        assertThat(countDownLatch.getCount(), is(0L));
+    }
+
+    @Test
+    public void testSetPkgStateLibraryFiles_removeOneExistingFile() {
+        final PackageSetting packageSetting = createPackageSetting("com.foo");
+        final List<String> oldUsesLibraryFiles = new ArrayList<>();
+        oldUsesLibraryFiles.add("code/path/A.apk");
+        oldUsesLibraryFiles.add("code/path/B.apk");
+        oldUsesLibraryFiles.add("code/path/C.apk");
+        packageSetting.setPkgStateLibraryFiles(oldUsesLibraryFiles);
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        packageSetting.registerObserver(new Watcher() {
+            @Override
+            public void onChange(Watchable what) {
+                countDownLatch.countDown();
+            }
+        });
+
+        final List<String> newUsesLibraryFiles = new ArrayList<>();
+        oldUsesLibraryFiles.add("code/path/A.apk");
+        oldUsesLibraryFiles.add("code/path/B.apk");
+        packageSetting.setPkgStateLibraryFiles(newUsesLibraryFiles);
+
+        assertThat(countDownLatch.getCount(), is(0L));
+    }
+
+    @Test
+    public void testSetPkgStateLibraryFiles_changeOneOfFile() {
+        final PackageSetting packageSetting = createPackageSetting("com.foo");
+        final List<String> oldUsesLibraryFiles = new ArrayList<>();
+        oldUsesLibraryFiles.add("code/path/A.apk");
+        oldUsesLibraryFiles.add("code/path/B.apk");
+        packageSetting.setPkgStateLibraryFiles(oldUsesLibraryFiles);
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        packageSetting.registerObserver(new Watcher() {
+            @Override
+            public void onChange(Watchable what) {
+                countDownLatch.countDown();
+            }
+        });
+
+        final List<String> newUsesLibraryFiles = new ArrayList<>();
+        newUsesLibraryFiles.add("code/path/A.apk");
+        newUsesLibraryFiles.add("code/path/B-1.apk");
+        packageSetting.setPkgStateLibraryFiles(newUsesLibraryFiles);
+
+        assertThat(countDownLatch.getCount(), is(0L));
+    }
+
+    @Test
+    public void testSetPkgStateLibraryFiles_nothingChanged() {
+        final PackageSetting packageSetting = createPackageSetting("com.foo");
+        final List<String> oldUsesLibraryFiles = new ArrayList<>();
+        oldUsesLibraryFiles.add("code/path/A.apk");
+        oldUsesLibraryFiles.add("code/path/B.apk");
+        packageSetting.setPkgStateLibraryFiles(oldUsesLibraryFiles);
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        packageSetting.registerObserver(new Watcher() {
+            @Override
+            public void onChange(Watchable what) {
+                countDownLatch.countDown();
+            }
+        });
+
+        final List<String> newUsesLibraryFiles = new ArrayList<>();
+        newUsesLibraryFiles.add("code/path/A.apk");
+        newUsesLibraryFiles.add("code/path/B.apk");
+        packageSetting.setPkgStateLibraryFiles(newUsesLibraryFiles);
+
+        assertThat(countDownLatch.getCount(), is(1L));
+    }
+
     private <T> void assertArrayEquals(T[] a, T[] b) {
         assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
                 Arrays.equals(a, b));