Merge "[pm] link new splits to old dirs for DONT_KILL installs" into main
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 4f9eab9..f311034 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2900,6 +2900,12 @@
                     // code is loaded by a new Activity before ApplicationInfo changes have
                     // propagated to all application threads.
                     mPm.scheduleDeferredNoKillPostDelete(args);
+                    if (Flags.improveInstallDontKill()) {
+                        synchronized (mPm.mInstallLock) {
+                            PackageManagerServiceUtils.linkSplitsToOldDirs(mPm.mInstaller,
+                                    packageName, pkgSetting.getPath(), pkgSetting.getOldPaths());
+                        }
+                    }
                 } else {
                     mRemovePackageHelper.cleanUpResources(packageName, args.getCodeFile(),
                             args.getInstructionSets());
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1a0e807..27c3dad 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -54,6 +54,7 @@
 import static com.android.internal.util.XmlUtils.writeUriAttribute;
 import static com.android.server.pm.PackageInstallerService.prepareStageDir;
 import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
+import static com.android.server.pm.PackageManagerService.DEFAULT_FILE_ACCESS_MODE;
 import static com.android.server.pm.PackageManagerServiceUtils.isInstalledByAdb;
 import static com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
 
@@ -1832,7 +1833,7 @@
             try {
                 Os.link(path, sourcePath);
                 // Grant READ access for APK to be read successfully
-                Os.chmod(sourcePath, 0644);
+                Os.chmod(sourcePath, DEFAULT_FILE_ACCESS_MODE);
             } catch (ErrnoException e) {
                 e.rethrowAsIOException();
             }
@@ -1901,7 +1902,8 @@
 
             // If file is app metadata then set permission to 0640 to deny user read access since it
             // might contain sensitive information.
-            int mode = name.equals(APP_METADATA_FILE_NAME) ? APP_METADATA_FILE_ACCESS_MODE : 0644;
+            int mode = name.equals(APP_METADATA_FILE_NAME)
+                    ? APP_METADATA_FILE_ACCESS_MODE : DEFAULT_FILE_ACCESS_MODE;
             ParcelFileDescriptor targetPfd = openTargetInternal(target.getAbsolutePath(),
                     O_CREAT | O_WRONLY, mode);
             Os.chmod(target.getAbsolutePath(), mode);
@@ -4246,7 +4248,7 @@
                 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
             }
             try {
-                Os.chmod(tmpFile.getAbsolutePath(), 0644);
+                Os.chmod(tmpFile.getAbsolutePath(), DEFAULT_FILE_ACCESS_MODE);
             } catch (ErrnoException e) {
                 throw new IOException("Failed to chmod " + tmpFile);
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 609b3aa..609a703 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -593,6 +593,8 @@
 
     static final String APP_METADATA_FILE_NAME = "app.metadata";
 
+    static final int DEFAULT_FILE_ACCESS_MODE = 0644;
+
     final Handler mHandler;
     final Handler mBackgroundHandler;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index cd34163..8531692 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -30,6 +30,7 @@
 import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
 import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
 import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED;
+import static com.android.server.pm.PackageManagerService.DEFAULT_FILE_ACCESS_MODE;
 import static com.android.server.pm.PackageManagerService.RANDOM_CODEPATH_PREFIX;
 import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX;
 import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
@@ -69,6 +70,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Process;
+import android.os.SELinux;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.incremental.IncrementalManager;
@@ -129,10 +131,12 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.zip.GZIPInputStream;
@@ -853,7 +857,7 @@
             FileUtils.copy(fileIn, outputStream);
             // Flush anything in buffer before chmod, because any writes after chmod will fail.
             outputStream.flush();
-            Os.fchmod(outputStream.getFD(), 0644);
+            Os.fchmod(outputStream.getFD(), DEFAULT_FILE_ACCESS_MODE);
             atomicFile.finishWrite(outputStream);
             return PackageManager.INSTALL_SUCCEEDED;
         } catch (IOException e) {
@@ -1081,8 +1085,8 @@
 
         final File targetFile = new File(targetDir, targetName);
         final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
-                O_RDWR | O_CREAT, 0644);
-        Os.chmod(targetFile.getAbsolutePath(), 0644);
+                O_RDWR | O_CREAT, DEFAULT_FILE_ACCESS_MODE);
+        Os.chmod(targetFile.getAbsolutePath(), DEFAULT_FILE_ACCESS_MODE);
         FileInputStream source = null;
         try {
             source = new FileInputStream(sourcePath);
@@ -1552,4 +1556,72 @@
     public static boolean isInstalledByAdb(String initiatingPackageName) {
         return initiatingPackageName == null || SHELL_PACKAGE_NAME.equals(initiatingPackageName);
     }
+
+    public static void linkSplitsToOldDirs(@NonNull Installer installer,
+                                           @NonNull String packageName,
+                                           @NonNull File newPath,
+                                           @Nullable Set<File> oldPaths) {
+        if (oldPaths == null || oldPaths.isEmpty()) {
+            return;
+        }
+        if (IncrementalManager.isIncrementalPath(newPath.getPath())) {
+            //TODO(b/291212866): handle incremental installs
+            return;
+        }
+        final File[] filesInNewPath = newPath.listFiles();
+        if (filesInNewPath == null || filesInNewPath.length == 0) {
+            return;
+        }
+        final List<String> splitApkNames = new ArrayList<String>();
+        for (int i = 0; i < filesInNewPath.length; i++) {
+            if (!filesInNewPath[i].isDirectory() && filesInNewPath[i].toString().endsWith(".apk")) {
+                splitApkNames.add(filesInNewPath[i].getName());
+            }
+        }
+        final int numSplits = splitApkNames.size();
+        if (numSplits == 0) {
+            return;
+        }
+        for (File oldPath : oldPaths) {
+            if (!oldPath.exists()) {
+                continue;
+            }
+            for (int i = 0; i < numSplits; i++) {
+                final String splitApkName = splitApkNames.get(i);
+                final File linkedSplit = new File(oldPath, splitApkName);
+                if (linkedSplit.exists()) {
+                    if (DEBUG) {
+                        Slog.d(PackageManagerService.TAG, "Skipping existing linked split <"
+                                + linkedSplit + ">");
+                    }
+                    continue;
+                }
+                final File sourceSplit = new File(newPath, splitApkName);
+                try {
+                    installer.linkFile(packageName, splitApkName,
+                            newPath.getAbsolutePath(), oldPath.getAbsolutePath());
+                    if (DEBUG) {
+                        Slog.d(PackageManagerService.TAG, "Linked <"
+                                + sourceSplit + "> to <" + linkedSplit + ">");
+                    }
+                } catch (Installer.InstallerException e) {
+                    Slog.w(PackageManagerService.TAG, "Failed to link split <"
+                            + sourceSplit + " > to <" + linkedSplit + ">", e);
+                    continue;
+                }
+                try {
+                    Os.chmod(linkedSplit.getAbsolutePath(), DEFAULT_FILE_ACCESS_MODE);
+                } catch (ErrnoException e) {
+                    Slog.w(PackageManagerService.TAG, "Failed to set mode for linked split <"
+                            + linkedSplit + ">", e);
+                    continue;
+                }
+                if (!SELinux.restorecon(linkedSplit)) {
+                    Slog.w(PackageManagerService.TAG, "Failed to restorecon for linked split <"
+                            + linkedSplit + ">");
+                }
+            }
+        }
+        //TODO(b/291212866): support native libs
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index dc1f1ac..5c9c8c6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -28,6 +28,7 @@
 import static android.content.pm.PackageManager.RESTRICTION_NONE;
 
 import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
+import static com.android.server.pm.PackageManagerService.DEFAULT_FILE_ACCESS_MODE;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
 import android.accounts.IAccountManager;
@@ -2349,7 +2350,7 @@
                 Streams.copy(inStream, outStream);
             }
             // Give read permissions to the other group.
-            Os.chmod(outputProfilePath, /*mode*/ 0644 );
+            Os.chmod(outputProfilePath, /*mode*/ DEFAULT_FILE_ACCESS_MODE);
         } catch (IOException | ErrnoException e) {
             pw.println("Error when reading the profile fd: " + e.getMessage());
             e.printStackTrace(pw);
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index c737283..f7603b5 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -27,6 +27,8 @@
 import com.android.server.pm.PackageKeySetData;
 import com.android.server.pm.permission.LegacyPermissionState;
 
+import java.io.File;
+import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -111,4 +113,7 @@
      */
     @Nullable
     String getAppMetadataFilePath();
+
+    @Nullable
+    Set<File> getOldPaths();
 }