import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.system.ErrnoException;
+import android.system.Int64Ref;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
@Override
public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
try {
- return openWriteInternal(name, offsetBytes, lengthBytes);
+ return doWriteInternal(name, offsetBytes, lengthBytes, null);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
- private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
- throws IOException {
+ @Override
+ public void write(String name, long offsetBytes, long lengthBytes,
+ ParcelFileDescriptor fd) {
+ try {
+ doWriteInternal(name, offsetBytes, lengthBytes, fd);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
+ private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
+ ParcelFileDescriptor incomingFd) throws IOException {
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
}
- if (PackageInstaller.ENABLE_REVOCABLE_FD) {
+ if (incomingFd != null) {
+ switch (Binder.getCallingUid()) {
+ case android.os.Process.SHELL_UID:
+ case android.os.Process.ROOT_UID:
+ break;
+ default:
+ throw new SecurityException("Reverse mode only supported from shell");
+ }
+
+ // In "reverse" mode, we're streaming data ourselves from the
+ // incoming FD, which means we never have to hand out our
+ // sensitive internal FD. We still rely on a "bridge" being
+ // inserted above to hold the session active.
+ try {
+ final Int64Ref last = new Int64Ref(0);
+ FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, (long progress) -> {
+ if (params.sizeBytes > 0) {
+ final long delta = progress - last.value;
+ last.value = progress;
+ addClientProgress((float) delta / (float) params.sizeBytes);
+ }
+ }, null, lengthBytes);
+ } finally {
+ IoUtils.closeQuietly(targetFd);
+ IoUtils.closeQuietly(incomingFd);
+
+ // We're done here, so remove the "bridge" that was holding
+ // the session active.
+ synchronized (mLock) {
+ if (PackageInstaller.ENABLE_REVOCABLE_FD) {
+ mFds.remove(fd);
+ } else {
+ mBridges.remove(bridge);
+ }
+ }
+ }
+ return null;
+ } else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
fd.init(mContext, targetFd);
return fd.getRevocableFileDescriptor();
} else {
package com.android.server.pm;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+
import android.accounts.IAccountManager;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionParams;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.SizedInputStream;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import libcore.io.IoUtils;
+import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-
class PackageManagerShellCommand extends ShellCommand {
/** Path for streaming APK content */
private static final String STDIN_PATH = "-";
final PrintWriter pw = getOutPrintWriter();
final ParcelFileDescriptor fd;
if (STDIN_PATH.equals(inPath)) {
- fd = null;
+ fd = new ParcelFileDescriptor(getInFileDescriptor());
} else if (inPath != null) {
fd = openFileForSystem(inPath, "r");
if (fd == null) {
return -1;
}
} else {
- fd = null;
+ fd = new ParcelFileDescriptor(getInFileDescriptor());
}
if (sizeBytes <= 0) {
getErrPrintWriter().println("Error: must specify a APK size");
return 1;
}
- final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId);
-
PackageInstaller.Session session = null;
- InputStream in = null;
- OutputStream out = null;
try {
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
-
- if (fd != null) {
- in = new ParcelFileDescriptor.AutoCloseInputStream(fd);
- } else {
- in = new SizedInputStream(getRawInputStream(), sizeBytes);
- }
- out = session.openWrite(splitName, 0, sizeBytes);
-
- int total = 0;
- byte[] buffer = new byte[1024 * 1024];
- int c;
- while ((c = in.read(buffer)) != -1) {
- total += c;
- out.write(buffer, 0, c);
-
- if (info.sizeBytes > 0) {
- final float fraction = ((float) c / (float) info.sizeBytes);
- session.addProgress(fraction);
- }
- }
- session.fsync(out);
+ session.write(splitName, 0, sizeBytes, fd);
if (logSuccess) {
- pw.println("Success: streamed " + total + " bytes");
+ pw.println("Success: streamed " + sizeBytes + " bytes");
}
return 0;
} catch (IOException e) {
getErrPrintWriter().println("Error: failed to write; " + e.getMessage());
return 1;
} finally {
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(in);
IoUtils.closeQuietly(session);
}
}