summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2014-07-12 18:09:46 -0700
committerJeff Sharkey <jsharkey@android.com>2014-07-12 21:12:25 -0700
commita10311434778ea1be1621c2251c0c8c2966f337b (patch)
tree3496202e354ae761d7c750e105c610f67f82542d
parente0b0bef75b66f0a87039c8f58c17b1596a2baebe (diff)
downloadframeworks_base-a10311434778ea1be1621c2251c0c8c2966f337b.zip
frameworks_base-a10311434778ea1be1621c2251c0c8c2966f337b.tar.gz
frameworks_base-a10311434778ea1be1621c2251c0c8c2966f337b.tar.bz2
Package installation listener events.
Flesh out implementation of install session observers. Carve out 20% of published install progress for final system operations such as dexopt, etc. Add dumpsys output for active install sessions. Create explicit fsync() instead of overriding meaning of flush(). Hack to throw IOExceptions over Binder calls. Bug: 14975160, 15348430 Change-Id: I874457e40c45d2661bc0a526df9285ffea4bb77c
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java13
-rw-r--r--core/java/android/content/pm/IPackageInstallerSession.aidl3
-rw-r--r--core/java/android/content/pm/InstallSessionParams.java27
-rw-r--r--core/java/android/content/pm/PackageInstaller.java61
-rw-r--r--core/java/android/os/FileBridge.java4
-rw-r--r--core/java/android/util/ExceptionUtils.java42
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java127
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java87
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java25
9 files changed, 313 insertions, 76 deletions
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 6cca03b..3d0eec4 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -31,7 +31,7 @@ import android.content.pm.InstallSessionParams;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.InstallResultCallback;
+import android.content.pm.PackageInstaller.CommitResultCallback;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
@@ -769,7 +769,7 @@ public final class Pm {
}
}
- class LocalInstallResultCallback extends InstallResultCallback {
+ class LocalCommitResultCallback extends CommitResultCallback {
boolean finished;
boolean success;
String msg;
@@ -1018,6 +1018,7 @@ public final class Pm {
final InstallSessionParams params = new InstallSessionParams();
params.installFlags = PackageManager.INSTALL_ALL_USERS;
params.fullInstall = true;
+ params.progressMax = -1;
String opt;
while ((opt = nextOption()) != null) {
@@ -1042,6 +1043,7 @@ public final class Pm {
params.fullInstall = false;
} else if (opt.equals("-S")) {
params.deltaSize = Long.parseLong(nextOptionData());
+ params.progressMax = (int) params.deltaSize;
} else {
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -1093,7 +1095,8 @@ public final class Pm {
out = session.openWrite(splitName, 0, sizeBytes);
final int n = Streams.copy(in, out);
- out.flush();
+ session.fsync(out);
+ session.addProgress(n);
System.out.println("Success: streamed " + n + " bytes");
} finally {
@@ -1110,8 +1113,8 @@ public final class Pm {
try {
session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
- final LocalInstallResultCallback callback = new LocalInstallResultCallback();
- session.install(callback);
+ final LocalCommitResultCallback callback = new LocalCommitResultCallback();
+ session.commit(callback);
synchronized (callback) {
while (!callback.finished) {
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index f881acd..d6775d4 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -21,7 +21,8 @@ import android.os.ParcelFileDescriptor;
/** {@hide} */
interface IPackageInstallerSession {
- void updateProgress(int progress);
+ void setClientProgress(int progress);
+ void addClientProgress(int progress);
ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);
diff --git a/core/java/android/content/pm/InstallSessionParams.java b/core/java/android/content/pm/InstallSessionParams.java
index f683523..43e3314 100644
--- a/core/java/android/content/pm/InstallSessionParams.java
+++ b/core/java/android/content/pm/InstallSessionParams.java
@@ -21,6 +21,8 @@ import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.IndentingPrintWriter;
+
/** {@hide} */
public class InstallSessionParams implements Parcelable {
@@ -37,6 +39,8 @@ public class InstallSessionParams implements Parcelable {
/** {@hide} */
public long deltaSize = -1;
/** {@hide} */
+ public int progressMax = 100;
+ /** {@hide} */
public String packageName;
/** {@hide} */
public Bitmap icon;
@@ -59,6 +63,7 @@ public class InstallSessionParams implements Parcelable {
installLocation = source.readInt();
signatures = (Signature[]) source.readParcelableArray(null);
deltaSize = source.readLong();
+ progressMax = source.readInt();
packageName = source.readString();
icon = source.readParcelable(null);
title = source.readString();
@@ -87,6 +92,10 @@ public class InstallSessionParams implements Parcelable {
this.deltaSize = deltaSize;
}
+ public void setProgressMax(int progressMax) {
+ this.progressMax = progressMax;
+ }
+
public void setPackageName(String packageName) {
this.packageName = packageName;
}
@@ -107,6 +116,23 @@ public class InstallSessionParams implements Parcelable {
this.referrerUri = referrerUri;
}
+ /** {@hide} */
+ public void dump(IndentingPrintWriter pw) {
+ pw.printPair("fullInstall", fullInstall);
+ pw.printHexPair("installFlags", installFlags);
+ pw.printPair("installLocation", installLocation);
+ pw.printPair("signatures", (signatures != null));
+ pw.printPair("deltaSize", deltaSize);
+ pw.printPair("progressMax", progressMax);
+ pw.printPair("packageName", packageName);
+ pw.printPair("icon", (icon != null));
+ pw.printPair("title", title);
+ pw.printPair("originatingUri", originatingUri);
+ pw.printPair("referrerUri", referrerUri);
+ pw.printPair("abiOverride", abiOverride);
+ pw.println();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -119,6 +145,7 @@ public class InstallSessionParams implements Parcelable {
dest.writeInt(installLocation);
dest.writeParcelableArray(signatures, flags);
dest.writeLong(deltaSize);
+ dest.writeInt(progressMax);
dest.writeString(packageName);
dest.writeParcelable(icon, flags);
dest.writeString(title != null ? title.toString() : null);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 9d756f7..401be06 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -23,8 +23,10 @@ import android.os.Bundle;
import android.os.FileBridge;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.ExceptionUtils;
import java.io.Closeable;
+import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
@@ -64,9 +66,12 @@ public class PackageInstaller {
observer.packageInstalled(packageName, null, returnCode);
}
- public int createSession(InstallSessionParams params) {
+ public int createSession(InstallSessionParams params) throws IOException {
try {
return mInstaller.createSession(mInstallerPackageName, params, mUserId);
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -136,8 +141,7 @@ public class PackageInstaller {
public abstract void onFinished(int sessionId, boolean success);
}
- public void registerObserver(SessionObserver observer) {
- // TODO: consider restricting to current launcher app
+ public void registerSessionObserver(SessionObserver observer) {
try {
mInstaller.registerObserver(observer.getBinder(), mUserId);
} catch (RemoteException e) {
@@ -145,7 +149,7 @@ public class PackageInstaller {
}
}
- public void unregisterObserver(SessionObserver observer) {
+ public void unregisterSessionObserver(SessionObserver observer) {
try {
mInstaller.unregisterObserver(observer.getBinder(), mUserId);
} catch (RemoteException e) {
@@ -173,9 +177,18 @@ public class PackageInstaller {
mSession = session;
}
- public void updateProgress(int progress) {
+ public void setProgress(int progress) {
try {
- mSession.updateProgress(progress);
+ mSession.setClientProgress(progress);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void addProgress(int progress) {
+ try {
+ mSession.addClientProgress(progress);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -187,19 +200,31 @@ public class PackageInstaller {
* {@link OutputStream#flush()} to ensure bytes have been written to
* disk.
*/
- public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes) {
+ public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes)
+ throws IOException {
try {
final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName,
offsetBytes, lengthBytes);
return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor());
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
} catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
+ throw new IOException(e);
}
}
- public void install(InstallResultCallback callback) {
+ public void fsync(OutputStream out) throws IOException {
+ if (out instanceof FileBridge.FileBridgeOutputStream) {
+ ((FileBridge.FileBridgeOutputStream) out).fsync();
+ } else {
+ throw new IllegalArgumentException("Unrecognized stream");
+ }
+ }
+
+ public void commit(CommitResultCallback callback) {
try {
- mSession.install(new InstallResultCallbackDelegate(callback).getBinder());
+ mSession.install(new CommitResultCallbackDelegate(callback).getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -246,16 +271,8 @@ public class PackageInstaller {
}
}
- public static abstract class InstallResultCallback {
- /**
- * The session installed successfully.
- */
+ public static abstract class CommitResultCallback {
public abstract void onSuccess();
-
- /**
- * General unclassified failure. You may be interested in overriding
- * more granular classifications.
- */
public abstract void onFailure(String msg);
/**
@@ -300,10 +317,10 @@ public class PackageInstaller {
}
}
- private static class InstallResultCallbackDelegate extends PackageInstallObserver {
- private final InstallResultCallback target;
+ private static class CommitResultCallbackDelegate extends PackageInstallObserver {
+ private final CommitResultCallback target;
- public InstallResultCallbackDelegate(InstallResultCallback target) {
+ public CommitResultCallbackDelegate(CommitResultCallback target) {
this.target = target;
}
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
index 691afdd..1e1ad9e 100644
--- a/core/java/android/os/FileBridge.java
+++ b/core/java/android/os/FileBridge.java
@@ -31,7 +31,6 @@ import libcore.io.Streams;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.SyncFailedException;
import java.nio.ByteOrder;
import java.util.Arrays;
@@ -144,8 +143,7 @@ public class FileBridge extends Thread {
}
}
- @Override
- public void flush() throws IOException {
+ public void fsync() throws IOException {
writeCommandAndBlock(CMD_FSYNC, "fsync()");
}
diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
new file mode 100644
index 0000000..6aae84d
--- /dev/null
+++ b/core/java/android/util/ExceptionUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.io.IOException;
+
+/**
+ * Utility methods for proxying richer exceptions across Binder calls.
+ *
+ * @hide
+ */
+public class ExceptionUtils {
+ // TODO: longer term these should be replaced with first-class
+ // Parcel.read/writeException() and AIDL support, but for now do this using
+ // a nasty hack.
+
+ private static final String PREFIX_IO = "\u2603";
+
+ public static RuntimeException wrap(IOException e) {
+ throw new IllegalStateException(PREFIX_IO + e.getMessage());
+ }
+
+ public static void maybeUnwrapIOException(RuntimeException e) throws IOException {
+ if ((e instanceof IllegalStateException) && e.getMessage().startsWith(PREFIX_IO)) {
+ throw new IOException(e.getMessage().substring(PREFIX_IO.length()));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index cd4ed31..0eb922d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -32,16 +32,20 @@ import android.os.Binder;
import android.os.FileUtils;
import android.os.HandlerThread;
import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
import android.os.SELinux;
import android.os.UserHandle;
import android.os.UserManager;
import android.system.ErrnoException;
import android.system.Os;
import android.util.ArraySet;
+import android.util.ExceptionUtils;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
import com.google.android.collect.Sets;
@@ -71,6 +75,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
+ private RemoteCallbackList<IPackageInstallerObserver> mObservers = new RemoteCallbackList<>();
+
private static final FilenameFilter sStageFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
@@ -144,7 +150,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
public int createSession(String installerPackageName, InstallSessionParams params,
int userId) {
final int callingUid = Binder.getCallingUid();
- mPm.enforceCrossUserPermission(callingUid, userId, false, TAG);
+ mPm.enforceCrossUserPermission(callingUid, userId, true, "createSession");
if (mPm.isUserRestricted(UserHandle.getUserId(callingUid),
UserManager.DISALLOW_INSTALL_APPS)) {
@@ -161,19 +167,32 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
params.installFlags |= INSTALL_REPLACE_EXISTING;
}
+ // Sanity check that install could fit
+ if (params.deltaSize > 0) {
+ try {
+ mPm.freeStorage(params.deltaSize);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
+ final int sessionId;
+ final PackageInstallerSession session;
synchronized (mSessions) {
+ sessionId = allocateSessionIdLocked();
+
final long createdMillis = System.currentTimeMillis();
- final int sessionId = allocateSessionIdLocked();
final File sessionStageDir = prepareSessionStageDir(sessionId);
- final PackageInstallerSession session = new PackageInstallerSession(mCallback, mPm,
- sessionId, userId, installerPackageName, callingUid, params, createdMillis,
- sessionStageDir, mInstallThread.getLooper());
+ session = new PackageInstallerSession(mCallback, mPm, sessionId, userId,
+ installerPackageName, callingUid, params, createdMillis, sessionStageDir,
+ mInstallThread.getLooper());
mSessions.put(sessionId, session);
-
- writeSessionsAsync();
- return sessionId;
}
+
+ notifySessionCreated(session.generateInfo());
+ writeSessionsAsync();
+ return sessionId;
}
@Override
@@ -221,7 +240,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@Override
public List<InstallSessionInfo> getSessions(int userId) {
- mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, false, TAG);
+ mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getSessions");
final List<InstallSessionInfo> result = new ArrayList<>();
synchronized (mSessions) {
@@ -238,32 +257,112 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@Override
public void uninstall(String packageName, int flags, IPackageDeleteObserver observer,
int userId) {
+ mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstall");
mPm.deletePackageAsUser(packageName, observer, userId, flags);
}
@Override
public void uninstallSplit(String basePackageName, String overlayName, int flags,
IPackageDeleteObserver observer, int userId) {
+ mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstallSplit");
+
// TODO: flesh out once PM has split support
throw new UnsupportedOperationException();
}
@Override
public void registerObserver(IPackageInstallerObserver observer, int userId) {
- throw new UnsupportedOperationException();
+ mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "registerObserver");
+
+ // TODO: consider restricting to active launcher app only
+ mObservers.register(observer, new UserHandle(userId));
}
@Override
public void unregisterObserver(IPackageInstallerObserver observer, int userId) {
- throw new UnsupportedOperationException();
+ mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "unregisterObserver");
+ mObservers.unregister(observer);
+ }
+
+ private int getSessionUserId(int sessionId) {
+ synchronized (mSessions) {
+ return UserHandle.getUserId(mSessions.get(sessionId).installerUid);
+ }
+ }
+
+ private void notifySessionCreated(InstallSessionInfo info) {
+ final int userId = getSessionUserId(info.sessionId);
+ final int n = mObservers.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ final IPackageInstallerObserver observer = mObservers.getBroadcastItem(i);
+ final UserHandle user = (UserHandle) mObservers.getBroadcastCookie(i);
+ if (userId == user.getIdentifier()) {
+ try {
+ observer.onSessionCreated(info);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+ mObservers.finishBroadcast();
+ }
+
+ private void notifySessionProgress(int sessionId, int progress) {
+ final int userId = getSessionUserId(sessionId);
+ final int n = mObservers.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ final IPackageInstallerObserver observer = mObservers.getBroadcastItem(i);
+ final UserHandle user = (UserHandle) mObservers.getBroadcastCookie(i);
+ if (userId == user.getIdentifier()) {
+ try {
+ observer.onSessionProgress(sessionId, progress);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+ mObservers.finishBroadcast();
+ }
+
+ private void notifySessionFinished(int sessionId, boolean success) {
+ final int userId = getSessionUserId(sessionId);
+ final int n = mObservers.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ final IPackageInstallerObserver observer = mObservers.getBroadcastItem(i);
+ final UserHandle user = (UserHandle) mObservers.getBroadcastCookie(i);
+ if (userId == user.getIdentifier()) {
+ try {
+ observer.onSessionFinished(sessionId, success);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+ mObservers.finishBroadcast();
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Active install sessions:");
+ pw.increaseIndent();
+ synchronized (mSessions) {
+ final int N = mSessions.size();
+ for (int i = 0; i < N; i++) {
+ final PackageInstallerSession session = mSessions.valueAt(i);
+ session.dump(pw);
+ pw.println();
+ }
+ }
+ pw.println();
+ pw.decreaseIndent();
}
class Callback {
- public void onProgressChanged(PackageInstallerSession session) {
- // TODO: notify listeners
+ public void onSessionProgress(PackageInstallerSession session, int progress) {
+ notifySessionProgress(session.sessionId, progress);
}
- public void onSessionInvalid(PackageInstallerSession session) {
+ public void onSessionFinished(PackageInstallerSession session, boolean success) {
+ notifySessionFinished(session.sessionId, success);
+ synchronized (mSessions) {
+ mSessions.remove(session.sessionId);
+ }
writeSessionsAsync();
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 21776b0..c399fa2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -48,9 +48,12 @@ import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
import android.util.ArraySet;
+import android.util.ExceptionUtils;
+import android.util.MathUtils;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import libcore.io.Libcore;
@@ -94,12 +97,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
installLocked();
} catch (PackageManagerException e) {
Slog.e(TAG, "Install failed: " + e);
- destroy();
+ destroyInternal();
try {
mRemoteObserver.packageInstalled(mPackageName, null, e.error,
e.getMessage());
} catch (RemoteException ignored) {
}
+ mCallback.onSessionFinished(PackageInstallerSession.this, false);
}
return true;
@@ -109,7 +113,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private final Object mLock = new Object();
- private int mProgress;
+ private int mClientProgress;
+ private int mProgress = 0;
private String mPackageName;
private int mVersionCode;
@@ -170,13 +175,28 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
@Override
- public void updateProgress(int progress) {
- mProgress = progress;
- mCallback.onProgressChanged(this);
+ public void setClientProgress(int progress) {
+ mClientProgress = progress;
+ mProgress = MathUtils.constrain((mClientProgress * 8 * 100) / (params.progressMax * 10), 0, 80);
+ mCallback.onSessionProgress(this, mProgress);
+ }
+
+ @Override
+ public void addClientProgress(int progress) {
+ setClientProgress(mClientProgress + progress);
}
@Override
public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
+ try {
+ return openWriteInternal(name, offsetBytes, lengthBytes);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
+ private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
+ throws IOException {
// TODO: relay over to DCS when installing to ASEC
// Quick sanity check of state, and allocate a pipe for ourselves. We
@@ -223,9 +243,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return new ParcelFileDescriptor(bridge.getClientSocket());
} catch (ErrnoException e) {
- throw new IllegalStateException("Failed to write", e);
- } catch (IOException e) {
- throw new IllegalStateException("Failed to write", e);
+ throw e.rethrowAsIOException();
}
}
@@ -274,6 +292,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
spliceExistingFilesIntoStage();
}
+ // TODO: surface more granular state from dexopt
+ mCallback.onSessionProgress(this, 90);
+
// TODO: for ASEC based applications, grow and stream in packages
// We've reached point of no return; call into PMS to install the stage.
@@ -282,9 +303,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
@Override
public void packageInstalled(String basePackageName, Bundle extras, int returnCode,
- String msg) throws RemoteException {
- destroy();
- remoteObserver.packageInstalled(basePackageName, extras, returnCode, msg);
+ String msg) {
+ destroyInternal();
+ try {
+ remoteObserver.packageInstalled(basePackageName, extras, returnCode, msg);
+ } catch (RemoteException ignored) {
+ }
+ final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
+ mCallback.onSessionFinished(PackageInstallerSession.this, success);
}
};
@@ -442,13 +468,40 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void destroy() {
try {
- synchronized (mLock) {
- mInvalid = true;
- }
- FileUtils.deleteContents(sessionStageDir);
- sessionStageDir.delete();
+ destroyInternal();
} finally {
- mCallback.onSessionInvalid(this);
+ mCallback.onSessionFinished(this, false);
+ }
+ }
+
+ private void destroyInternal() {
+ synchronized (mLock) {
+ mInvalid = true;
}
+ FileUtils.deleteContents(sessionStageDir);
+ sessionStageDir.delete();
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Session " + sessionId + ":");
+ pw.increaseIndent();
+
+ pw.printPair("userId", userId);
+ pw.printPair("installerPackageName", installerPackageName);
+ pw.printPair("installerUid", installerUid);
+ pw.printPair("createdMillis", createdMillis);
+ pw.printPair("sessionStageDir", sessionStageDir);
+ pw.println();
+
+ params.dump(pw);
+
+ pw.printPair("mClientProgress", mClientProgress);
+ pw.printPair("mProgress", mProgress);
+ pw.printPair("mMutationsAllowed", mMutationsAllowed);
+ pw.printPair("mPermissionsConfirmed", mPermissionsConfirmed);
+ pw.printPair("mBridges", mBridges.size());
+ pw.println();
+
+ pw.decreaseIndent();
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 74a5ab5..101ef92 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -70,6 +70,7 @@ import com.android.internal.os.IParcelFileDescriptorFactory;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.IntentResolver;
@@ -12016,30 +12017,19 @@ public class PackageManagerService extends IPackageManager.Stub {
static class DumpState {
public static final int DUMP_LIBS = 1 << 0;
-
public static final int DUMP_FEATURES = 1 << 1;
-
public static final int DUMP_RESOLVERS = 1 << 2;
-
public static final int DUMP_PERMISSIONS = 1 << 3;
-
public static final int DUMP_PACKAGES = 1 << 4;
-
public static final int DUMP_SHARED_USERS = 1 << 5;
-
public static final int DUMP_MESSAGES = 1 << 6;
-
public static final int DUMP_PROVIDERS = 1 << 7;
-
public static final int DUMP_VERIFIERS = 1 << 8;
-
public static final int DUMP_PREFERRED = 1 << 9;
-
public static final int DUMP_PREFERRED_XML = 1 << 10;
-
public static final int DUMP_KEYSETS = 1 << 11;
-
public static final int DUMP_VERSION = 1 << 12;
+ public static final int DUMP_INSTALLS = 1 << 13;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
@@ -12143,6 +12133,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pw.println(" version: print database version info");
pw.println(" write: write current settings now");
pw.println(" <package.name>: info about given package");
+ pw.println(" installs: details about install sessions");
return;
} else if ("--checkin".equals(opt)) {
checkin = true;
@@ -12199,6 +12190,8 @@ public class PackageManagerService extends IPackageManager.Stub {
pw.println("Settings written.");
return;
}
+ } else if ("installs".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_INSTALLS);
}
}
@@ -12414,9 +12407,13 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.dumpSharedUsersLPr(pw, packageName, dumpState);
}
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS)) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ mInstallerService.dump(new IndentingPrintWriter(pw, " ", 120));
+ }
+
if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
- if (dumpState.onTitlePrinted())
- pw.println();
+ if (dumpState.onTitlePrinted()) pw.println();
mSettings.dumpReadMessagesLPr(pw, dumpState);
pw.println();