diff options
| author | 2017-08-08 19:49:13 +0000 | |
|---|---|---|
| committer | 2017-08-08 19:49:13 +0000 | |
| commit | 7bec053dffb90f7cd0e39eec7f56fb33d847c3eb (patch) | |
| tree | b76ba5f3dd52945d2477eab90b82ef3332b410ec | |
| parent | a5a660a84d2e220f70b748cbbe255b8c018e3753 (diff) | |
| parent | 9890f8b426550485aaab164a7bedbcd545862b85 (diff) | |
Merge "Revert session-transfer change" into oc-mr1-dev
| -rw-r--r-- | api/current.txt | 7 | ||||
| -rw-r--r-- | api/system-current.txt | 15 | ||||
| -rw-r--r-- | api/test-current.txt | 7 | ||||
| -rw-r--r-- | core/java/android/content/pm/IPackageInstallerSession.aidl | 3 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageInstaller.java | 237 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/PackageInstallerService.java | 212 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/PackageInstallerSession.java | 681 |
7 files changed, 327 insertions, 835 deletions
diff --git a/api/current.txt b/api/current.txt index ec6215856fdd..6fa840ecb08e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10506,7 +10506,6 @@ package android.content.pm { 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 @@ package android.content.pm { 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 f2d2b34249e1..c102ccc3fc5a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -11180,14 +11180,12 @@ package android.content.pm { 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 @@ package android.content.pm { 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 c80c3641c316..630ca8e9f566 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -10544,7 +10544,6 @@ package android.content.pm { 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 @@ package android.content.pm { 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 0b16852246f8..2a3fac341e24 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -32,7 +32,6 @@ interface IPackageInstallerSession { 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 f4fdcaa44836..c3ebf554ea8c 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -38,7 +38,6 @@ import android.os.Message; 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 @@ public class PackageInstaller { * @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 class PackageInstaller { */ 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 @@ public class PackageInstaller { } /** - * 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 class PackageInstaller { 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 @@ public class PackageInstaller { 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 @@ public class PackageInstaller { 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 @@ public class PackageInstaller { 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 c3b93b428cb5..bab70117659a 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.PackageManager; 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.SparseBooleanArray; 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.ImageUtils; 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { /** 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } } + 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 0fd696fbcd90..58237713d793 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_CREAT; 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.ApkLite; 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.Handler; 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 com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapt 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void addClientProgress(float progress) { synchronized (mLock) { - assertCallerIsOwnerOrRootLocked(); - setClientProgress(mClientProgress + progress); } } @@ -489,15 +390,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { bridge = new FileBridge(); mBridges.add(bridge); } - - stageDir = resolveStageDirLocked(); } try { @@ -575,7 +462,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // the session lock, since otherwise it's a lock inversion. mCallback.onSessionSealedBlocking(this); } - } - /** - * 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); + // This ongoing commit should keep session active, even though client + // will probably close their end. + mActiveCount.incrementAndGet(); - // Read transfers from the original owner stay open, but as the session's data - // cannot be modified anymore, there is no leak of information. + final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext, + statusReceiver, sessionId, mIsInstallerDeviceOwner, userId); + mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget(); } - @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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mRelinquished = true; mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, - mInstallerPackageName, mInstallerUid, user, mCertificates); + installerPackageName, installerUid, user, mCertificates); } /** @@ -924,7 +698,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw PackageManagerException.from(e); } - assertApkConsistentLocked("Existing base", existingBase); + assertApkConsistent("Existing base", existingBase); // Inherit base if not overridden if (mResolvedBaseFile == null) { @@ -1104,7 +878,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mCallback.onSessionActiveChanged(this, true); } - boolean wasPrepared; synchronized (mLock) { - wasPrepared = mPrepared; if (!mPrepared) { if (stageDir != null) { prepareStageDir(stageDir); @@ -1378,20 +1141,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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); - } } |