summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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();