Merge "Remove icon from base alert dialog, use light theme"
diff --git a/Android.mk b/Android.mk
index a7eb03d..5d957d0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -210,6 +210,7 @@
core/java/android/service/dreams/IDozeHardware.aidl \
core/java/android/service/dreams/IDreamManager.aidl \
core/java/android/service/dreams/IDreamService.aidl \
+ core/java/android/service/persistentdata/IPersistentDataBlockService.aidl \
core/java/android/service/fingerprint/IFingerprintService.aidl \
core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl \
core/java/android/service/trust/ITrustAgentService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 453f33d..29ec56b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35802,6 +35802,7 @@
method public deprecated void onGlobalFocusChanged(android.view.View, android.view.View);
method public void onPause();
method public void onResume();
+ method public static void optOutDataReductionProxy();
method public boolean overlayHorizontalScrollbar();
method public boolean overlayVerticalScrollbar();
method public boolean pageDown(boolean);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index a435fba..92e9290 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -19,15 +19,18 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
+import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver2;
+import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstallerParams;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
@@ -46,12 +49,21 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.SizedInputStream;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@@ -61,7 +73,10 @@
import java.util.WeakHashMap;
public final class Pm {
+ private static final String TAG = "Pm";
+
IPackageManager mPm;
+ IPackageInstaller mInstaller;
IUserManager mUm;
private WeakHashMap<String, Resources> mResourceCache
@@ -75,10 +90,18 @@
"Error: Could not access the Package Manager. Is the system running?";
public static void main(String[] args) {
- new Pm().run(args);
+ try {
+ new Pm().run(args);
+ } catch (Exception e) {
+ Log.e(TAG, "Error", e);
+ System.err.println("Error: " + e);
+ if (e instanceof RemoteException) {
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
}
- public void run(String[] args) {
+ public void run(String[] args) throws IOException, RemoteException {
boolean validCommand = false;
if (args.length < 1) {
showUsage();
@@ -91,6 +114,7 @@
System.err.println(PM_NOT_RUNNING_ERR);
return;
}
+ mInstaller = mPm.getPackageInstaller();
mArgs = args;
String op = args[0];
@@ -116,6 +140,31 @@
return;
}
+ if ("install-create".equals(op)) {
+ runInstallCreate();
+ return;
+ }
+
+ if ("install-write".equals(op)) {
+ runInstallWrite();
+ return;
+ }
+
+ if ("install-commit".equals(op)) {
+ runInstallCommit();
+ return;
+ }
+
+ if ("install-destroy".equals(op)) {
+ runInstallDestroy();
+ return;
+ }
+
+ if ("set-installer".equals(op)) {
+ runSetInstaller();
+ return;
+ }
+
if ("uninstall".equals(op)) {
runUninstall();
return;
@@ -697,7 +746,7 @@
ActivityManager.dumpPackageStateStatic(FileDescriptor.out, pkg);
}
- class PackageInstallObserver extends IPackageInstallObserver2.Stub {
+ class LocalPackageInstallObserver extends PackageInstallObserver {
boolean finished;
int result;
String extraPermission;
@@ -705,7 +754,7 @@
@Override
public void packageInstalled(String name, Bundle extras, int status) {
- synchronized( this) {
+ synchronized (this) {
finished = true;
result = status;
if (status == PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION) {
@@ -723,7 +772,7 @@
* Converts a failure code into a string by using reflection to find a matching constant
* in PackageManager.
*/
- private String installFailureToString(PackageInstallObserver obs) {
+ private String installFailureToString(LocalPackageInstallObserver obs) {
final int result = obs.result;
Field[] fields = PackageManager.class.getFields();
for (Field f: fields) {
@@ -908,12 +957,12 @@
verificationURI = null;
}
- PackageInstallObserver obs = new PackageInstallObserver();
+ LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
try {
VerificationParams verificationParams = new VerificationParams(verificationURI,
originatingURI, referrerURI, VerificationParams.NO_UID, null);
- mPm.installPackage(apkFilePath, obs, installFlags, installerPackageName,
+ mPm.installPackage(apkFilePath, obs.getBinder(), installFlags, installerPackageName,
verificationParams, abi);
synchronized (obs) {
@@ -937,35 +986,149 @@
}
}
- /**
- * Convert a string containing hex-encoded bytes to a byte array.
- *
- * @param input String containing hex-encoded bytes
- * @return input as an array of bytes
- */
- private byte[] hexToBytes(String input) {
- if (input == null) {
- return null;
+ private void runInstallCreate() throws RemoteException {
+ String installerPackageName = null;
+
+ final PackageInstallerParams params = new PackageInstallerParams();
+ params.installFlags = PackageManager.INSTALL_ALL_USERS;
+ params.fullInstall = true;
+
+ String opt;
+ while ((opt = nextOption()) != null) {
+ if (opt.equals("-l")) {
+ params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+ } else if (opt.equals("-r")) {
+ params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ } else if (opt.equals("-i")) {
+ installerPackageName = nextArg();
+ if (installerPackageName == null) {
+ throw new IllegalArgumentException("Missing installer package");
+ }
+ } else if (opt.equals("-t")) {
+ params.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+ } else if (opt.equals("-s")) {
+ params.installFlags |= PackageManager.INSTALL_EXTERNAL;
+ } else if (opt.equals("-f")) {
+ params.installFlags |= PackageManager.INSTALL_INTERNAL;
+ } else if (opt.equals("-d")) {
+ params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ } else if (opt.equals("-p")) {
+ params.fullInstall = false;
+ } else if (opt.equals("-S")) {
+ params.deltaSize = Long.parseLong(nextOptionData());
+ } else {
+ throw new IllegalArgumentException("Unknown option " + opt);
+ }
}
- final int inputLength = input.length();
- if ((inputLength % 2) != 0) {
- System.err.print("Invalid length; must be multiple of 2");
- return null;
+ final int sessionId = mInstaller.createSession(installerPackageName, params,
+ UserHandle.USER_OWNER);
+
+ // NOTE: adb depends on parsing this string
+ System.out.println("Success: created install session [" + sessionId + "]");
+ }
+
+ private void runInstallWrite() throws IOException, RemoteException {
+ long sizeBytes = -1;
+
+ String opt;
+ while ((opt = nextOption()) != null) {
+ if (opt.equals("-S")) {
+ sizeBytes = Long.parseLong(nextOptionData());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
}
- final int byteLength = inputLength / 2;
- final byte[] output = new byte[byteLength];
+ final int sessionId = Integer.parseInt(nextArg());
+ final String splitName = nextArg();
- int inputIndex = 0;
- int byteIndex = 0;
- while (inputIndex < inputLength) {
- output[byteIndex++] = (byte) Integer.parseInt(
- input.substring(inputIndex, inputIndex + 2), 16);
- inputIndex += 2;
+ String path = nextArg();
+ if ("-".equals(path)) {
+ path = null;
+ } else if (path != null) {
+ final File file = new File(path);
+ if (file.isFile()) {
+ sizeBytes = file.length();
+ }
}
- return output;
+ PackageInstaller.Session session = null;
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+
+ if (path != null) {
+ in = new FileInputStream(path);
+ } else {
+ in = new SizedInputStream(System.in, sizeBytes);
+ }
+ out = session.openWrite(splitName, 0, sizeBytes);
+
+ final int n = Streams.copy(in, out);
+ out.flush();
+
+ System.out.println("Success: streamed " + n + " bytes");
+ } finally {
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private void runInstallCommit() throws RemoteException {
+ final int sessionId = Integer.parseInt(nextArg());
+
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+
+ final LocalPackageInstallObserver observer = new LocalPackageInstallObserver();
+ session.install(observer);
+
+ synchronized (observer) {
+ while (!observer.finished) {
+ try {
+ observer.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ if (observer.result != PackageManager.INSTALL_SUCCEEDED) {
+ throw new IllegalStateException(
+ "Failure [" + installFailureToString(observer) + "]");
+ }
+ }
+ System.out.println("Success");
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private void runInstallDestroy() throws RemoteException {
+ final int sessionId = Integer.parseInt(nextArg());
+
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+ session.destroy();
+ System.out.println("Success");
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private void runSetInstaller() throws RemoteException {
+ final String targetPackage = nextArg();
+ final String installerPackageName = nextArg();
+
+ if (targetPackage == null || installerPackageName == null) {
+ throw new IllegalArgumentException(
+ "must provide both target and installer package names");
+ }
+
+ mPm.setInstallerPackageName(targetPackage, installerPackageName);
+ System.out.println("Success");
}
public void runCreateUser() {
@@ -1146,10 +1309,10 @@
}
}
- private boolean deletePackage(String pkg, int unInstallFlags, int userId) {
+ private boolean deletePackage(String packageName, int flags, int userId) {
PackageDeleteObserver obs = new PackageDeleteObserver();
try {
- mPm.deletePackageAsUser(pkg, obs, userId, unInstallFlags);
+ mInstaller.uninstall(packageName, flags, obs, userId);
synchronized (obs) {
while (!obs.finished) {
@@ -1548,10 +1711,13 @@
System.err.println(" pm list users");
System.err.println(" pm path PACKAGE");
System.err.println(" pm dump PACKAGE");
- System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]");
- System.err.println(" [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>]");
- System.err.println(" [--originating-uri <URI>] [--referrer <URI>] PATH");
+ System.err.println(" pm install [-lrtsfd] [-i PACKAGE] [PATH]");
+ System.err.println(" pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]");
+ System.err.println(" pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]");
+ System.err.println(" pm install-commit SESSION_ID");
+ System.err.println(" pm install-destroy SESSION_ID");
System.err.println(" pm uninstall [-k] [--user USER_ID] PACKAGE");
+ System.err.println(" pm set-installer PACKAGE INSTALLER");
System.err.println(" pm clear [--user USER_ID] PACKAGE");
System.err.println(" pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
@@ -1600,16 +1766,28 @@
System.err.println("");
System.err.println("pm path: print the path to the .apk of the given PACKAGE.");
System.err.println("");
- System.err.println("pm dump: print system state associated w ith the given PACKAGE.");
+ System.err.println("pm dump: print system state associated with the given PACKAGE.");
System.err.println("");
- System.err.println("pm install: installs a package to the system. Options:");
- System.err.println(" -l: install the package with FORWARD_LOCK.");
- System.err.println(" -r: reinstall an exisiting app, keeping its data.");
- System.err.println(" -t: allow test .apks to be installed.");
- System.err.println(" -i: specify the installer package name.");
- System.err.println(" -s: install package on sdcard.");
- System.err.println(" -f: install package on internal flash.");
- System.err.println(" -d: allow version code downgrade.");
+ System.err.println("pm install: install a single legacy package");
+ System.err.println("pm install-create: create an install session");
+ System.err.println(" -l: forward lock application");
+ System.err.println(" -r: replace existing application");
+ System.err.println(" -t: allow test packages");
+ System.err.println(" -i: specify the installer package name");
+ System.err.println(" -s: install application on sdcard");
+ System.err.println(" -f: install application on internal flash");
+ System.err.println(" -d: allow version code downgrade");
+ System.err.println(" -p: partial application install");
+ System.err.println(" -S: size in bytes of entire session");
+ System.err.println("");
+ System.err.println("pm install-write: write a package into existing session; path may");
+ System.err.println(" be '-' to read from stdin");
+ System.err.println(" -S: size in bytes of package, required for stdin");
+ System.err.println("");
+ System.err.println("pm install-commit: perform install of fully staged session");
+ System.err.println("pm install-destroy: destroy session");
+ System.err.println("");
+ System.err.println("pm set-installer: set installer package name");
System.err.println("");
System.err.println("pm uninstall: removes a package from the system. Options:");
System.err.println(" -k: keep the data and cache directories around after package removal.");
@@ -1643,5 +1821,6 @@
System.err.println("");
System.err.println("pm remove-user: remove the user with the given USER_IDENTIFIER,");
System.err.println(" deleting all data associated with that user");
+ System.err.println("");
}
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 97f1e50..2935b8e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1463,8 +1463,8 @@
@Override
public PackageInstaller getPackageInstaller() {
try {
- return new PackageInstaller(this, mPM.getPackageInstaller(), mContext.getUserId(),
- mContext.getPackageName());
+ return new PackageInstaller(this, mPM.getPackageInstaller(), mContext.getPackageName(),
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fa2266b..cacb2df 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -20,6 +20,8 @@
import android.net.wifi.WifiScanner;
import android.os.Build;
+import android.service.persistentdata.IPersistentDataBlockService;
+import android.service.persistentdata.PersistentDataBlockManager;
import com.android.internal.policy.PolicyManager;
import com.android.internal.util.Preconditions;
@@ -733,7 +735,14 @@
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(JOB_SCHEDULER_SERVICE);
return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b));
- }});
+ }});
+
+ registerService(PERSISTENT_DATA_BLOCK_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(PERSISTENT_DATA_BLOCK_SERVICE);
+ return new PersistentDataBlockManager(
+ IPersistentDataBlockService.Stub.asInterface(b));
+ }});
}
static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 80b6b58..04275d8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2801,6 +2801,16 @@
public static final String JOB_SCHEDULER_SERVICE = "jobscheduler";
/**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.service.persistentdata.PersistentDataBlockManager} instance retrieving
+ * a file descriptor for a persistent data block.
+ * @see #getSystemService
+ * @see android.service.persistentdata.PersistentDataBlockManager
+ * @hide
+ */
+ public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 68c019b..4d6ee64 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -23,11 +23,11 @@
/** {@hide} */
interface IPackageInstaller {
- int createSession(int userId, String installerPackageName, in PackageInstallerParams params);
+ int createSession(String installerPackageName, in PackageInstallerParams params, int userId);
IPackageInstallerSession openSession(int sessionId);
- int[] getSessions(int userId, String installerPackageName);
+ int[] getSessions(String installerPackageName, int userId);
- void uninstall(int userId, String basePackageName, in IPackageDeleteObserver observer);
- void uninstallSplit(int userId, String basePackageName, String splitName, in IPackageDeleteObserver observer);
+ void uninstall(String basePackageName, int flags, in IPackageDeleteObserver observer, int userId);
+ void uninstallSplit(String basePackageName, String splitName, int flags, in IPackageDeleteObserver observer, int userId);
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 4672015..a60a2fe 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -23,6 +23,7 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import java.io.Closeable;
import java.io.OutputStream;
/** {@hide} */
@@ -33,12 +34,12 @@
private final String mInstallerPackageName;
/** {@hide} */
- public PackageInstaller(PackageManager pm, IPackageInstaller installer, int userId,
- String installerPackageName) {
+ public PackageInstaller(PackageManager pm, IPackageInstaller installer,
+ String installerPackageName, int userId) {
mPm = pm;
mInstaller = installer;
- mUserId = userId;
mInstallerPackageName = installerPackageName;
+ mUserId = userId;
}
public boolean isPackageAvailable(String basePackageName) {
@@ -63,7 +64,7 @@
public int createSession(PackageInstallerParams params) {
try {
- return mInstaller.createSession(mUserId, mInstallerPackageName, params);
+ return mInstaller.createSession(mInstallerPackageName, params, mUserId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -79,7 +80,7 @@
public int[] getSessions() {
try {
- return mInstaller.getSessions(mUserId, mInstallerPackageName);
+ return mInstaller.getSessions(mInstallerPackageName, mUserId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -87,7 +88,7 @@
public void uninstall(String basePackageName, PackageUninstallObserver observer) {
try {
- mInstaller.uninstall(mUserId, basePackageName, observer.getBinder());
+ mInstaller.uninstall(basePackageName, 0, observer.getBinder(), mUserId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -96,7 +97,7 @@
public void uninstall(String basePackageName, String splitName,
PackageUninstallObserver observer) {
try {
- mInstaller.uninstallSplit(mUserId, basePackageName, splitName, observer.getBinder());
+ mInstaller.uninstallSplit(basePackageName, splitName, 0, observer.getBinder(), mUserId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -114,7 +115,7 @@
* installation (for example, the same split name), the package in this
* session will replace the existing package.
*/
- public class Session {
+ public static class Session implements Closeable {
private IPackageInstallerSession mSession;
/** {@hide} */
@@ -154,6 +155,7 @@
}
}
+ @Override
public void close() {
// No resources to release at the moment
}
diff --git a/core/java/android/content/pm/PackageInstallerParams.java b/core/java/android/content/pm/PackageInstallerParams.java
index 67cf276..527edee 100644
--- a/core/java/android/content/pm/PackageInstallerParams.java
+++ b/core/java/android/content/pm/PackageInstallerParams.java
@@ -41,6 +41,8 @@
/** {@hide} */
public long deltaSize = -1;
/** {@hide} */
+ public String basePackageName;
+ /** {@hide} */
public Bitmap icon;
/** {@hide} */
public String title;
@@ -48,23 +50,27 @@
public Uri originatingUri;
/** {@hide} */
public Uri referrerUri;
+ /** {@hide} */
+ public String abiOverride;
public PackageInstallerParams() {
}
/** {@hide} */
public PackageInstallerParams(Parcel source) {
- this.fullInstall = source.readInt() != 0;
- this.installFlags = source.readInt();
- this.installLocation = source.readInt();
- this.signatures = (Signature[]) source.readParcelableArray(null);
+ fullInstall = source.readInt() != 0;
+ installFlags = source.readInt();
+ installLocation = source.readInt();
+ signatures = (Signature[]) source.readParcelableArray(null);
deltaSize = source.readLong();
+ basePackageName = source.readString();
if (source.readInt() != 0) {
icon = Bitmap.CREATOR.createFromParcel(source);
}
title = source.readString();
- originatingUri = Uri.CREATOR.createFromParcel(source);
- referrerUri = Uri.CREATOR.createFromParcel(source);
+ originatingUri = source.readParcelable(null);
+ referrerUri = source.readParcelable(null);
+ abiOverride = source.readString();
}
public void setFullInstall(boolean fullInstall) {
@@ -87,6 +93,10 @@
this.deltaSize = deltaSize;
}
+ public void setBasePackageName(String basePackageName) {
+ this.basePackageName = basePackageName;
+ }
+
public void setIcon(Bitmap icon) {
this.icon = icon;
}
@@ -115,6 +125,7 @@
dest.writeInt(installLocation);
dest.writeParcelableArray(signatures, flags);
dest.writeLong(deltaSize);
+ dest.writeString(basePackageName);
if (icon != null) {
dest.writeInt(1);
icon.writeToParcel(dest, flags);
@@ -124,6 +135,7 @@
dest.writeString(title);
dest.writeParcelable(originatingUri, flags);
dest.writeParcelable(referrerUri, flags);
+ dest.writeString(abiOverride);
}
public static final Parcelable.Creator<PackageInstallerParams>
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3b118c0..ab0bf25 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -71,7 +71,6 @@
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -923,19 +922,17 @@
"Package " + apkPath + " has no certificates at entry "
+ entry.getName());
}
+ final Signature[] entrySignatures = convertToSignatures(entryCerts);
if (pkg.mCertificates == null) {
pkg.mCertificates = entryCerts;
- pkg.mSignatures = convertToSignatures(entryCerts);
+ pkg.mSignatures = entrySignatures;
pkg.mSigningKeys = new ArraySet<PublicKey>();
for (int i=0; i < entryCerts.length; i++) {
pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
}
} else {
- final boolean certsMatch = (pkg.mCertificates.length == entryCerts.length)
- && ArrayUtils.containsAll(pkg.mCertificates, entryCerts)
- && ArrayUtils.containsAll(entryCerts, pkg.mCertificates);
- if (!certsMatch) {
+ if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath
+ " has mismatched certificates at entry "
@@ -1097,7 +1094,7 @@
if (splitName.length() == 0) {
splitName = null;
} else {
- final String error = validateName(splitName, true);
+ final String error = validateName(splitName, false);
if (error != null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Invalid manifest split: " + error);
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index 96aa083..7edf4b9 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -249,6 +249,7 @@
* @hide
*/
public static boolean areExactMatch(Signature[] a, Signature[] b) {
- return ArrayUtils.containsAll(a, b) && ArrayUtils.containsAll(b, a);
+ return (a.length == b.length) && ArrayUtils.containsAll(a, b)
+ && ArrayUtils.containsAll(b, a);
}
}
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
index 7f8bc9f..691afdd 100644
--- a/core/java/android/os/FileBridge.java
+++ b/core/java/android/os/FileBridge.java
@@ -54,6 +54,8 @@
private static final int CMD_WRITE = 1;
/** CMD_FSYNC */
private static final int CMD_FSYNC = 2;
+ /** CMD_CLOSE */
+ private static final int CMD_CLOSE = 3;
private FileDescriptor mTarget;
@@ -102,12 +104,17 @@
// Sync and echo back to confirm
Os.fsync(mTarget);
IoBridge.write(mServer, temp, 0, MSG_LENGTH);
+
+ } else if (cmd == CMD_CLOSE) {
+ // Close and echo back to confirm
+ Os.fsync(mTarget);
+ Os.close(mTarget);
+ mClosed = true;
+ IoBridge.write(mServer, temp, 0, MSG_LENGTH);
+ break;
}
}
- // Client was closed; one last fsync
- Os.fsync(mTarget);
-
} catch (ErrnoException e) {
Log.e(TAG, "Failed during bridge: ", e);
} catch (IOException e) {
@@ -130,22 +137,30 @@
@Override
public void close() throws IOException {
- IoBridge.closeAndSignalBlockedThreads(mClient);
+ try {
+ writeCommandAndBlock(CMD_CLOSE, "close()");
+ } finally {
+ IoBridge.closeAndSignalBlockedThreads(mClient);
+ }
}
@Override
public void flush() throws IOException {
- Memory.pokeInt(mTemp, 0, CMD_FSYNC, ByteOrder.BIG_ENDIAN);
+ writeCommandAndBlock(CMD_FSYNC, "fsync()");
+ }
+
+ private void writeCommandAndBlock(int cmd, String cmdString) throws IOException {
+ Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN);
IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
// Wait for server to ack
if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
- if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == CMD_FSYNC) {
+ if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) {
return;
}
}
- throw new SyncFailedException("Failed to fsync() across bridge");
+ throw new IOException("Failed to execute " + cmdString + " across bridge");
}
@Override
diff --git a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
new file mode 100644
index 0000000..06e776d
--- /dev/null
+++ b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
@@ -0,0 +1,20 @@
+package android.service.persistentdata;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Internal interface through which to communicate to the
+ * PersistentDataBlockService. The persistent data block allows writing
+ * raw data and setting the OEM unlock enabled/disabled bit contained
+ * in the partition.
+ *
+ * @hide
+ */
+interface IPersistentDataBlockService {
+ int write(in byte[] data);
+ int read(out byte[] data);
+ int getDataBlockSize();
+
+ void setOemUnlockEnabled(boolean enabled);
+ boolean getOemUnlockEnabled();
+}
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
new file mode 100644
index 0000000..afcf717
--- /dev/null
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -0,0 +1,97 @@
+package android.service.persistentdata;
+
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Interface for reading and writing data blocks to a persistent partition.
+ *
+ * Allows writing one block at a time. Namely, each time
+ * {@link android.service.persistentdata.PersistentDataBlockManager}.write(byte[] data)
+ * is called, it will overwite the data that was previously written on the block.
+ *
+ * Clients can query the size of the currently written block via
+ * {@link android.service.persistentdata.PersistentDataBlockManager}.getTotalDataSize().
+ *
+ * Clients can any number of bytes from the currently written block up to its total size by invoking
+ * {@link android.service.persistentdata.PersistentDataBlockManager}.read(byte[] data).
+ *
+ * @hide
+ */
+public class PersistentDataBlockManager {
+ private static final String TAG = PersistentDataBlockManager.class.getSimpleName();
+ private IPersistentDataBlockService sService;
+
+ public PersistentDataBlockManager(IPersistentDataBlockService service) {
+ sService = service;
+ }
+
+ /**
+ * Writes {@code data} to the persistent partition. Previously written data
+ * will be overwritten. This data will persist across factory resets.
+ *
+ * @param data the data to write
+ */
+ public void write(byte[] data) {
+ try {
+ sService.write(data);
+ } catch (RemoteException e) {
+ onError("writing data");
+ }
+ }
+
+ /**
+ * Tries to read {@code data.length} bytes into {@code data}. Call {@code getDataBlockSize()}
+ * to determine the total size of the block currently residing in the persistent partition.
+ *
+ * @param data the buffer in which to read the data
+ * @return the actual number of bytes read
+ */
+ public int read(byte[] data) {
+ try {
+ return sService.read(data);
+ } catch (RemoteException e) {
+ onError("reading data");
+ return -1;
+ }
+ }
+
+ /**
+ * Retrieves the size of the block currently written to the persistent partition.
+ */
+ public int getDataBlockSize() {
+ try {
+ return sService.getDataBlockSize();
+ } catch (RemoteException e) {
+ onError("getting data block size");
+ return 0;
+ }
+ }
+
+ /**
+ * Writes a byte enabling or disabling the ability to "OEM unlock" the device.
+ */
+ public void setOemUnlockEnabled(boolean enabled) {
+ try {
+ sService.setOemUnlockEnabled(enabled);
+ } catch (RemoteException e) {
+ onError("setting OEM unlock enabled to " + enabled);
+ }
+ }
+
+ /**
+ * Returns whether or not "OEM unlock" is enabled or disabled on this device.
+ */
+ public boolean getOemUnlockEnabled() {
+ try {
+ return sService.getOemUnlockEnabled();
+ } catch (RemoteException e) {
+ onError("getting OEM unlock enabled bit");
+ return false;
+ }
+ }
+
+ private void onError(String msg) {
+ Slog.v(TAG, "Remote exception while " + msg);
+ }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 482c7e8..290a574 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1805,6 +1805,21 @@
}
/**
+ * Sets whether the application wants to opt out from using the Data Reduction Proxy
+ * service.
+ * Data reduction proxy can only be enabled by the user and will almost always be
+ * transparent to the application. In rare cases where using the proxy interferes
+ * with the app, the application developer can use this API to opt out from using the
+ * proxy. Note that this may increase network bandwidth usage.
+ *
+ * See <a href=http://developer.chrome.com/multidevice/data-compression>
+ * Data Compression Proxy</a>
+ */
+ public static void optOutDataReductionProxy() {
+ getFactory().getStatics().optOutDataReductionProxy();
+ }
+
+ /**
* Gets the list of currently loaded plugins.
*
* @return the list of currently loaded plugins
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 6e6a987..5ff2655 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -59,6 +59,11 @@
*/
void clearClientCertPreferences(Runnable onCleared);
+ /**
+ * Implements the API method:
+ * {@link android.webkit.WebView#optOutDataReductionProxy() }
+ */
+ void optOutDataReductionProxy();
}
Statics getStatics();
diff --git a/core/java/com/android/internal/util/SizedInputStream.java b/core/java/com/android/internal/util/SizedInputStream.java
new file mode 100644
index 0000000..00a729d
--- /dev/null
+++ b/core/java/com/android/internal/util/SizedInputStream.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import libcore.io.Streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Reads exact number of bytes from wrapped stream, returning EOF once those
+ * bytes have been read.
+ */
+public class SizedInputStream extends InputStream {
+ private final InputStream mWrapped;
+ private long mLength;
+
+ public SizedInputStream(InputStream wrapped, long length) {
+ mWrapped = wrapped;
+ mLength = length;
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ mWrapped.close();
+ }
+
+ @Override
+ public int read() throws IOException {
+ return Streams.readSingleByte(this);
+ }
+
+ @Override
+ public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+ if (mLength <= 0) {
+ return -1;
+ } else if (byteCount > mLength) {
+ byteCount = (int) mLength;
+ }
+
+ final int n = mWrapped.read(buffer, byteOffset, byteCount);
+ if (n == -1) {
+ if (mLength > 0) {
+ throw new IOException("Unexpected EOF; expected " + mLength + " more bytes");
+ }
+ } else {
+ mLength -= n;
+ }
+ return n;
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 59399d2..e38fce9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1075,6 +1075,13 @@
<permission android:name="android.permission.TV_INPUT_HARDWARE"
android:protectionLevel="signatureOrSystem" />
+ <!-- @hide Allows enabling/disabling OEM unlock
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.OEM_UNLOCK_STATE"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="signature" />
+
+
<!-- =========================================== -->
<!-- Permissions associated with audio capture -->
<!-- =========================================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f34fbc13..4585b78 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1610,12 +1610,15 @@
-->
</string-array>
-<!-- Flag indicating apps will skip sending hold request before merge. In this case
- IMS service implementation will do both.i.e.hold followed by merge. -->
+ <!-- Flag indicating which package name can access the persistent data partition -->
+ <string name="config_persistentDataPackageName" translatable="false"></string>
+
+ <!-- Flag indicating apps will skip sending hold request before merge. In this case
+ IMS service implementation will do both.i.e.hold followed by merge. -->
<bool name="skipHoldBeforeMerge">true</bool>
<!-- Flag indicating emergency calls will always use IMS irrespective of the state of
- the IMS connection -->
+ the IMS connection -->
<bool name="useImsAlwaysForEmergencyCall">true</bool>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9422212..2065088 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1668,6 +1668,7 @@
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
<java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
+ <java-symbol type="string" name="config_persistentDataPackageName" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 30c6f5d..bcfda99 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -187,7 +187,6 @@
*/
bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) {
if (len < 2 || polygon == NULL) {
- ALOGW("Invalid polygon %p, length is %d @ isClockwise()", polygon, len);
return true;
}
double sum = 0;
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
new file mode 100644
index 0000000..1eded6f
--- /dev/null
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -0,0 +1,261 @@
+package com.android.server;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.service.persistentdata.IPersistentDataBlockService;
+import android.util.Log;
+import com.android.internal.R;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+/**
+ * Service for reading and writing blocks to a persistent partition.
+ * This data will live across factory resets.
+ *
+ * Allows writing one block at a time. Namely, each time
+ * {@link android.service.persistentdata.IPersistentDataBlockService}.write(byte[] data)
+ * is called, it will overwite the data that was previously written on the block.
+ *
+ * Clients can query the size of the currently written block via
+ * {@link android.service.persistentdata.IPersistentDataBlockService}.getTotalDataSize().
+ *
+ * Clients can any number of bytes from the currently written block up to its total size by invoking
+ * {@link android.service.persistentdata.IPersistentDataBlockService}.read(byte[] data)
+ */
+public class PersistentDataBlockService extends SystemService {
+ private static final String TAG = PersistentDataBlockService.class.getSimpleName();
+
+ private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
+ private static final int HEADER_SIZE = 8;
+ private static final int BLOCK_ID = 0x1990;
+
+ private final Context mContext;
+ private final String mDataBlockFile;
+ private long mBlockDeviceSize;
+
+ private final int mAllowedUid;
+
+ public PersistentDataBlockService(Context context) {
+ super(context);
+ mContext = context;
+ mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+ mBlockDeviceSize = 0; // Load lazily
+ String allowedPackage = context.getResources()
+ .getString(R.string.config_persistentDataPackageName);
+ PackageManager pm = mContext.getPackageManager();
+ int allowedUid = -1;
+ try {
+ allowedUid = pm.getPackageUid(allowedPackage,
+ Binder.getCallingUserHandle().getIdentifier());
+ } catch (PackageManager.NameNotFoundException e) {
+ // not expected
+ Log.e(TAG, "not able to find package " + allowedPackage, e);
+ }
+
+ mAllowedUid = allowedUid;
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
+ }
+
+ private void enforceOemUnlockPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.OEM_UNLOCK_STATE,
+ "Can't access OEM unlock state");
+ }
+
+ private void enforceUid(int callingUid) {
+ if (callingUid != mAllowedUid) {
+ throw new SecurityException("uid " + callingUid + " not allowed to access PST");
+ }
+ }
+
+ private int getTotalDataSize(DataInputStream inputStream) throws IOException {
+ int totalDataSize;
+ int blockId = inputStream.readInt();
+ if (blockId == BLOCK_ID) {
+ totalDataSize = inputStream.readInt();
+ } else {
+ totalDataSize = 0;
+ }
+ return totalDataSize;
+ }
+
+ private long maybeReadBlockDeviceSize() {
+ synchronized (this) {
+ if (mBlockDeviceSize == 0) {
+ mBlockDeviceSize = getBlockDeviceSize(mDataBlockFile);
+ }
+ }
+
+ return mBlockDeviceSize;
+ }
+
+ private native long getBlockDeviceSize(String path);
+
+ private final IBinder mService = new IPersistentDataBlockService.Stub() {
+ @Override
+ public int write(byte[] data) throws RemoteException {
+ enforceUid(Binder.getCallingUid());
+
+ // Need to ensure we don't write over the last byte
+ if (data.length > maybeReadBlockDeviceSize() - HEADER_SIZE - 1) {
+ return -1;
+ }
+
+ DataOutputStream outputStream;
+ try {
+ outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "partition not available?", e);
+ return -1;
+ }
+
+ ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
+ headerAndData.putInt(BLOCK_ID);
+ headerAndData.putInt(data.length);
+ headerAndData.put(data);
+
+ try {
+ outputStream.write(headerAndData.array());
+ return data.length;
+ } catch (IOException e) {
+ Log.e(TAG, "failed writing to the persistent data block", e);
+ return -1;
+ } finally {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ Log.e(TAG, "failed closing output stream", e);
+ }
+ }
+ }
+
+ @Override
+ public int read(byte[] data) {
+ enforceUid(Binder.getCallingUid());
+
+ DataInputStream inputStream;
+ try {
+ inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "partition not available?", e);
+ return -1;
+ }
+
+ try {
+ int totalDataSize = getTotalDataSize(inputStream);
+ return inputStream.read(data, 0,
+ (data.length > totalDataSize) ? totalDataSize : data.length);
+ } catch (IOException e) {
+ Log.e(TAG, "failed to read data", e);
+ return -1;
+ } finally {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close OutputStream");
+ }
+ }
+ }
+
+ @Override
+ public void setOemUnlockEnabled(boolean enabled) {
+ enforceOemUnlockPermission();
+ FileOutputStream outputStream;
+ try {
+ outputStream = new FileOutputStream(new File(mDataBlockFile));
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "parition not available", e);
+ return;
+ }
+
+ try {
+ FileChannel channel = outputStream.getChannel();
+
+ channel.position(maybeReadBlockDeviceSize() - 1);
+
+ ByteBuffer data = ByteBuffer.allocate(1);
+ data.put(enabled ? (byte) 1 : (byte) 0);
+ data.flip();
+
+ channel.write(data);
+ } catch (IOException e) {
+ Log.e(TAG, "unable to access persistent partition", e);
+ } finally {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close OutputStream");
+ }
+ }
+ }
+
+ @Override
+ public boolean getOemUnlockEnabled() {
+ enforceOemUnlockPermission();
+ DataInputStream inputStream;
+ try {
+ inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "partition not available");
+ return false;
+ }
+
+ try {
+ inputStream.skip(maybeReadBlockDeviceSize() - 1);
+ return inputStream.readByte() != 0;
+ } catch (IOException e) {
+ Log.e(TAG, "unable to access persistent partition", e);
+ return false;
+ } finally {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close OutputStream");
+ }
+ }
+ }
+
+ @Override
+ public int getDataBlockSize() {
+ enforceUid(Binder.getCallingUid());
+
+ DataInputStream inputStream;
+ try {
+ inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "partition not available");
+ return 0;
+ }
+
+ try {
+ return getTotalDataSize(inputStream);
+ } catch (IOException e) {
+ Log.e(TAG, "error reading data block size");
+ return 0;
+ } finally {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close OutputStream");
+ }
+ }
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 5fdfce4..682a38d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -30,8 +30,11 @@
import android.os.FileUtils;
import android.os.HandlerThread;
import android.os.Process;
+import android.os.SELinux;
import android.os.UserHandle;
import android.os.UserManager;
+import android.system.ErrnoException;
+import android.system.Os;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -42,6 +45,8 @@
import com.google.android.collect.Sets;
import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
public class PackageInstallerService extends IPackageInstaller.Stub {
private static final String TAG = "PackageInstaller";
@@ -54,8 +59,8 @@
private final AppOpsManager mAppOps;
private final File mStagingDir;
+ private final HandlerThread mInstallThread;
- private final HandlerThread mInstallThread = new HandlerThread(TAG);
private final Callback mCallback = new Callback();
@GuardedBy("mSessions")
@@ -63,27 +68,50 @@
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
+ private static final FilenameFilter sStageFilter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.startsWith("vmdl") && name.endsWith(".tmp");
+ }
+ };
+
public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) {
mContext = context;
mPm = pm;
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mStagingDir = stagingDir;
- mStagingDir.mkdirs();
+
+ mInstallThread = new HandlerThread(TAG);
+ mInstallThread.start();
synchronized (mSessions) {
readSessionsLocked();
// Clean up orphaned staging directories
- final ArraySet<String> dirs = Sets.newArraySet(mStagingDir.list());
+ final ArraySet<File> stages = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
for (int i = 0; i < mSessions.size(); i++) {
- dirs.remove(Integer.toString(mSessions.keyAt(i)));
+ final PackageInstallerSession session = mSessions.valueAt(i);
+ stages.remove(session.sessionStageDir);
}
- for (String dirName : dirs) {
- Slog.w(TAG, "Deleting orphan session " + dirName);
- final File dir = new File(mStagingDir, dirName);
- FileUtils.deleteContents(dir);
- dir.delete();
+ for (File stage : stages) {
+ Slog.w(TAG, "Deleting orphan stage " + stage);
+ if (stage.isDirectory()) {
+ FileUtils.deleteContents(stage);
+ }
+ stage.delete();
+ }
+ }
+ }
+
+ @Deprecated
+ public File allocateSessionDir() throws IOException {
+ synchronized (mSessions) {
+ try {
+ final int sessionId = allocateSessionIdLocked();
+ return prepareSessionStageDir(sessionId);
+ } catch (IllegalStateException e) {
+ throw new IOException(e);
}
}
}
@@ -110,11 +138,10 @@
}
@Override
- public int createSession(int userId, String installerPackageName,
- PackageInstallerParams params) {
+ public int createSession(String installerPackageName, PackageInstallerParams params,
+ int userId) {
final int callingUid = Binder.getCallingUid();
mPm.enforceCrossUserPermission(callingUid, userId, false, TAG);
- mAppOps.checkPackage(callingUid, installerPackageName);
if (mPm.isUserRestricted(UserHandle.getUserId(callingUid),
UserManager.DISALLOW_INSTALL_APPS)) {
@@ -124,20 +151,21 @@
if ((callingUid == Process.SHELL_UID) || (callingUid == 0)) {
params.installFlags |= INSTALL_FROM_ADB;
} else {
+ mAppOps.checkPackage(callingUid, installerPackageName);
+
params.installFlags &= ~INSTALL_FROM_ADB;
params.installFlags &= ~INSTALL_ALL_USERS;
params.installFlags |= INSTALL_REPLACE_EXISTING;
}
synchronized (mSessions) {
- final int sessionId = allocateSessionIdLocked();
final long createdMillis = System.currentTimeMillis();
- final File sessionDir = new File(mStagingDir, Integer.toString(sessionId));
- sessionDir.mkdirs();
+ final int sessionId = allocateSessionIdLocked();
+ final File sessionStageDir = prepareSessionStageDir(sessionId);
final PackageInstallerSession session = new PackageInstallerSession(mCallback, mPm,
sessionId, userId, installerPackageName, callingUid, params, createdMillis,
- sessionDir, mInstallThread.getLooper());
+ sessionStageDir, mInstallThread.getLooper());
mSessions.put(sessionId, session);
writeSessionsAsync();
@@ -166,8 +194,30 @@
return mNextSessionId++;
}
+ private File prepareSessionStageDir(int sessionId) {
+ final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp");
+
+ if (file.exists()) {
+ throw new IllegalStateException();
+ }
+
+ try {
+ Os.mkdir(file.getAbsolutePath(), 0755);
+ Os.chmod(file.getAbsolutePath(), 0755);
+ } catch (ErrnoException e) {
+ // This purposefully throws if directory already exists
+ throw new IllegalStateException("Failed to prepare session dir", e);
+ }
+
+ if (!SELinux.restorecon(file)) {
+ throw new IllegalStateException("Failed to prepare session dir");
+ }
+
+ return file;
+ }
+
@Override
- public int[] getSessions(int userId, String installerPackageName) {
+ public int[] getSessions(String installerPackageName, int userId) {
final int callingUid = Binder.getCallingUid();
mPm.enforceCrossUserPermission(callingUid, userId, false, TAG);
mAppOps.checkPackage(callingUid, installerPackageName);
@@ -187,13 +237,14 @@
}
@Override
- public void uninstall(int userId, String basePackageName, IPackageDeleteObserver observer) {
- mPm.deletePackageAsUser(basePackageName, observer, userId, 0);
+ public void uninstall(String basePackageName, int flags, IPackageDeleteObserver observer,
+ int userId) {
+ mPm.deletePackageAsUser(basePackageName, observer, userId, flags);
}
@Override
- public void uninstallSplit(int userId, String basePackageName, String overlayName,
- IPackageDeleteObserver observer) {
+ public void uninstallSplit(String basePackageName, String overlayName, int flags,
+ IPackageDeleteObserver observer, int userId) {
// TODO: flesh out once PM has split support
throw new UnsupportedOperationException();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 8067bd9..3448803 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -20,6 +20,8 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_WRONLY;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstallObserver2;
@@ -37,9 +39,11 @@
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.system.ErrnoException;
+import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
import android.util.ArraySet;
@@ -58,6 +62,10 @@
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String TAG = "PackageInstaller";
+ // TODO: enforce INSTALL_ALLOW_TEST
+ // TODO: enforce INSTALL_ALLOW_DOWNGRADE
+ // TODO: handle INSTALL_EXTERNAL, INSTALL_INTERNAL
+
private final PackageInstallerService.Callback mCallback;
private final PackageManagerService mPm;
private final Handler mHandler;
@@ -69,7 +77,7 @@
public final int installerUid;
public final PackageInstallerParams params;
public final long createdMillis;
- public final File sessionDir;
+ public final File sessionStageDir;
private static final int MSG_INSTALL = 0;
@@ -85,6 +93,7 @@
installLocked();
} catch (InstallFailedException e) {
Slog.e(TAG, "Install failed: " + e);
+ destroy();
try {
mRemoteObserver.packageInstalled(mPackageName, null, e.error);
} catch (RemoteException ignored) {
@@ -114,7 +123,7 @@
public PackageInstallerSession(PackageInstallerService.Callback callback,
PackageManagerService pm, int sessionId, int userId, String installerPackageName,
- int installerUid, PackageInstallerParams params, long createdMillis, File sessionDir,
+ int installerUid, PackageInstallerParams params, long createdMillis, File sessionStageDir,
Looper looper) {
mCallback = callback;
mPm = pm;
@@ -126,7 +135,7 @@
this.installerUid = installerUid;
this.params = params;
this.createdMillis = createdMillis;
- this.sessionDir = sessionDir;
+ this.sessionStageDir = sessionStageDir;
// Check against any explicitly provided signatures
mSignatures = params.signatures;
@@ -138,6 +147,9 @@
== PackageManager.PERMISSION_GRANTED) {
mPermissionsConfirmed = true;
}
+ if (installerUid == Process.SHELL_UID || installerUid == 0) {
+ mPermissionsConfirmed = true;
+ }
}
@Override
@@ -168,10 +180,11 @@
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
- final File target = new File(sessionDir, name);
+ final File target = new File(sessionStageDir, name);
final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
- OsConstants.O_CREAT | OsConstants.O_WRONLY, 00700);
+ O_CREAT | O_WRONLY, 0644);
+ Os.chmod(target.getAbsolutePath(), 0644);
// If caller specified a total length, allocate it for them. Free up
// cache space to grow, if needed.
@@ -235,7 +248,7 @@
if (!mPermissionsConfirmed) {
// TODO: async confirm permissions with user
// when they confirm, we'll kick off another install() pass
- mPermissionsConfirmed = true;
+ throw new SecurityException("Caller must hold INSTALL permission");
}
// Inherit any packages and native libraries from existing install that
@@ -258,7 +271,7 @@
}
};
- mPm.installStage(mPackageName, this.sessionDir, localObserver, params, installerPackageName,
+ mPm.installStage(mPackageName, this.sessionStageDir, localObserver, params, installerPackageName,
installerUid, new UserHandle(userId));
}
@@ -271,9 +284,8 @@
private void validateInstallLocked() throws InstallFailedException {
mPackageName = null;
mVersionCode = -1;
- mSignatures = null;
- final File[] files = sessionDir.listFiles();
+ final File[] files = sessionStageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
@@ -296,11 +308,11 @@
}
// Use first package to define unknown values
- if (mPackageName != null) {
+ if (mPackageName == null) {
mPackageName = info.packageName;
mVersionCode = info.versionCode;
}
- if (mSignatures != null) {
+ if (mSignatures == null) {
mSignatures = info.signatures;
}
@@ -310,9 +322,9 @@
// Take this opportunity to enforce uniform naming
final String name;
if (info.splitName == null) {
- name = info.packageName + ".apk";
+ name = "base.apk";
} else {
- name = info.packageName + "-" + info.splitName + ".apk";
+ name = "split_" + info.splitName + ".apk";
}
if (!FileUtils.isValidExtFilename(name)) {
throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
@@ -382,7 +394,7 @@
final File existingDir = new File(app.getBaseCodePath());
try {
- linkTreeIgnoringExisting(existingDir, sessionDir);
+ linkTreeIgnoringExisting(existingDir, sessionStageDir);
} catch (ErrnoException e) {
throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR,
"Failed to splice into stage");
@@ -416,8 +428,8 @@
synchronized (mLock) {
mInvalid = true;
}
- FileUtils.deleteContents(sessionDir);
- sessionDir.delete();
+ FileUtils.deleteContents(sessionStageDir);
+ sessionStageDir.delete();
} finally {
mCallback.onSessionInvalid(this);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0a201f9..fe9ce8f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -377,11 +377,6 @@
// apps.
final File mDrmAppPrivateInstallDir;
- /** Directory where third-party apps are staged before install */
- final File mAppStagingDir;
-
- private final Random mTempFileRandom = new Random();
-
// ----------------------------------------------------------------
// Lock for state used when installing and doing other long running
@@ -1363,7 +1358,6 @@
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- mAppStagingDir = new File(dataDir, "app-staging");
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
@@ -1767,7 +1761,7 @@
} // synchronized (mPackages)
} // synchronized (mInstallLock)
- mInstallerService = new PackageInstallerService(context, this, mAppStagingDir);
+ mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
@@ -4172,6 +4166,7 @@
pp.collectCertificates(pkg, parseFlags);
pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
+ Slog.e(TAG, "Failed during collect: " + e);
mLastScanError = e.error;
return false;
}
@@ -4200,6 +4195,7 @@
try {
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
+ Slog.e(TAG, "Failed during scan: " + e);
mLastScanError = e.error;
return null;
}
@@ -7652,8 +7648,20 @@
verificationParams.setInstallerUid(uid);
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(originFile, observer, filteredFlags, installerPackageName,
- verificationParams, user, packageAbiOverride);
+ msg.obj = new InstallParams(originFile, false, observer, filteredFlags,
+ installerPackageName, verificationParams, user, packageAbiOverride);
+ mHandler.sendMessage(msg);
+ }
+
+ void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer,
+ PackageInstallerParams params, String installerPackageName, int installerUid,
+ UserHandle user) {
+ final VerificationParams verifParams = new VerificationParams(null, params.originatingUri,
+ params.referrerUri, installerUid, null);
+
+ final Message msg = mHandler.obtainMessage(INIT_COPY);
+ msg.obj = new InstallParams(stageDir, true, observer, params.installFlags,
+ installerPackageName, verifParams, user, params.abiOverride);
mHandler.sendMessage(msg);
}
@@ -7769,17 +7777,6 @@
}
}
- void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer2,
- PackageInstallerParams params, String installerPackageName, int installerUid,
- UserHandle user) {
- Slog.e(TAG, "TODO: install stage!");
- try {
- observer2.packageInstalled(packageName, null,
- PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
- } catch (RemoteException ignored) {
- }
- }
-
/**
* @hide
*/
@@ -8365,10 +8362,10 @@
final File originFile;
/**
- * Flag indicating that {@link #originFile} lives in a trusted location,
+ * Flag indicating that {@link #originFile} has already been staged,
* meaning downstream users don't need to defensively copy the contents.
*/
- boolean originTrusted;
+ boolean originStaged;
final IPackageInstallObserver2 observer;
int flags;
@@ -8379,12 +8376,12 @@
final String packageAbiOverride;
final String packageInstructionSetOverride;
- InstallParams(File originFile, IPackageInstallObserver2 observer, int flags,
- String installerPackageName, VerificationParams verificationParams, UserHandle user,
- String packageAbiOverride) {
+ InstallParams(File originFile, boolean originStaged, IPackageInstallObserver2 observer,
+ int flags, String installerPackageName, VerificationParams verificationParams,
+ UserHandle user, String packageAbiOverride) {
super(user);
this.originFile = Preconditions.checkNotNull(originFile);
- this.originTrusted = false;
+ this.originStaged = originStaged;
this.observer = observer;
this.flags = flags;
this.installerPackageName = installerPackageName;
@@ -8912,8 +8909,8 @@
static abstract class InstallArgs {
/** @see InstallParams#originFile */
final File originFile;
- /** @see InstallParams#originTrusted */
- final boolean originTrusted;
+ /** @see InstallParams#originStaged */
+ final boolean originStaged;
// TODO: define inherit location
@@ -8926,11 +8923,11 @@
final String instructionSet;
final String abiOverride;
- InstallArgs(File originFile, boolean originTrusted, IPackageInstallObserver2 observer,
+ InstallArgs(File originFile, boolean originStaged, IPackageInstallObserver2 observer,
int flags, String installerPackageName, ManifestDigest manifestDigest,
UserHandle user, String instructionSet, String abiOverride) {
this.originFile = originFile;
- this.originTrusted = originTrusted;
+ this.originStaged = originStaged;
this.flags = flags;
this.observer = observer;
this.installerPackageName = installerPackageName;
@@ -9009,7 +9006,7 @@
/** New install */
FileInstallArgs(InstallParams params) {
- super(params.originFile, params.originTrusted, params.observer, params.flags,
+ super(params.originFile, params.originStaged, params.observer, params.flags,
params.installerPackageName, params.getManifestDigest(), params.getUser(),
params.packageInstructionSetOverride, params.packageAbiOverride);
if (isFwdLocked()) {
@@ -9028,7 +9025,7 @@
/** New install from existing */
FileInstallArgs(File originFile, String instructionSet) {
- super(originFile, true, null, 0, null, null, null, instructionSet, null);
+ super(originFile, false, null, 0, null, null, null, instructionSet, null);
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
@@ -9053,37 +9050,45 @@
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
- try {
- final File tempDir = createTempPackageDir(mAppInstallDir);
- codeFile = tempDir;
- resourceFile = tempDir;
- } catch (IOException e) {
- Slog.w(TAG, "Failed to create copy file: " + e);
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- }
+ int ret = PackageManager.INSTALL_SUCCEEDED;
- final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
- @Override
- public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
- if (!FileUtils.isValidExtFilename(name)) {
- throw new IllegalArgumentException("Invalid filename: " + name);
- }
- try {
- final File file = new File(codeFile, name);
- final FileDescriptor fd = Os.open(file.getAbsolutePath(),
- O_RDWR | O_CREAT, 0644);
- Os.chmod(file.getAbsolutePath(), 0644);
- return new ParcelFileDescriptor(fd);
- } catch (ErrnoException e) {
- throw new RemoteException("Failed to open: " + e.getMessage());
- }
+ if (originStaged) {
+ Slog.d(TAG, originFile + " already staged; skipping copy");
+ codeFile = originFile;
+ resourceFile = originFile;
+ } else {
+ try {
+ final File tempDir = mInstallerService.allocateSessionDir();
+ codeFile = tempDir;
+ resourceFile = tempDir;
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to create copy file: " + e);
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
- };
- int ret = imcs.copyPackage(originFile.getAbsolutePath(), target);
- if (ret != PackageManager.INSTALL_SUCCEEDED) {
- Slog.e(TAG, "Failed to copy package");
- return ret;
+ final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
+ @Override
+ public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
+ if (!FileUtils.isValidExtFilename(name)) {
+ throw new IllegalArgumentException("Invalid filename: " + name);
+ }
+ try {
+ final File file = new File(codeFile, name);
+ final FileDescriptor fd = Os.open(file.getAbsolutePath(),
+ O_RDWR | O_CREAT, 0644);
+ Os.chmod(file.getAbsolutePath(), 0644);
+ return new ParcelFileDescriptor(fd);
+ } catch (ErrnoException e) {
+ throw new RemoteException("Failed to open: " + e.getMessage());
+ }
+ }
+ };
+
+ ret = imcs.copyPackage(originFile.getAbsolutePath(), target);
+ if (ret != PackageManager.INSTALL_SUCCEEDED) {
+ Slog.e(TAG, "Failed to copy package");
+ return ret;
+ }
}
String[] abiList = (abiOverride != null) ?
@@ -9288,7 +9293,7 @@
/** New install */
AsecInstallArgs(InstallParams params) {
- super(params.originFile, params.originTrusted, params.observer, params.flags,
+ super(params.originFile, params.originStaged, params.observer, params.flags,
params.installerPackageName, params.getManifestDigest(), params.getUser(),
params.packageInstructionSetOverride, params.packageAbiOverride);
}
@@ -9318,7 +9323,7 @@
/** New install from existing */
AsecInstallArgs(File originPackageFile, String cid, String instructionSet,
boolean isExternal, boolean isForwardLocked) {
- super(originPackageFile, true, null, (isExternal ? INSTALL_EXTERNAL : 0)
+ super(originPackageFile, false, null, (isExternal ? INSTALL_EXTERNAL : 0)
| (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
instructionSet, null);
this.cid = cid;
@@ -10082,6 +10087,7 @@
try {
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
} catch (PackageParserException e) {
+ Slog.e(TAG, "Failed during install: " + e);
res.returnCode = e.error;
return;
}
@@ -10098,6 +10104,7 @@
pp.collectCertificates(pkg, parseFlags);
pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
+ Slog.e(TAG, "Failed during install: " + e);
res.returnCode = e.error;
return;
}
@@ -10276,65 +10283,11 @@
return name.startsWith("vmdl") && name.endsWith(".tmp");
}
};
- deleteTempPackageFilesInDirectory(mAppInstallDir, filter);
- deleteTempPackageFilesInDirectory(mDrmAppPrivateInstallDir, filter);
- }
-
- private static final void deleteTempPackageFilesInDirectory(File directory,
- FilenameFilter filter) {
- final File[] files = directory.listFiles(filter);
- if (!ArrayUtils.isEmpty(files)) {
- for (File file : files) {
- if (file.isDirectory()) {
- FileUtils.deleteContents(file);
- file.delete();
- } else if (file.isFile()) {
- file.delete();
- }
- }
+ for (File file : mDrmAppPrivateInstallDir.listFiles(filter)) {
+ file.delete();
}
}
- private File createTempPackageDir(File installDir) throws IOException {
- int n = 0;
- while (n++ < 32) {
- final File file = new File(installDir, "vmdl" + mTempFileRandom.nextInt() + ".tmp");
- try {
- Os.mkdir(file.getAbsolutePath(), 0755);
- Os.chmod(file.getAbsolutePath(), 0755);
- if (!SELinux.restorecon(file)) {
- throw new IOException("Failed to restorecon");
- }
- return file;
- } catch (ErrnoException e) {
- if (e.errno == EEXIST) continue;
- throw e.rethrowAsIOException();
- }
- }
- throw new IOException("Failed to create temp directory");
- }
-
- private File createTempPackageFile(File installDir) throws IOException {
- int n = 0;
- while (n++ < 32) {
- final File file = new File(installDir, "vmdl" + mTempFileRandom.nextInt() + ".tmp");
- try {
- final FileDescriptor fd = Os.open(file.getAbsolutePath(),
- O_RDWR | O_CREAT | O_EXCL, 0644);
- IoUtils.closeQuietly(fd);
- Os.chmod(file.getAbsolutePath(), 0644);
- if (!SELinux.restorecon(file)) {
- throw new IOException("Failed to restorecon");
- }
- return file;
- } catch (ErrnoException e) {
- if (e.errno == EEXIST) continue;
- throw e.rethrowAsIOException();
- }
- }
- throw new IOException("Failed to create temp file");
- }
-
@Override
public void deletePackageAsUser(final String packageName,
final IPackageDeleteObserver observer,
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index db44d3a..5599760 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -26,6 +26,7 @@
$(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_PersistentDataBlockService.cpp \
$(LOCAL_REL_DIR)/onload.cpp
include external/stlport/libstlport.mk
diff --git a/services/core/jni/com_android_server_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
new file mode 100644
index 0000000..673a6e2
--- /dev/null
+++ b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android_runtime/AndroidRuntime.h>
+#include <JNIHelp.h>
+#include <jni.h>
+
+#include <utils/misc.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+#include <inttypes.h>
+#include <fcntl.h>
+
+namespace android {
+
+ uint64_t get_block_device_size(int fd)
+ {
+ uint64_t size = 0;
+ int ret;
+
+ ret = ioctl(fd, BLKGETSIZE64, &size);
+
+ if (ret)
+ return 0;
+
+ return size;
+ }
+
+ static jlong com_android_server_PeristentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath) {
+ const char *path = env->GetStringUTFChars(jpath, 0);
+ int fd = open(path, O_RDONLY);
+
+ if (fd < 0)
+ return 0;
+
+ return get_block_device_size(fd);
+ }
+
+ static JNINativeMethod sMethods[] = {
+ /* name, signature, funcPtr */
+ {"getBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_PeristentDataBlockService_getBlockDeviceSize},
+ };
+
+ int register_android_server_PersistentDataBlockService(JNIEnv* env)
+ {
+ return jniRegisterNativeMethods(env, "com/android/server/PersistentDataBlockService",
+ sMethods, NELEM(sMethods));
+ }
+
+} /* namespace android */
\ No newline at end of file
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index a302104..bf7501e 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -41,6 +41,7 @@
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
+int register_android_server_PersistentDataBlockService(JNIEnv* env);
};
using namespace android;
@@ -77,6 +78,7 @@
register_android_server_hdmi_HdmiCecController(env);
register_android_server_hdmi_HdmiMhlController(env);
register_android_server_tv_TvInputHal(env);
+ register_android_server_PersistentDataBlockService(env);
return JNI_VERSION_1_4;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b63bbe7..e0ebd54 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -135,6 +135,7 @@
"com.android.server.ethernet.EthernetService";
private static final String JOB_SCHEDULER_SERVICE_CLASS =
"com.android.server.job.JobSchedulerService";
+ private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
private final int mFactoryTestMode;
private Timer mProfilerSnapshotTimer;
@@ -574,6 +575,10 @@
reportWtf("starting LockSettingsService service", e);
}
+ if (!SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("")) {
+ mSystemServiceManager.startService(PersistentDataBlockService.class);
+ }
+
// Always start the Device Policy Manager, so that the API is compatible with
// API8.
mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 3d93bbe..963c796 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -900,8 +900,8 @@
return 0;
}
-status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split,
- sp<AaptFile>& outFile) {
+status_t generateAndroidManifestForSplit(Bundle* bundle, const sp<AaptAssets>& assets,
+ const sp<ApkSplit>& split, sp<AaptFile>& outFile, ResourceTable* table) {
const String8 filename("AndroidManifest.xml");
const String16 androidPrefix("android");
const String16 androidNSUri("http://schemas.android.com/apk/res/android");
@@ -910,8 +910,19 @@
// Build the <manifest> tag
sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest"));
- // Add the 'package' attribute which is set to the original package name.
- manifest->addAttribute(String16(), String16("package"), package);
+ // Add the 'package' attribute which is set to the package name.
+ const char* packageName = assets->getPackage();
+ const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
+ if (manifestPackageNameOverride != NULL) {
+ packageName = manifestPackageNameOverride;
+ }
+ manifest->addAttribute(String16(), String16("package"), String16(packageName));
+
+ // Add the 'versionCode' attribute which is set to the original version code.
+ if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "versionCode",
+ bundle->getVersionCode(), true, true)) {
+ return UNKNOWN_ERROR;
+ }
// Add the 'split' attribute which describes the configurations included.
String8 splitName("config_");
@@ -923,8 +934,8 @@
manifest->addChild(app);
root->addChild(manifest);
- status_t err = root->flatten(outFile, true, true);
- if (err != NO_ERROR) {
+ int err = compileXmlFile(assets, root, outFile, table);
+ if (err < NO_ERROR) {
return err;
}
outFile->setCompressionMethod(ZipEntry::kCompressDeflated);
@@ -1371,8 +1382,8 @@
} else {
sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
AaptGroupEntry(), String8());
- err = generateAndroidManifestForSplit(String16(assets->getPackage()), split,
- generatedManifest);
+ err = generateAndroidManifestForSplit(bundle, assets, split,
+ generatedManifest, &table);
if (err != NO_ERROR) {
fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n",
split->getPrintableName().string());