Merge "Revert session-transfer change" into oc-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index ec62158..6fa840e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10506,7 +10506,6 @@
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
method public void removeSplit(java.lang.String) throws java.io.IOException;
method public void setStagingProgress(float);
- method public void transfer(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
}
public static abstract class PackageInstaller.SessionCallback {
@@ -10524,16 +10523,10 @@
method public android.graphics.Bitmap getAppIcon();
method public java.lang.CharSequence getAppLabel();
method public java.lang.String getAppPackageName();
- method public int getInstallLocation();
method public int getInstallReason();
method public java.lang.String getInstallerPackageName();
- method public int getMode();
- method public int getOriginatingUid();
- method public android.net.Uri getOriginatingUri();
method public float getProgress();
- method public android.net.Uri getReferrerUri();
method public int getSessionId();
- method public long getSize();
method public boolean isActive();
method public boolean isSealed();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index f2d2b34..c102ccc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -11180,14 +11180,12 @@
method public void abandon();
method public void close();
method public void commit(android.content.IntentSender);
- method public void commitTransferred(android.content.IntentSender);
method public void fsync(java.io.OutputStream) throws java.io.IOException;
method public java.lang.String[] getNames() throws java.io.IOException;
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
method public void removeSplit(java.lang.String) throws java.io.IOException;
method public void setStagingProgress(float);
- method public void transfer(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
}
public static abstract class PackageInstaller.SessionCallback {
@@ -11202,26 +11200,13 @@
public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
method public android.content.Intent createDetailsIntent();
method public int describeContents();
- method public boolean getAllocateAggressive();
- method public boolean getAllowDowngrade();
method public android.graphics.Bitmap getAppIcon();
method public java.lang.CharSequence getAppLabel();
method public java.lang.String getAppPackageName();
- method public boolean getDontKillApp();
- method public java.lang.String[] getGrantedRuntimePermissions();
- method public boolean getInstallAsFullApp(boolean);
- method public boolean getInstallAsInstantApp(boolean);
- method public boolean getInstallAsVirtualPreload();
- method public int getInstallLocation();
method public int getInstallReason();
method public java.lang.String getInstallerPackageName();
- method public int getMode();
- method public int getOriginatingUid();
- method public android.net.Uri getOriginatingUri();
method public float getProgress();
- method public android.net.Uri getReferrerUri();
method public int getSessionId();
- method public long getSize();
method public boolean isActive();
method public boolean isSealed();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index c80c364..630ca8e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10544,7 +10544,6 @@
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
method public void removeSplit(java.lang.String) throws java.io.IOException;
method public void setStagingProgress(float);
- method public void transfer(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
}
public static abstract class PackageInstaller.SessionCallback {
@@ -10562,16 +10561,10 @@
method public android.graphics.Bitmap getAppIcon();
method public java.lang.CharSequence getAppLabel();
method public java.lang.String getAppPackageName();
- method public int getInstallLocation();
method public int getInstallReason();
method public java.lang.String getInstallerPackageName();
- method public int getMode();
- method public int getOriginatingUid();
- method public android.net.Uri getOriginatingUri();
method public float getProgress();
- method public android.net.Uri getReferrerUri();
method public int getSessionId();
- method public long getSize();
method public boolean isActive();
method public boolean isSealed();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 0b16852..2a3fac3 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -32,7 +32,6 @@
void removeSplit(String splitName);
void close();
- void commit(in IntentSender statusReceiver, boolean forTransferred);
- void transfer(in String packageName);
+ void commit(in IntentSender statusReceiver);
void abandon();
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index f4fdcaa..c3ebf55 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -38,7 +38,6 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.system.ErrnoException;
@@ -794,7 +793,7 @@
* @throws IOException if trouble opening the file for writing, such as
* lack of disk space or unavailable media.
* @throws SecurityException if called after the session has been
- * sealed or abandoned
+ * committed or abandoned.
*/
public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
long lengthBytes) throws IOException {
@@ -919,68 +918,7 @@
*/
public void commit(@NonNull IntentSender statusReceiver) {
try {
- mSession.commit(statusReceiver, false);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Attempt to commit a session that has been {@link #transfer(String) transferred}.
- *
- * <p>If the device reboots before the session has been finalized, you may commit the
- * session again.
- *
- * <p>The caller of this method is responsible to ensure the safety of the session. As the
- * session was created by another - usually less trusted - app, it is paramount that before
- * committing <u>all</u> public and system {@link SessionInfo properties of the session}
- * and <u>all</u> {@link #openRead(String) APKs} are verified by the caller. It might happen
- * that new properties are added to the session with a new API revision. In this case the
- * callers need to be updated.
- *
- * @param statusReceiver Callbacks called when the state of the session changes.
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
- public void commitTransferred(@NonNull IntentSender statusReceiver) {
- try {
- mSession.commit(statusReceiver, true);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Transfer the session to a new owner.
- * <p>
- * Only sessions that update the installing app can be transferred.
- * <p>
- * After the transfer to a package with a different uid all method calls on the session
- * will cause {@link SecurityException}s.
- * <p>
- * Once this method is called, the session is sealed and no additional mutations beside
- * committing it may be performed on the session.
- *
- * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
- * permission.
- *
- * @throws PackageManager.NameNotFoundException if the new owner could not be found.
- * @throws SecurityException if called after the session has been committed or abandoned.
- * @throws SecurityException if the session does not update the original installer
- * @throws SecurityException if streams opened through
- * {@link #openWrite(String, long, long) are still open.
- */
- public void transfer(@NonNull String packageName)
- throws PackageManager.NameNotFoundException {
- Preconditions.checkNotNull(packageName);
-
- try {
- mSession.transfer(packageName);
- } catch (ParcelableException e) {
- e.maybeRethrow(PackageManager.NameNotFoundException.class);
- throw new RuntimeException(e);
+ mSession.commit(statusReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1103,26 +1041,6 @@
}
/**
- * Check if there are hidden options set.
- *
- * <p>Hidden options are those options that cannot be verified via public or system-api
- * methods on {@link SessionInfo}.
- *
- * @return {@code true} if any hidden option is set.
- *
- * @hide
- */
- public boolean areHiddenOptionsSet() {
- return (installFlags & (PackageManager.INSTALL_ALLOW_DOWNGRADE
- | PackageManager.INSTALL_DONT_KILL_APP
- | PackageManager.INSTALL_INSTANT_APP
- | PackageManager.INSTALL_FULL_APP
- | PackageManager.INSTALL_VIRTUAL_PRELOAD
- | PackageManager.INSTALL_ALLOCATE_AGGRESSIVE)) != installFlags
- || abiOverride != null || volumeUuid != null;
- }
-
- /**
* Provide value of {@link PackageInfo#installLocation}, which may be used
* to determine where the app will be staged. Defaults to
* {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
@@ -1382,19 +1300,6 @@
public CharSequence appLabel;
/** {@hide} */
- public int installLocation;
- /** {@hide} */
- public Uri originatingUri;
- /** {@hide} */
- public int originatingUid;
- /** {@hide} */
- public Uri referrerUri;
- /** {@hide} */
- public String[] grantedRuntimePermissions;
- /** {@hide} */
- public int installFlags;
-
- /** {@hide} */
public SessionInfo() {
}
@@ -1413,13 +1318,6 @@
appPackageName = source.readString();
appIcon = source.readParcelable(null);
appLabel = source.readString();
-
- installLocation = source.readInt();
- originatingUri = source.readParcelable(null);
- originatingUid = source.readInt();
- referrerUri = source.readParcelable(null);
- grantedRuntimePermissions = source.readStringArray();
- installFlags = source.readInt();
}
/**
@@ -1543,130 +1441,6 @@
return intent;
}
- /**
- * Get the mode of the session as set in the constructor of the {@link SessionParams}.
- *
- * @return One of {@link SessionParams#MODE_FULL_INSTALL}
- * or {@link SessionParams#MODE_INHERIT_EXISTING}
- */
- public int getMode() {
- return mode;
- }
-
- /**
- * Get the value set in {@link SessionParams#setInstallLocation(int)}.
- */
- public int getInstallLocation() {
- return installLocation;
- }
-
- /**
- * Get the value as set in {@link SessionParams#setSize(long)}.
- *
- * <p>The value is a hint and does not have to match the actual size.
- */
- public long getSize() {
- return sizeBytes;
- }
-
- /**
- * Get the value set in {@link SessionParams#setOriginatingUri(Uri)}.
- */
- public @Nullable Uri getOriginatingUri() {
- return originatingUri;
- }
-
- /**
- * Get the value set in {@link SessionParams#setOriginatingUid(int)}.
- */
- public int getOriginatingUid() {
- return originatingUid;
- }
-
- /**
- * Get the value set in {@link SessionParams#setReferrerUri(Uri)}
- */
- public @Nullable Uri getReferrerUri() {
- return referrerUri;
- }
-
- /**
- * Get the value set in {@link SessionParams#setGrantedRuntimePermissions(String[])}.
- *
- * @hide
- */
- @SystemApi
- public @Nullable String[] getGrantedRuntimePermissions() {
- return grantedRuntimePermissions;
- }
-
- /**
- * Get the value set in {@link SessionParams#setAllowDowngrade(boolean)}.
- *
- * @hide
- */
- @SystemApi
- public boolean getAllowDowngrade() {
- return (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
- }
-
- /**
- * Get the value set in {@link SessionParams#setDontKillApp(boolean)}.
- *
- * @hide
- */
- @SystemApi
- public boolean getDontKillApp() {
- return (installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0;
- }
-
- /**
- * If {@link SessionParams#setInstallAsInstantApp(boolean)} was called with {@code true},
- * return true. If it was called with {@code false} or if it was not called return false.
- *
- * @hide
- *
- * @see #getInstallAsFullApp
- */
- @SystemApi
- public boolean getInstallAsInstantApp(boolean isInstantApp) {
- return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
- }
-
- /**
- * If {@link SessionParams#setInstallAsInstantApp(boolean)} was called with {@code false},
- * return true. If it was called with {@code true} or if it was not called return false.
- *
- * @hide
- *
- * @see #getInstallAsInstantApp
- */
- @SystemApi
- public boolean getInstallAsFullApp(boolean isInstantApp) {
- return (installFlags & PackageManager.INSTALL_FULL_APP) != 0;
- }
-
- /**
- * Get if {@link SessionParams#setInstallAsVirtualPreload()} was called.
- *
- * @hide
- */
- @SystemApi
- public boolean getInstallAsVirtualPreload() {
- return (installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0;
- }
-
- /**
- * Get the value set in {@link SessionParams#setAllocateAggressive(boolean)}.
- *
- * @hide
- */
- @SystemApi
- public boolean getAllocateAggressive() {
- return (installFlags & PackageManager.INSTALL_ALLOCATE_AGGRESSIVE) != 0;
- }
-
-
/** {@hide} */
@Deprecated
public @Nullable Intent getDetailsIntent() {
@@ -1693,13 +1467,6 @@
dest.writeString(appPackageName);
dest.writeParcelable(appIcon, flags);
dest.writeString(appLabel != null ? appLabel.toString() : null);
-
- dest.writeInt(installLocation);
- dest.writeParcelable(originatingUri, flags);
- dest.writeInt(originatingUid);
- dest.writeParcelable(referrerUri, flags);
- dest.writeStringArray(grantedRuntimePermissions);
- dest.writeInt(installFlags);
}
public static final Parcelable.Creator<SessionInfo>
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index c3b93b4..bab7011 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -16,6 +16,17 @@
package com.android.server.pm;
+import static com.android.internal.util.XmlUtils.readBitmapAttribute;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
+import static com.android.internal.util.XmlUtils.readStringAttribute;
+import static com.android.internal.util.XmlUtils.readUriAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
+import static com.android.internal.util.XmlUtils.writeStringAttribute;
+import static com.android.internal.util.XmlUtils.writeUriAttribute;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -43,6 +54,8 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.VersionedPackage;
import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -71,6 +84,9 @@
import android.util.SparseIntArray;
import android.util.Xml;
+import java.io.CharArrayWriter;
+import libcore.io.IoUtils;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageHelper;
@@ -81,13 +97,10 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
-import libcore.io.IoUtils;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -112,6 +125,32 @@
/** XML constants used in {@link #mSessionsFile} */
private static final String TAG_SESSIONS = "sessions";
+ private static final String TAG_SESSION = "session";
+ private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
+ private static final String ATTR_SESSION_ID = "sessionId";
+ private static final String ATTR_USER_ID = "userId";
+ private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
+ private static final String ATTR_INSTALLER_UID = "installerUid";
+ private static final String ATTR_CREATED_MILLIS = "createdMillis";
+ private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
+ private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
+ private static final String ATTR_PREPARED = "prepared";
+ private static final String ATTR_SEALED = "sealed";
+ private static final String ATTR_MODE = "mode";
+ private static final String ATTR_INSTALL_FLAGS = "installFlags";
+ private static final String ATTR_INSTALL_LOCATION = "installLocation";
+ private static final String ATTR_SIZE_BYTES = "sizeBytes";
+ private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
+ @Deprecated
+ private static final String ATTR_APP_ICON = "appIcon";
+ private static final String ATTR_APP_LABEL = "appLabel";
+ private static final String ATTR_ORIGINATING_URI = "originatingUri";
+ private static final String ATTR_ORIGINATING_UID = "originatingUid";
+ private static final String ATTR_REFERRER_URI = "referrerUri";
+ private static final String ATTR_ABI_OVERRIDE = "abiOverride";
+ private static final String ATTR_VOLUME_UUID = "volumeUuid";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_INSTALL_REASON = "installRason";
/** Automatically destroy sessions older than this */
private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
@@ -318,10 +357,8 @@
while ((type = in.next()) != END_DOCUMENT) {
if (type == START_TAG) {
final String tag = in.getName();
- if (PackageInstallerSession.TAG_SESSION.equals(tag)) {
- final PackageInstallerSession session = PackageInstallerSession.
- readFromXml(in, mInternalCallback, mContext, mPm,
- mInstallThread.getLooper(), mSessionsDir);
+ if (TAG_SESSION.equals(tag)) {
+ final PackageInstallerSession session = readSessionLocked(in);
final long age = System.currentTimeMillis() - session.createdMillis;
final boolean valid;
@@ -360,10 +397,53 @@
session.dump(pw);
mHistoricalSessions.add(writer.toString());
- int installerUid = session.getInstallerUid();
// Increment the number of sessions by this installerUid.
- mHistoricalSessionsByInstaller.put(installerUid,
- mHistoricalSessionsByInstaller.get(installerUid) + 1);
+ mHistoricalSessionsByInstaller.put(
+ session.installerUid,
+ mHistoricalSessionsByInstaller.get(session.installerUid) + 1);
+ }
+
+ private PackageInstallerSession readSessionLocked(XmlPullParser in) throws IOException,
+ XmlPullParserException {
+ final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
+ final int userId = readIntAttribute(in, ATTR_USER_ID);
+ final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
+ final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, mPm.getPackageUid(
+ installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
+ final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
+ final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
+ final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
+ final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
+ final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
+ final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
+
+ final SessionParams params = new SessionParams(
+ SessionParams.MODE_INVALID);
+ params.mode = readIntAttribute(in, ATTR_MODE);
+ params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
+ params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
+ params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
+ params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
+ params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
+ params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
+ params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
+ params.originatingUid =
+ readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
+ params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
+ params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
+ params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
+ params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
+ params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
+
+ final File appIconFile = buildAppIconFile(sessionId);
+ if (appIconFile.exists()) {
+ params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
+ params.appIconLastModified = appIconFile.lastModified();
+ }
+
+ return new PackageInstallerSession(mInternalCallback, mContext, mPm,
+ mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid,
+ params, createdMillis, stageDir, stageCid, prepared, sealed);
}
private void writeSessionsLocked() {
@@ -380,7 +460,7 @@
final int size = mSessions.size();
for (int i = 0; i < size; i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
- session.write(out, mSessionsDir);
+ writeSessionLocked(out, session);
}
out.endTag(null, TAG_SESSIONS);
out.endDocument();
@@ -393,6 +473,106 @@
}
}
+ private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
+ throws IOException {
+ final SessionParams params = session.params;
+
+ out.startTag(null, TAG_SESSION);
+
+ writeIntAttribute(out, ATTR_SESSION_ID, session.sessionId);
+ writeIntAttribute(out, ATTR_USER_ID, session.userId);
+ writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
+ session.installerPackageName);
+ writeIntAttribute(out, ATTR_INSTALLER_UID, session.installerUid);
+ writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
+ if (session.stageDir != null) {
+ writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
+ session.stageDir.getAbsolutePath());
+ }
+ if (session.stageCid != null) {
+ writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
+ }
+ writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared());
+ writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
+
+ writeIntAttribute(out, ATTR_MODE, params.mode);
+ writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
+ writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
+ writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
+ writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
+ writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
+ writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
+ writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
+ writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
+ writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
+ writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
+ writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
+
+ // Persist app icon if changed since last written
+ final File appIconFile = buildAppIconFile(session.sessionId);
+ if (params.appIcon == null && appIconFile.exists()) {
+ appIconFile.delete();
+ } else if (params.appIcon != null
+ && appIconFile.lastModified() != params.appIconLastModified) {
+ if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
+ FileOutputStream os = null;
+ try {
+ os = new FileOutputStream(appIconFile);
+ params.appIcon.compress(CompressFormat.PNG, 90, os);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
+ } finally {
+ IoUtils.closeQuietly(os);
+ }
+
+ params.appIconLastModified = appIconFile.lastModified();
+ }
+
+ writeGrantedRuntimePermissions(out, params.grantedRuntimePermissions);
+
+ out.endTag(null, TAG_SESSION);
+ }
+
+ private static void writeGrantedRuntimePermissions(XmlSerializer out,
+ String[] grantedRuntimePermissions) throws IOException {
+ if (grantedRuntimePermissions != null) {
+ for (String permission : grantedRuntimePermissions) {
+ out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
+ writeStringAttribute(out, ATTR_NAME, permission);
+ out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
+ }
+ }
+ }
+
+ private static String[] readGrantedRuntimePermissions(XmlPullParser in)
+ throws IOException, XmlPullParserException {
+ List<String> permissions = null;
+
+ final int outerDepth = in.getDepth();
+ int type;
+ while ((type = in.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
+ String permission = readStringAttribute(in, ATTR_NAME);
+ if (permissions == null) {
+ permissions = new ArrayList<>();
+ }
+ permissions.add(permission);
+ }
+ }
+
+ if (permissions == null) {
+ return null;
+ }
+
+ String[] permissionsArray = new String[permissions.size()];
+ permissions.toArray(permissionsArray);
+ return permissionsArray;
+ }
+
private File buildAppIconFile(int sessionId) {
return new File(mSessionsDir, "app_icon." + sessionId + ".png");
}
@@ -705,11 +885,9 @@
synchronized (mSessions) {
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
-
- SessionInfo info = session.generateInfo(false);
- if (Objects.equals(info.getInstallerPackageName(), installerPackageName)
+ if (Objects.equals(session.installerPackageName, installerPackageName)
&& session.userId == userId) {
- result.add(info);
+ result.add(session.generateInfo(false));
}
}
}
@@ -784,7 +962,7 @@
final int size = sessions.size();
for (int i = 0; i < size; i++) {
final PackageInstallerSession session = sessions.valueAt(i);
- if (session.getInstallerUid() == installerUid) {
+ if (session.installerUid == installerUid) {
count++;
}
}
@@ -796,7 +974,7 @@
if (callingUid == Process.ROOT_UID) {
return true;
} else {
- return (session != null) && (callingUid == session.getInstallerUid());
+ return (session != null) && (callingUid == session.installerUid);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0fd696f..5823771 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -25,22 +25,9 @@
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
-import static com.android.internal.util.XmlUtils.readBitmapAttribute;
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
-import static com.android.internal.util.XmlUtils.readStringAttribute;
-import static com.android.internal.util.XmlUtils.readUriAttribute;
-import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.internal.util.XmlUtils.writeUriAttribute;
import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
-import android.Manifest;
-import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
@@ -58,8 +45,6 @@
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.Signature;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.os.Binder;
import android.os.Bundle;
import android.os.FileBridge;
@@ -68,7 +53,6 @@
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
-import android.os.ParcelableException;
import android.os.Process;
import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
@@ -96,14 +80,9 @@
import libcore.io.IoUtils;
import libcore.io.Libcore;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileFilter;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.security.cert.Certificate;
import java.util.ArrayList;
@@ -118,34 +97,6 @@
private static final int MSG_COMMIT = 0;
- /** XML constants used for persisting a session */
- static final String TAG_SESSION = "session";
- private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
- private static final String ATTR_SESSION_ID = "sessionId";
- private static final String ATTR_USER_ID = "userId";
- private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
- private static final String ATTR_INSTALLER_UID = "installerUid";
- private static final String ATTR_CREATED_MILLIS = "createdMillis";
- private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
- private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
- private static final String ATTR_PREPARED = "prepared";
- private static final String ATTR_SEALED = "sealed";
- private static final String ATTR_MODE = "mode";
- private static final String ATTR_INSTALL_FLAGS = "installFlags";
- private static final String ATTR_INSTALL_LOCATION = "installLocation";
- private static final String ATTR_SIZE_BYTES = "sizeBytes";
- private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
- @Deprecated
- private static final String ATTR_APP_ICON = "appIcon";
- private static final String ATTR_APP_LABEL = "appLabel";
- private static final String ATTR_ORIGINATING_URI = "originatingUri";
- private static final String ATTR_ORIGINATING_UID = "originatingUid";
- private static final String ATTR_REFERRER_URI = "referrerUri";
- private static final String ATTR_ABI_OVERRIDE = "abiOverride";
- private static final String ATTR_VOLUME_UUID = "volumeUuid";
- private static final String ATTR_NAME = "name";
- private static final String ATTR_INSTALL_REASON = "installRason";
-
// TODO: enforce INSTALL_ALLOW_TEST
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
@@ -153,9 +104,12 @@
private final Context mContext;
private final PackageManagerService mPm;
private final Handler mHandler;
+ private final boolean mIsInstallerDeviceOwner;
final int sessionId;
final int userId;
+ final String installerPackageName;
+ final int installerUid;
final SessionParams params;
final long createdMillis;
final int defaultContainerGid;
@@ -168,17 +122,6 @@
private final Object mLock = new Object();
- /** Uid of the creator of this session. */
- private final int mOriginalInstallerUid;
-
- /** Package of the owner of the installer session */
- @GuardedBy("mLock")
- private String mInstallerPackageName;
-
- /** Uid of the owner of the installer session */
- @GuardedBy("mLock")
- private int mInstallerUid;
-
@GuardedBy("mLock")
private float mClientProgress = 0;
@GuardedBy("mLock")
@@ -189,25 +132,18 @@
@GuardedBy("mLock")
private float mReportedProgress = -1;
- /** State of the session. */
@GuardedBy("mLock")
private boolean mPrepared = false;
@GuardedBy("mLock")
private boolean mSealed = false;
@GuardedBy("mLock")
- private boolean mCommitted = false;
+ private boolean mPermissionsAccepted = false;
@GuardedBy("mLock")
private boolean mRelinquished = false;
@GuardedBy("mLock")
private boolean mDestroyed = false;
- /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
- @GuardedBy("mLock")
- private boolean mPermissionsManuallyAccepted = false;
-
- @GuardedBy("mLock")
private int mFinalStatus;
- @GuardedBy("mLock")
private String mFinalMessage;
@GuardedBy("mLock")
@@ -219,13 +155,9 @@
private IPackageInstallObserver2 mRemoteObserver;
/** Fields derived from commit parsing */
- @GuardedBy("mLock")
private String mPackageName;
- @GuardedBy("mLock")
private int mVersionCode;
- @GuardedBy("mLock")
private Signature[] mSignatures;
- @GuardedBy("mLock")
private Certificate[][] mCertificates;
/**
@@ -273,61 +205,32 @@
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
+ // Cache package manager data without the lock held
+ final PackageInfo pkgInfo = mPm.getPackageInfo(
+ params.appPackageName, PackageManager.GET_SIGNATURES
+ | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
+ final ApplicationInfo appInfo = mPm.getApplicationInfo(
+ params.appPackageName, 0, userId);
+
synchronized (mLock) {
if (msg.obj != null) {
mRemoteObserver = (IPackageInstallObserver2) msg.obj;
}
+
try {
- commitLocked();
+ commitLocked(pkgInfo, appInfo);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
destroyInternal();
dispatchSessionFinished(e.error, completeMsg, null);
}
- }
- return true;
+ return true;
+ }
}
};
- /**
- * @return {@code true} iff the installing is app an device owner?
- */
- private boolean isInstallerDeviceOwnerLocked() {
- DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
-
- return (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
- mInstallerPackageName);
- }
-
- /**
- * Checks if the permissions still need to be confirmed.
- *
- * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
- * installer might still {@link #transfer(String) change}.
- *
- * @return {@code true} iff we need to ask to confirm the permissions?
- */
- private boolean needToAskForPermissionsLocked() {
- if (mPermissionsManuallyAccepted) {
- return false;
- }
-
- final boolean isPermissionGranted =
- (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
- mInstallerUid) == PackageManager.PERMISSION_GRANTED);
- final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
- final boolean forcePermissionPrompt =
- (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
-
- // Device owners are allowed to silently install packages, so the permission check is
- // waived if the installer is the device owner.
- return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
- || isInstallerDeviceOwnerLocked());
- }
-
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
String installerPackageName, int installerUid, SessionParams params, long createdMillis,
@@ -339,9 +242,8 @@
this.sessionId = sessionId;
this.userId = userId;
- mOriginalInstallerUid = installerUid;
- mInstallerPackageName = installerPackageName;
- mInstallerUid = installerUid;
+ this.installerPackageName = installerPackageName;
+ this.installerUid = installerUid;
this.params = params;
this.createdMillis = createdMillis;
this.stageDir = stageDir;
@@ -355,6 +257,26 @@
mPrepared = prepared;
mSealed = sealed;
+ // Device owners are allowed to silently install packages, so the permission check is
+ // waived if the installer is the device owner.
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ final boolean isPermissionGranted =
+ (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
+ == PackageManager.PERMISSION_GRANTED);
+ final boolean isInstallerRoot = (installerUid == Process.ROOT_UID);
+ final boolean forcePermissionPrompt =
+ (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
+ mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
+ installerPackageName);
+ if ((isPermissionGranted
+ || isInstallerRoot
+ || mIsInstallerDeviceOwner)
+ && !forcePermissionPrompt) {
+ mPermissionsAccepted = true;
+ } else {
+ mPermissionsAccepted = false;
+ }
final long identity = Binder.clearCallingIdentity();
try {
final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
@@ -373,7 +295,7 @@
final SessionInfo info = new SessionInfo();
synchronized (mLock) {
info.sessionId = sessionId;
- info.installerPackageName = mInstallerPackageName;
+ info.installerPackageName = installerPackageName;
info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
mResolvedBaseFile.getAbsolutePath() : null;
info.progress = mProgress;
@@ -388,13 +310,6 @@
info.appIcon = params.appIcon;
}
info.appLabel = params.appLabel;
-
- info.installLocation = params.installLocation;
- info.originatingUri = params.originatingUri;
- info.originatingUid = params.originatingUid;
- info.referrerUri = params.referrerUri;
- info.grantedRuntimePermissions = params.grantedRuntimePermissions;
- info.installFlags = params.installFlags;
}
return info;
}
@@ -411,26 +326,14 @@
}
}
- private void assertPreparedAndNotSealedLocked(String cookie) {
- assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
- if (mSealed) {
- throw new SecurityException(cookie + " not allowed after sealing");
- }
- }
-
- private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
- assertPreparedAndNotDestroyedLocked(cookie);
- if (mCommitted) {
- throw new SecurityException(cookie + " not allowed after commit");
- }
- }
-
- private void assertPreparedAndNotDestroyedLocked(String cookie) {
- if (!mPrepared) {
- throw new IllegalStateException(cookie + " before prepared");
- }
- if (mDestroyed) {
- throw new SecurityException(cookie + " not allowed after destruction");
+ private void assertPreparedAndNotSealed(String cookie) {
+ synchronized (mLock) {
+ if (!mPrepared) {
+ throw new IllegalStateException(cookie + " before prepared");
+ }
+ if (mSealed) {
+ throw new SecurityException(cookie + " not allowed after commit");
+ }
}
}
@@ -439,27 +342,27 @@
* might point at an ASEC mount point, which is why we delay path resolution
* until someone actively works with the session.
*/
- private File resolveStageDirLocked() throws IOException {
- if (mResolvedStageDir == null) {
- if (stageDir != null) {
- mResolvedStageDir = stageDir;
- } else {
- final String path = PackageHelper.getSdDir(stageCid);
- if (path != null) {
- mResolvedStageDir = new File(path);
+ private File resolveStageDir() throws IOException {
+ synchronized (mLock) {
+ if (mResolvedStageDir == null) {
+ if (stageDir != null) {
+ mResolvedStageDir = stageDir;
} else {
- throw new IOException("Failed to resolve path to container " + stageCid);
+ final String path = PackageHelper.getSdDir(stageCid);
+ if (path != null) {
+ mResolvedStageDir = new File(path);
+ } else {
+ throw new IOException("Failed to resolve path to container " + stageCid);
+ }
}
}
+ return mResolvedStageDir;
}
- return mResolvedStageDir;
}
@Override
public void setClientProgress(float progress) {
synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
-
// Always publish first staging movement
final boolean forcePublish = (mClientProgress == 0);
mClientProgress = progress;
@@ -470,8 +373,6 @@
@Override
public void addClientProgress(float progress) {
synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
-
setClientProgress(mClientProgress + progress);
}
}
@@ -489,15 +390,11 @@
@Override
public String[] getNames() {
- synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
- assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
-
- try {
- return resolveStageDirLocked().list();
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
- }
+ assertPreparedAndNotSealed("getNames");
+ try {
+ return resolveStageDir().list();
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
}
}
@@ -506,26 +403,20 @@
if (TextUtils.isEmpty(params.appPackageName)) {
throw new IllegalStateException("Must specify package name to remove a split");
}
-
- synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
- assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit");
-
- try {
- createRemoveSplitMarkerLocked(splitName);
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
- }
+ try {
+ createRemoveSplitMarker(splitName);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
}
}
- private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
+ private void createRemoveSplitMarker(String splitName) throws IOException {
try {
final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;
if (!FileUtils.isValidExtFilename(markerName)) {
throw new IllegalArgumentException("Invalid marker: " + markerName);
}
- final File target = new File(resolveStageDirLocked(), markerName);
+ final File target = new File(resolveStageDir(), markerName);
target.createNewFile();
Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
} catch (ErrnoException e) {
@@ -549,10 +440,8 @@
// will block any attempted install transitions.
final RevocableFileDescriptor fd;
final FileBridge bridge;
- final File stageDir;
synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
- assertPreparedAndNotSealedLocked("openWrite");
+ assertPreparedAndNotSealed("openWrite");
if (PackageInstaller.ENABLE_REVOCABLE_FD) {
fd = new RevocableFileDescriptor();
@@ -563,8 +452,6 @@
bridge = new FileBridge();
mBridges.add(bridge);
}
-
- stageDir = resolveStageDirLocked();
}
try {
@@ -575,7 +462,7 @@
final File target;
final long identity = Binder.clearCallingIdentity();
try {
- target = new File(stageDir, name);
+ target = new File(resolveStageDir(), name);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -613,108 +500,55 @@
@Override
public ParcelFileDescriptor openRead(String name) {
- synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
- assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
- try {
- return openReadInternalLocked(name);
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
- }
+ try {
+ return openReadInternal(name);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
}
}
- private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException {
+ private ParcelFileDescriptor openReadInternal(String name) throws IOException {
+ assertPreparedAndNotSealed("openRead");
+
try {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
- final File target = new File(resolveStageDirLocked(), name);
+ final File target = new File(resolveStageDir(), name);
+
final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
return new ParcelFileDescriptor(targetFd);
+
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
}
- /**
- * Check if the caller is the owner of this session. Otherwise throw a
- * {@link SecurityException}.
- */
- private void assertCallerIsOwnerOrRootLocked() {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
- throw new SecurityException("Session does not belong to uid " + callingUid);
- }
- }
-
- /**
- * If anybody is reading or writing data of the session, throw an {@link SecurityException}.
- */
- private void assertNoWriteFileTransfersOpenLocked() {
- // Verify that all writers are hands-off
- for (RevocableFileDescriptor fd : mFds) {
- if (!fd.isRevoked()) {
- throw new SecurityException("Files still open");
- }
- }
- for (FileBridge bridge : mBridges) {
- if (!bridge.isClosed()) {
- throw new SecurityException("Files still open");
- }
- }
- }
-
@Override
- public void commit(IntentSender statusReceiver, boolean forTransfer) {
+ public void commit(IntentSender statusReceiver) {
Preconditions.checkNotNull(statusReceiver);
- // Cache package manager data without the lock held
- final PackageInfo installedPkgInfo = mPm.getPackageInfo(
- params.appPackageName, PackageManager.GET_SIGNATURES
- | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
-
final boolean wasSealed;
synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
- assertPreparedAndNotDestroyedLocked("commit");
-
- if (forTransfer) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
-
- if (mInstallerUid == mOriginalInstallerUid) {
- throw new IllegalArgumentException("Session has not been transferred");
- }
- } else {
- if (mInstallerUid != mOriginalInstallerUid) {
- throw new IllegalArgumentException("Session has been transferred");
- }
- }
-
wasSealed = mSealed;
if (!mSealed) {
- try {
- sealAndValidateLocked(installedPkgInfo);
- } catch (PackageManagerException e) {
- // Do now throw an exception here to stay compatible with O and older
- destroyInternal();
- dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
- return;
+ // Verify that all writers are hands-off
+ for (RevocableFileDescriptor fd : mFds) {
+ if (!fd.isRevoked()) {
+ throw new SecurityException("Files still open");
+ }
}
+ for (FileBridge bridge : mBridges) {
+ if (!bridge.isClosed()) {
+ throw new SecurityException("Files still open");
+ }
+ }
+ mSealed = true;
}
// Client staging is fully done at this point
mClientProgress = 1f;
computeProgressLocked(true);
-
- // This ongoing commit should keep session active, even though client
- // will probably close their end.
- mActiveCount.incrementAndGet();
-
- mCommitted = true;
- final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
- mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId);
- mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
if (!wasSealed) {
@@ -723,82 +557,17 @@
// the session lock, since otherwise it's a lock inversion.
mCallback.onSessionSealedBlocking(this);
}
+
+ // This ongoing commit should keep session active, even though client
+ // will probably close their end.
+ mActiveCount.incrementAndGet();
+
+ final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
+ statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
+ mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
- /**
- * Seal the session to prevent further modification and validate the contents of it.
- *
- * <p>The session will be sealed after calling this method even if it failed.
- *
- * @param pkgInfo The package info for {@link #params}.packagename
- */
- private void sealAndValidateLocked(PackageInfo pkgInfo)
- throws PackageManagerException {
- assertNoWriteFileTransfersOpenLocked();
-
- mSealed = true;
-
- // Verify that stage looks sane with respect to existing application.
- // This currently only ensures packageName, versionCode, and certificate
- // consistency.
- validateInstallLocked(pkgInfo);
-
- // Read transfers from the original owner stay open, but as the session's data
- // cannot be modified anymore, there is no leak of information.
- }
-
- @Override
- public void transfer(String packageName) {
- Preconditions.checkNotNull(packageName);
-
- ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
- if (newOwnerAppInfo == null) {
- throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
- }
-
- if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
- Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) {
- throw new SecurityException("Destination package " + packageName + " does not have "
- + "the " + Manifest.permission.INSTALL_PACKAGES + " permission");
- }
-
- // Cache package manager data without the lock held
- final PackageInfo installedPkgInfo = mPm.getPackageInfo(
- params.appPackageName, PackageManager.GET_SIGNATURES
- | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
-
- // Only install flags that can be verified by the app the session is transferred to are
- // allowed. The parameters can be read via PackageInstaller.SessionInfo.
- if (!params.areHiddenOptionsSet()) {
- throw new SecurityException("Can only transfer sessions that use public options");
- }
-
- synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
- assertPreparedAndNotSealedLocked("transfer");
-
- try {
- sealAndValidateLocked(installedPkgInfo);
- } catch (PackageManagerException e) {
- throw new IllegalArgumentException("Package is not valid", e);
- }
-
- if (!mPackageName.equals(mInstallerPackageName)) {
- throw new SecurityException("Can only transfer sessions that update the original "
- + "installer");
- }
-
- mInstallerPackageName = packageName;
- mInstallerUid = newOwnerAppInfo.uid;
- }
-
- // Persist the fact that we've sealed ourselves to prevent
- // mutations of any hard links we create. We do this without holding
- // the session lock, since otherwise it's a lock inversion.
- mCallback.onSessionSealedBlocking(this);
- }
-
- private void commitLocked()
+ private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
throws PackageManagerException {
if (mDestroyed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
@@ -808,17 +577,22 @@
}
try {
- resolveStageDirLocked();
+ resolveStageDir();
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to resolve stage location", e);
}
+ // Verify that stage looks sane with respect to existing application.
+ // This currently only ensures packageName, versionCode, and certificate
+ // consistency.
+ validateInstallLocked(pkgInfo, appInfo);
+
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSignatures);
Preconditions.checkNotNull(mResolvedBaseFile);
- if (needToAskForPermissionsLocked()) {
+ if (!mPermissionsAccepted) {
// User needs to accept permissions; give installer an intent they
// can use to involve user.
final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
@@ -848,7 +622,7 @@
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
try {
final List<File> fromFiles = mResolvedInheritedFiles;
- final File toDir = resolveStageDirLocked();
+ final File toDir = resolveStageDir();
if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
@@ -909,7 +683,7 @@
mRelinquished = true;
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
- mInstallerPackageName, mInstallerUid, user, mCertificates);
+ installerPackageName, installerUid, user, mCertificates);
}
/**
@@ -924,7 +698,8 @@
* Note that upgrade compatibility is still performed by
* {@link PackageManagerService}.
*/
- private void validateInstallLocked(PackageInfo pkgInfo) throws PackageManagerException {
+ private void validateInstallLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
+ throws PackageManagerException {
mPackageName = null;
mVersionCode = -1;
mSignatures = null;
@@ -977,7 +752,7 @@
mCertificates = apk.certificates;
}
- assertApkConsistentLocked(String.valueOf(addedFile), apk);
+ assertApkConsistent(String.valueOf(addedFile), apk);
// Take this opportunity to enforce uniform naming
final String targetName;
@@ -1032,14 +807,13 @@
} else {
// Partial installs must be consistent with existing install
- if (pkgInfo == null || pkgInfo.applicationInfo == null) {
+ if (appInfo == null) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Missing existing base package for " + mPackageName);
}
final PackageLite existing;
final ApkLite existingBase;
- ApplicationInfo appInfo = pkgInfo.applicationInfo;
try {
existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
@@ -1048,7 +822,7 @@
throw PackageManagerException.from(e);
}
- assertApkConsistentLocked("Existing base", existingBase);
+ assertApkConsistent("Existing base", existingBase);
// Inherit base if not overridden
if (mResolvedBaseFile == null) {
@@ -1104,7 +878,7 @@
}
}
- private void assertApkConsistentLocked(String tag, ApkLite apk)
+ private void assertApkConsistent(String tag, ApkLite apk)
throws PackageManagerException {
if (!mPackageName.equals(apk.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
@@ -1185,15 +959,6 @@
return true;
}
- /**
- * @return the uid of the owner this session
- */
- public int getInstallerUid() {
- synchronized (mLock) {
- return mInstallerUid;
- }
- }
-
private static String getRelativePath(File file, File base) throws IOException {
final String pathStr = file.getAbsolutePath();
final String baseStr = base.getAbsolutePath();
@@ -1341,9 +1106,9 @@
if (accepted) {
// Mark and kick off another install pass
synchronized (mLock) {
- mPermissionsManuallyAccepted = true;
- mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
+ mPermissionsAccepted = true;
}
+ mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
} else {
destroyInternal();
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
@@ -1355,9 +1120,7 @@
mCallback.onSessionActiveChanged(this, true);
}
- boolean wasPrepared;
synchronized (mLock) {
- wasPrepared = mPrepared;
if (!mPrepared) {
if (stageDir != null) {
prepareStageDir(stageDir);
@@ -1378,20 +1141,13 @@
}
mPrepared = true;
+ mCallback.onSessionPrepared(this);
}
}
-
- if (!wasPrepared) {
- mCallback.onSessionPrepared(this);
- }
}
@Override
public void close() {
- synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
- }
-
if (mActiveCount.decrementAndGet() == 0) {
mCallback.onSessionActiveChanged(this, false);
}
@@ -1399,33 +1155,21 @@
@Override
public void abandon() {
- synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
-
- if (mRelinquished) {
- Slog.d(TAG, "Ignoring abandon after commit relinquished control");
- return;
- }
- destroyInternal();
+ if (mRelinquished) {
+ Slog.d(TAG, "Ignoring abandon after commit relinquished control");
+ return;
}
-
+ destroyInternal();
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
}
private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
- IPackageInstallObserver2 observer;
- String packageName;
- synchronized (mLock) {
- mFinalStatus = returnCode;
- mFinalMessage = msg;
+ mFinalStatus = returnCode;
+ mFinalMessage = msg;
- observer = mRemoteObserver;
- packageName = mPackageName;
- }
-
- if (observer != null) {
+ if (mRemoteObserver != null) {
try {
- observer.onPackageInstalled(packageName, returnCode, msg, extras);
+ mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);
} catch (RemoteException ignored) {
}
}
@@ -1476,9 +1220,8 @@
pw.increaseIndent();
pw.printPair("userId", userId);
- pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
- pw.printPair("mInstallerPackageName", mInstallerPackageName);
- pw.printPair("mInstallerUid", mInstallerUid);
+ pw.printPair("installerPackageName", installerPackageName);
+ pw.printPair("installerUid", installerUid);
pw.printPair("createdMillis", createdMillis);
pw.printPair("stageDir", stageDir);
pw.printPair("stageCid", stageCid);
@@ -1489,7 +1232,7 @@
pw.printPair("mClientProgress", mClientProgress);
pw.printPair("mProgress", mProgress);
pw.printPair("mSealed", mSealed);
- pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
+ pw.printPair("mPermissionsAccepted", mPermissionsAccepted);
pw.printPair("mRelinquished", mRelinquished);
pw.printPair("mDestroyed", mDestroyed);
pw.printPair("mFds", mFds.size());
@@ -1500,170 +1243,4 @@
pw.decreaseIndent();
}
-
- private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out,
- String[] grantedRuntimePermissions) throws IOException {
- if (grantedRuntimePermissions != null) {
- for (String permission : grantedRuntimePermissions) {
- out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
- writeStringAttribute(out, ATTR_NAME, permission);
- out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
- }
- }
- }
-
- private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
- return new File(sessionsDir, "app_icon." + sessionId + ".png");
- }
-
- /**
- * Write this session to a {@link XmlSerializer}.
- *
- * @param out Where to write the session to
- * @param sessionsDir The directory containing the sessions
- */
- void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
- synchronized (mLock) {
- out.startTag(null, TAG_SESSION);
-
- writeIntAttribute(out, ATTR_SESSION_ID, sessionId);
- writeIntAttribute(out, ATTR_USER_ID, userId);
- writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
- mInstallerPackageName);
- writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid);
- writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis);
- if (stageDir != null) {
- writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
- stageDir.getAbsolutePath());
- }
- if (stageCid != null) {
- writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
- }
- writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
- writeBooleanAttribute(out, ATTR_SEALED, isSealed());
-
- writeIntAttribute(out, ATTR_MODE, params.mode);
- writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
- writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
- writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
- writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
- writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
- writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
- writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
- writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
- writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
- writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
- writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
-
- // Persist app icon if changed since last written
- File appIconFile = buildAppIconFile(sessionId, sessionsDir);
- if (params.appIcon == null && appIconFile.exists()) {
- appIconFile.delete();
- } else if (params.appIcon != null
- && appIconFile.lastModified() != params.appIconLastModified) {
- if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
- FileOutputStream os = null;
- try {
- os = new FileOutputStream(appIconFile);
- params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
- } finally {
- IoUtils.closeQuietly(os);
- }
-
- params.appIconLastModified = appIconFile.lastModified();
- }
-
- writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
- }
-
- out.endTag(null, TAG_SESSION);
- }
-
- private static String[] readGrantedRuntimePermissions(XmlPullParser in)
- throws IOException, XmlPullParserException {
- List<String> permissions = null;
-
- final int outerDepth = in.getDepth();
- int type;
- while ((type = in.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
- String permission = readStringAttribute(in, ATTR_NAME);
- if (permissions == null) {
- permissions = new ArrayList<>();
- }
- permissions.add(permission);
- }
- }
-
- if (permissions == null) {
- return null;
- }
-
- String[] permissionsArray = new String[permissions.size()];
- permissions.toArray(permissionsArray);
- return permissionsArray;
- }
-
- /**
- * Read new session from a {@link XmlPullParser xml description} and create it.
- *
- * @param in The source of the description
- * @param callback Callback the session uses to notify about changes of it's state
- * @param context Context to be used by the session
- * @param pm PackageManager to use by the session
- * @param installerThread Thread to be used for callbacks of this session
- * @param sessionsDir The directory the sessions are stored in
- *
- * @return The newly created session
- */
- public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
- @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
- @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir)
- throws IOException, XmlPullParserException {
- final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
- final int userId = readIntAttribute(in, ATTR_USER_ID);
- final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
- final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid(
- installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
- final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
- final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
- final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
- final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
- final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
- final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
-
- final SessionParams params = new SessionParams(
- SessionParams.MODE_INVALID);
- params.mode = readIntAttribute(in, ATTR_MODE);
- params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
- params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
- params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
- params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
- params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
- params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
- params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
- params.originatingUid =
- readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
- params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
- params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
- params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
- params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
- params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
-
- final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
- if (appIconFile.exists()) {
- params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
- params.appIconLastModified = appIconFile.lastModified();
- }
-
- return new PackageInstallerSession(callback, context, pm,
- installerThread, sessionId, userId, installerPackageName, installerUid,
- params, createdMillis, stageDir, stageCid, prepared, sealed);
- }
}