[pm] link new splits to old dirs for DONT_KILL installs
For DONT_KILL installs, it's possible that apps use old code paths to
access new splits. This CL links newly added splits back to all the old
code paths.
BUG: 291212866
Test: manual; atest InstallDontKillTest
Change-Id: I6815eaa872fa58e321efccbfb19d42e94795015e
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 92be4ee..05e05a8 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2894,6 +2894,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 117d03f..53e1884 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;
@@ -1831,7 +1832,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();
}
@@ -1900,7 +1901,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);
@@ -4245,7 +4247,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 5225529..9d92caa 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 5724ee0..b565bc7 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;
@@ -2347,7 +2348,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();
}