summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/PackageManager.java8
-rw-r--r--core/java/android/content/pm/PackageParser.java1
-rw-r--r--core/java/android/os/Environment.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java53
-rwxr-xr-xservices/core/java/com/android/server/pm/Settings.java61
5 files changed, 134 insertions, 0 deletions
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7b924fa..0fa5f51 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -862,6 +862,14 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_THEME_UNKNOWN_ERROR = -402;
/**
+ * Used for prebundles
+ * Installation failed for a prebundled app because the user previously uninstalled it
+ * and we don't want to bring it back
+ * @hide
+ */
+ public static final int INSTALL_FAILED_UNINSTALLED_PREBUNDLE = -403;
+
+ /**
* Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the
* package's data directory.
*
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4f280ca..f4cf1e6 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -645,6 +645,7 @@ public class PackageParser {
public final static int PARSE_IS_PRIVILEGED = 1<<7;
public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
+ public final static int PARSE_IS_PREBUNDLED_DIR = 1<<10;
private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 64d6da5..75bef5c 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -37,6 +37,7 @@ public class Environment {
private static final String ENV_ANDROID_STORAGE = "ANDROID_STORAGE";
private static final String ENV_OEM_ROOT = "OEM_ROOT";
private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
+ private static final String ENV_PREBUNDLED_ROOT = "PREBUNDLED_ROOT";
/** {@hide} */
public static final String DIR_ANDROID = "Android";
@@ -55,6 +56,7 @@ public class Environment {
private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage");
private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
+ private static final File DIR_PREBUNDLED_ROOT = getDirectory(ENV_PREBUNDLED_ROOT, "/vendor/bundled-app");
private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
@@ -165,6 +167,15 @@ public class Environment {
}
/**
+ * Return the root directory for "prebundled" apps. These apps will be installed directly
+ * from this partition but will not be marked as system apps and will hence be uninstallable.
+ * @hide
+ */
+ public static File getPrebundledDirectory() {
+ return DIR_PREBUNDLED_ROOT;
+ }
+
+ /**
* Gets the system directory available for secure storage.
* If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
* Otherwise, it returns the unencrypted /data/system directory.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0ad4dab..e56cd9d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -66,6 +66,7 @@ import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UNINSTALLED_PREBUNDLE;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
@@ -94,6 +95,7 @@ import android.app.AppGlobals;
import android.app.ComposedIconInfo;
import android.app.IActivityManager;
import android.app.IconPackHelper;
+import android.app.PackageInstallObserver;
import android.app.admin.IDevicePolicyManager;
import android.app.backup.IBackupManager;
import android.app.usage.UsageStats;
@@ -2172,6 +2174,10 @@ public class PackageManagerService extends IPackageManager.Stub {
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
+ // Collect all prebundled packages.
+ scanDirLI(Environment.getPrebundledDirectory(),
+ PackageParser.PARSE_IS_PREBUNDLED_DIR, scanFlags, 0);
+
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
mInstaller.moveFiles();
@@ -5731,6 +5737,13 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " flags=0x" + Integer.toHexString(parseFlags));
}
+ boolean isPrebundled = (parseFlags & PackageParser.PARSE_IS_PREBUNDLED_DIR) != 0;
+ if (isPrebundled) {
+ synchronized (mPackages) {
+ mSettings.readPrebundledPackagesLPr();
+ }
+ }
+
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
@@ -5741,6 +5754,17 @@ public class PackageManagerService extends IPackageManager.Stub {
try {
scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
+ if (isPrebundled) {
+ final PackageParser.Package pkg;
+ try {
+ pkg = new PackageParser().parsePackage(file, parseFlags);
+ } catch (PackageParserException e) {
+ throw PackageManagerException.from(e);
+ }
+ synchronized (mPackages) {
+ mSettings.markPrebundledPackageInstalledLPr(pkg.packageName);
+ }
+ }
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
@@ -5756,6 +5780,12 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
}
+
+ if (isPrebundled) {
+ synchronized (mPackages) {
+ mSettings.writePrebundledPackagesLPr();
+ }
+ }
}
private static File getSettingsProblemFile() {
@@ -5850,6 +5880,29 @@ public class PackageManagerService extends IPackageManager.Stub {
throw PackageManagerException.from(e);
}
+ if ((parseFlags & PackageParser.PARSE_IS_PREBUNDLED_DIR) != 0) {
+ synchronized (mPackages) {
+ PackageSetting existingSettings = mSettings.peekPackageLPr(pkg.packageName);
+ if (mSettings.wasPrebundledPackageInstalledLPr(pkg.packageName) &&
+ existingSettings == null) {
+ // The prebundled app was installed at some point in time, but now it is
+ // gone. Assume that the user uninstalled it intentionally: do not reinstall.
+ throw new PackageManagerException(INSTALL_FAILED_UNINSTALLED_PREBUNDLE,
+ "skip reinstall for " + pkg.packageName);
+ } else if (existingSettings != null
+ && existingSettings.versionCode >= pkg.mVersionCode
+ && !existingSettings.codePathString.contains(
+ Environment.getPrebundledDirectory().getPath())) {
+ // This app is installed in a location that is not the prebundled location
+ // and has a higher (or same) version as the prebundled one. Skip
+ // installing the prebundled version.
+ Slog.d(TAG, pkg.packageName + " already installed at " +
+ existingSettings.codePathString);
+ return null; // return null so we still mark package as installed
+ }
+ }
+ }
+
PackageSetting ps = null;
PackageSetting updatedPkg;
// reader
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 484f4c0..0716eb9 100755
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -67,6 +67,7 @@ import com.android.server.pm.PermissionsState.PermissionState;
import java.io.FileNotFoundException;
import java.util.Collection;
+import java.util.HashSet;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -84,6 +85,7 @@ import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.content.pm.PackageUserState;
import android.content.pm.VerifierDeviceIdentity;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -93,9 +95,13 @@ import android.util.SparseIntArray;
import android.util.Xml;
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
@@ -217,6 +223,7 @@ final class Settings {
private final File mPackageListFilename;
private final File mStoppedPackagesFilename;
private final File mBackupStoppedPackagesFilename;
+ private final File mPrebundledPackagesFilename;
final ArrayMap<String, PackageSetting> mPackages =
new ArrayMap<String, PackageSetting>();
@@ -224,6 +231,8 @@ final class Settings {
// List of replaced system applications
private final ArrayMap<String, PackageSetting> mDisabledSysPackages =
new ArrayMap<String, PackageSetting>();
+ private final HashSet<String> mPrebundledPackages =
+ new HashSet<String>();
// Set of restored intent-filter verification states
private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications =
@@ -357,6 +366,7 @@ final class Settings {
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
mPackageListFilename = new File(mSystemDir, "packages.list");
+ mPrebundledPackagesFilename = new File(mSystemDir, "prebundled-packages.list");
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
// Deprecated: Needed for migration
@@ -2340,6 +2350,57 @@ final class Settings {
}
}
+ void writePrebundledPackagesLPr() {
+ PrintWriter writer = null;
+ try {
+ writer = new PrintWriter(
+ new BufferedWriter(new FileWriter(mPrebundledPackagesFilename, false)));
+ for (String packageName : mPrebundledPackages) {
+ writer.println(packageName);
+ }
+ } catch (IOException e) {
+ Slog.e(PackageManagerService.TAG, "Unable to write prebundled package list", e);
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ }
+
+ void readPrebundledPackagesLPr() {
+ if (!mPrebundledPackagesFilename.exists()) {
+ return;
+ }
+
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(mPrebundledPackagesFilename));
+ String packageName = reader.readLine();
+ while (packageName != null) {
+ if (!TextUtils.isEmpty(packageName)) {
+ mPrebundledPackages.add(packageName);
+ }
+ packageName = reader.readLine();
+ }
+ } catch (IOException e) {
+ Slog.e(PackageManagerService.TAG, "Unable to read prebundled package list", e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {}
+ }
+ }
+ }
+
+ void markPrebundledPackageInstalledLPr(String packageName) {
+ mPrebundledPackages.add(packageName);
+ }
+
+ boolean wasPrebundledPackageInstalledLPr(String packageName) {
+ return mPrebundledPackages.contains(packageName);
+ }
+
void writeDisabledSysPackageLPr(XmlSerializer serializer, final PackageSetting pkg)
throws java.io.IOException {
serializer.startTag(null, "updated-package");