diff options
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"); |