Loading core/java/android/content/pm/PackageInstaller.java +13 −3 Original line number Diff line number Diff line Loading @@ -19,9 +19,12 @@ package android.content.pm; import android.app.PackageInstallObserver; import android.app.PackageUninstallObserver; import android.content.pm.PackageManager.NameNotFoundException; import android.os.FileBridge; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import java.io.OutputStream; /** {@hide} */ public class PackageInstaller { private final PackageManager mPm; Loading Loading @@ -127,10 +130,17 @@ public class PackageInstaller { } } public ParcelFileDescriptor openWrite(String overlayName, long offsetBytes, long lengthBytes) { /** * Open an APK file for writing, starting at the given offset. You can * then stream data into the file, periodically calling * {@link OutputStream#flush()} to ensure bytes have been written to * disk. */ public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes) { try { return mSession.openWrite(overlayName, offsetBytes, lengthBytes); final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName, offsetBytes, lengthBytes); return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } Loading core/java/android/os/FileBridge.java 0 → 100644 +165 −0 Original line number Diff line number Diff line /* * 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.os; import static android.system.OsConstants.AF_UNIX; import static android.system.OsConstants.SOCK_STREAM; import android.system.ErrnoException; import android.system.Os; import android.util.Log; import libcore.io.IoBridge; import libcore.io.IoUtils; import libcore.io.Memory; 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; /** * Simple bridge that allows file access across process boundaries without * returning the underlying {@link FileDescriptor}. This is useful when the * server side needs to strongly assert that a client side is completely * hands-off. * * @hide */ public class FileBridge extends Thread { private static final String TAG = "FileBridge"; // TODO: consider extending to support bidirectional IO private static final int MSG_LENGTH = 8; /** CMD_WRITE [len] [data] */ private static final int CMD_WRITE = 1; /** CMD_FSYNC */ private static final int CMD_FSYNC = 2; private FileDescriptor mTarget; private final FileDescriptor mServer = new FileDescriptor(); private final FileDescriptor mClient = new FileDescriptor(); private volatile boolean mClosed; public FileBridge() { try { Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient); } catch (ErrnoException e) { throw new RuntimeException("Failed to create bridge"); } } public boolean isClosed() { return mClosed; } public void setTargetFile(FileDescriptor target) { mTarget = target; } public FileDescriptor getClientSocket() { return mClient; } @Override public void run() { final byte[] temp = new byte[8192]; try { while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) { final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN); if (cmd == CMD_WRITE) { // Shuttle data into local file int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN); while (len > 0) { int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len)); IoBridge.write(mTarget, temp, 0, n); len -= n; } } else if (cmd == CMD_FSYNC) { // Sync and echo back to confirm Os.fsync(mTarget); IoBridge.write(mServer, temp, 0, MSG_LENGTH); } } // Client was closed; one last fsync Os.fsync(mTarget); } catch (ErrnoException e) { Log.e(TAG, "Failed during bridge: ", e); } catch (IOException e) { Log.e(TAG, "Failed during bridge: ", e); } finally { IoUtils.closeQuietly(mTarget); IoUtils.closeQuietly(mServer); IoUtils.closeQuietly(mClient); mClosed = true; } } public static class FileBridgeOutputStream extends OutputStream { private final FileDescriptor mClient; private final byte[] mTemp = new byte[MSG_LENGTH]; public FileBridgeOutputStream(FileDescriptor client) { mClient = client; } @Override public void close() throws IOException { IoBridge.closeAndSignalBlockedThreads(mClient); } @Override public void flush() throws IOException { Memory.pokeInt(mTemp, 0, CMD_FSYNC, ByteOrder.BIG_ENDIAN); IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); // Wait for server to ack if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) { if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == CMD_FSYNC) { return; } } throw new SyncFailedException("Failed to fsync() across bridge"); } @Override public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException { Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount); Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN); Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN); IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); IoBridge.write(mClient, buffer, byteOffset, byteCount); } @Override public void write(int oneByte) throws IOException { Streams.writeSingleByte(this, oneByte); } } } core/tests/coretests/src/android/os/FileBridgeTest.java 0 → 100644 +156 −0 Original line number Diff line number Diff line /* * 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.os; import android.os.FileBridge.FileBridgeOutputStream; import android.test.AndroidTestCase; import android.test.MoreAsserts; import libcore.io.Streams; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Random; public class FileBridgeTest extends AndroidTestCase { private File file; private FileOutputStream fileOs; private FileBridge bridge; private FileBridgeOutputStream client; @Override protected void setUp() throws Exception { super.setUp(); file = getContext().getFileStreamPath("meow.dat"); file.delete(); fileOs = new FileOutputStream(file); bridge = new FileBridge(); bridge.setTargetFile(fileOs.getFD()); bridge.start(); client = new FileBridgeOutputStream(bridge.getClientSocket()); } @Override protected void tearDown() throws Exception { fileOs.close(); file.delete(); } private void assertOpen() throws Exception { assertFalse("expected open", bridge.isClosed()); } private void closeAndAssertClosed() throws Exception { client.close(); // Wait a beat for things to settle down SystemClock.sleep(200); assertTrue("expected closed", bridge.isClosed()); } private void assertContents(byte[] expected) throws Exception { MoreAsserts.assertEquals(expected, Streams.readFully(new FileInputStream(file))); } public void testNoWriteNoSync() throws Exception { assertOpen(); closeAndAssertClosed(); } public void testNoWriteSync() throws Exception { assertOpen(); client.flush(); closeAndAssertClosed(); } public void testWriteNoSync() throws Exception { assertOpen(); client.write("meow".getBytes(StandardCharsets.UTF_8)); closeAndAssertClosed(); assertContents("meow".getBytes(StandardCharsets.UTF_8)); } public void testWriteSync() throws Exception { assertOpen(); client.write("cake".getBytes(StandardCharsets.UTF_8)); client.flush(); closeAndAssertClosed(); assertContents("cake".getBytes(StandardCharsets.UTF_8)); } public void testWriteSyncWrite() throws Exception { assertOpen(); client.write("meow".getBytes(StandardCharsets.UTF_8)); client.flush(); client.write("cake".getBytes(StandardCharsets.UTF_8)); closeAndAssertClosed(); assertContents("meowcake".getBytes(StandardCharsets.UTF_8)); } public void testEmptyWrite() throws Exception { assertOpen(); client.write(new byte[0]); closeAndAssertClosed(); assertContents(new byte[0]); } public void testWriteAfterClose() throws Exception { assertOpen(); client.write("meow".getBytes(StandardCharsets.UTF_8)); closeAndAssertClosed(); try { client.write("cake".getBytes(StandardCharsets.UTF_8)); fail("wrote after close!"); } catch (IOException expected) { } assertContents("meow".getBytes(StandardCharsets.UTF_8)); } public void testRandomWrite() throws Exception { final Random r = new Random(); final ByteArrayOutputStream result = new ByteArrayOutputStream(); for (int i = 0; i < 512; i++) { final byte[] test = new byte[r.nextInt(24169)]; r.nextBytes(test); result.write(test); client.write(test); client.flush(); } closeAndAssertClosed(); assertContents(result.toByteArray()); } public void testGiantWrite() throws Exception { final byte[] test = new byte[263401]; new Random().nextBytes(test); assertOpen(); client.write(test); closeAndAssertClosed(); assertContents(test); } } services/core/java/com/android/server/pm/PackageInstallerSession.java +10 −55 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.content.pm.PackageParser.PackageLite; import android.content.pm.Signature; import android.os.Build; import android.os.Bundle; import android.os.FileBridge; import android.os.FileUtils; import android.os.Handler; import android.os.Looper; Loading Loading @@ -114,7 +115,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private boolean mPermissionsConfirmed; private boolean mInvalid; private ArrayList<WritePipe> mPipes = new ArrayList<>(); private ArrayList<FileBridge> mBridges = new ArrayList<>(); private IPackageInstallObserver2 mRemoteObserver; Loading Loading @@ -159,14 +160,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // 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. final WritePipe pipe; final FileBridge bridge; synchronized (mLock) { if (!mMutationsAllowed) { throw new IllegalStateException("Mutations not allowed"); } pipe = new WritePipe(); mPipes.add(pipe); bridge = new FileBridge(); mBridges.add(bridge); } try { Loading Loading @@ -194,9 +195,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); } pipe.setTargetFd(targetFd); pipe.start(); return pipe.getWriteFd(); bridge.setTargetFile(targetFd); bridge.start(); return new ParcelFileDescriptor(bridge.getClientSocket()); } catch (ErrnoException e) { throw new IllegalStateException("Failed to write", e); Loading @@ -218,8 +219,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Verify that all writers are hands-off if (mMutationsAllowed) { for (WritePipe pipe : mPipes) { if (!pipe.isClosed()) { for (FileBridge bridge : mBridges) { if (!bridge.isClosed()) { throw new InstallFailedException(INSTALL_FAILED_PACKAGE_CHANGED, "Files still open"); } Loading Loading @@ -482,52 +483,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } private static class WritePipe extends Thread { private final ParcelFileDescriptor[] mPipe; private FileDescriptor mTargetFd; private volatile boolean mClosed; public WritePipe() { try { mPipe = ParcelFileDescriptor.createPipe(); } catch (IOException e) { throw new IllegalStateException("Failed to create pipe"); } } public boolean isClosed() { return mClosed; } public void setTargetFd(FileDescriptor targetFd) { mTargetFd = targetFd; } public ParcelFileDescriptor getWriteFd() { return mPipe[1]; } @Override public void run() { FileInputStream in = null; FileOutputStream out = null; try { // TODO: look at switching to sendfile(2) to speed up in = new FileInputStream(mPipe[0].getFileDescriptor()); out = new FileOutputStream(mTargetFd); Streams.copy(in, out); } catch (IOException e) { Slog.w(TAG, "Failed to stream data: " + e); } finally { IoUtils.closeQuietly(mPipe[0]); IoUtils.closeQuietly(mTargetFd); mClosed = true; } } } private class InstallFailedException extends Exception { private final int error; Loading Loading
core/java/android/content/pm/PackageInstaller.java +13 −3 Original line number Diff line number Diff line Loading @@ -19,9 +19,12 @@ package android.content.pm; import android.app.PackageInstallObserver; import android.app.PackageUninstallObserver; import android.content.pm.PackageManager.NameNotFoundException; import android.os.FileBridge; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import java.io.OutputStream; /** {@hide} */ public class PackageInstaller { private final PackageManager mPm; Loading Loading @@ -127,10 +130,17 @@ public class PackageInstaller { } } public ParcelFileDescriptor openWrite(String overlayName, long offsetBytes, long lengthBytes) { /** * Open an APK file for writing, starting at the given offset. You can * then stream data into the file, periodically calling * {@link OutputStream#flush()} to ensure bytes have been written to * disk. */ public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes) { try { return mSession.openWrite(overlayName, offsetBytes, lengthBytes); final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName, offsetBytes, lengthBytes); return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } Loading
core/java/android/os/FileBridge.java 0 → 100644 +165 −0 Original line number Diff line number Diff line /* * 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.os; import static android.system.OsConstants.AF_UNIX; import static android.system.OsConstants.SOCK_STREAM; import android.system.ErrnoException; import android.system.Os; import android.util.Log; import libcore.io.IoBridge; import libcore.io.IoUtils; import libcore.io.Memory; 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; /** * Simple bridge that allows file access across process boundaries without * returning the underlying {@link FileDescriptor}. This is useful when the * server side needs to strongly assert that a client side is completely * hands-off. * * @hide */ public class FileBridge extends Thread { private static final String TAG = "FileBridge"; // TODO: consider extending to support bidirectional IO private static final int MSG_LENGTH = 8; /** CMD_WRITE [len] [data] */ private static final int CMD_WRITE = 1; /** CMD_FSYNC */ private static final int CMD_FSYNC = 2; private FileDescriptor mTarget; private final FileDescriptor mServer = new FileDescriptor(); private final FileDescriptor mClient = new FileDescriptor(); private volatile boolean mClosed; public FileBridge() { try { Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient); } catch (ErrnoException e) { throw new RuntimeException("Failed to create bridge"); } } public boolean isClosed() { return mClosed; } public void setTargetFile(FileDescriptor target) { mTarget = target; } public FileDescriptor getClientSocket() { return mClient; } @Override public void run() { final byte[] temp = new byte[8192]; try { while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) { final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN); if (cmd == CMD_WRITE) { // Shuttle data into local file int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN); while (len > 0) { int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len)); IoBridge.write(mTarget, temp, 0, n); len -= n; } } else if (cmd == CMD_FSYNC) { // Sync and echo back to confirm Os.fsync(mTarget); IoBridge.write(mServer, temp, 0, MSG_LENGTH); } } // Client was closed; one last fsync Os.fsync(mTarget); } catch (ErrnoException e) { Log.e(TAG, "Failed during bridge: ", e); } catch (IOException e) { Log.e(TAG, "Failed during bridge: ", e); } finally { IoUtils.closeQuietly(mTarget); IoUtils.closeQuietly(mServer); IoUtils.closeQuietly(mClient); mClosed = true; } } public static class FileBridgeOutputStream extends OutputStream { private final FileDescriptor mClient; private final byte[] mTemp = new byte[MSG_LENGTH]; public FileBridgeOutputStream(FileDescriptor client) { mClient = client; } @Override public void close() throws IOException { IoBridge.closeAndSignalBlockedThreads(mClient); } @Override public void flush() throws IOException { Memory.pokeInt(mTemp, 0, CMD_FSYNC, ByteOrder.BIG_ENDIAN); IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); // Wait for server to ack if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) { if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == CMD_FSYNC) { return; } } throw new SyncFailedException("Failed to fsync() across bridge"); } @Override public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException { Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount); Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN); Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN); IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); IoBridge.write(mClient, buffer, byteOffset, byteCount); } @Override public void write(int oneByte) throws IOException { Streams.writeSingleByte(this, oneByte); } } }
core/tests/coretests/src/android/os/FileBridgeTest.java 0 → 100644 +156 −0 Original line number Diff line number Diff line /* * 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.os; import android.os.FileBridge.FileBridgeOutputStream; import android.test.AndroidTestCase; import android.test.MoreAsserts; import libcore.io.Streams; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Random; public class FileBridgeTest extends AndroidTestCase { private File file; private FileOutputStream fileOs; private FileBridge bridge; private FileBridgeOutputStream client; @Override protected void setUp() throws Exception { super.setUp(); file = getContext().getFileStreamPath("meow.dat"); file.delete(); fileOs = new FileOutputStream(file); bridge = new FileBridge(); bridge.setTargetFile(fileOs.getFD()); bridge.start(); client = new FileBridgeOutputStream(bridge.getClientSocket()); } @Override protected void tearDown() throws Exception { fileOs.close(); file.delete(); } private void assertOpen() throws Exception { assertFalse("expected open", bridge.isClosed()); } private void closeAndAssertClosed() throws Exception { client.close(); // Wait a beat for things to settle down SystemClock.sleep(200); assertTrue("expected closed", bridge.isClosed()); } private void assertContents(byte[] expected) throws Exception { MoreAsserts.assertEquals(expected, Streams.readFully(new FileInputStream(file))); } public void testNoWriteNoSync() throws Exception { assertOpen(); closeAndAssertClosed(); } public void testNoWriteSync() throws Exception { assertOpen(); client.flush(); closeAndAssertClosed(); } public void testWriteNoSync() throws Exception { assertOpen(); client.write("meow".getBytes(StandardCharsets.UTF_8)); closeAndAssertClosed(); assertContents("meow".getBytes(StandardCharsets.UTF_8)); } public void testWriteSync() throws Exception { assertOpen(); client.write("cake".getBytes(StandardCharsets.UTF_8)); client.flush(); closeAndAssertClosed(); assertContents("cake".getBytes(StandardCharsets.UTF_8)); } public void testWriteSyncWrite() throws Exception { assertOpen(); client.write("meow".getBytes(StandardCharsets.UTF_8)); client.flush(); client.write("cake".getBytes(StandardCharsets.UTF_8)); closeAndAssertClosed(); assertContents("meowcake".getBytes(StandardCharsets.UTF_8)); } public void testEmptyWrite() throws Exception { assertOpen(); client.write(new byte[0]); closeAndAssertClosed(); assertContents(new byte[0]); } public void testWriteAfterClose() throws Exception { assertOpen(); client.write("meow".getBytes(StandardCharsets.UTF_8)); closeAndAssertClosed(); try { client.write("cake".getBytes(StandardCharsets.UTF_8)); fail("wrote after close!"); } catch (IOException expected) { } assertContents("meow".getBytes(StandardCharsets.UTF_8)); } public void testRandomWrite() throws Exception { final Random r = new Random(); final ByteArrayOutputStream result = new ByteArrayOutputStream(); for (int i = 0; i < 512; i++) { final byte[] test = new byte[r.nextInt(24169)]; r.nextBytes(test); result.write(test); client.write(test); client.flush(); } closeAndAssertClosed(); assertContents(result.toByteArray()); } public void testGiantWrite() throws Exception { final byte[] test = new byte[263401]; new Random().nextBytes(test); assertOpen(); client.write(test); closeAndAssertClosed(); assertContents(test); } }
services/core/java/com/android/server/pm/PackageInstallerSession.java +10 −55 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.content.pm.PackageParser.PackageLite; import android.content.pm.Signature; import android.os.Build; import android.os.Bundle; import android.os.FileBridge; import android.os.FileUtils; import android.os.Handler; import android.os.Looper; Loading Loading @@ -114,7 +115,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private boolean mPermissionsConfirmed; private boolean mInvalid; private ArrayList<WritePipe> mPipes = new ArrayList<>(); private ArrayList<FileBridge> mBridges = new ArrayList<>(); private IPackageInstallObserver2 mRemoteObserver; Loading Loading @@ -159,14 +160,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // 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. final WritePipe pipe; final FileBridge bridge; synchronized (mLock) { if (!mMutationsAllowed) { throw new IllegalStateException("Mutations not allowed"); } pipe = new WritePipe(); mPipes.add(pipe); bridge = new FileBridge(); mBridges.add(bridge); } try { Loading Loading @@ -194,9 +195,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); } pipe.setTargetFd(targetFd); pipe.start(); return pipe.getWriteFd(); bridge.setTargetFile(targetFd); bridge.start(); return new ParcelFileDescriptor(bridge.getClientSocket()); } catch (ErrnoException e) { throw new IllegalStateException("Failed to write", e); Loading @@ -218,8 +219,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Verify that all writers are hands-off if (mMutationsAllowed) { for (WritePipe pipe : mPipes) { if (!pipe.isClosed()) { for (FileBridge bridge : mBridges) { if (!bridge.isClosed()) { throw new InstallFailedException(INSTALL_FAILED_PACKAGE_CHANGED, "Files still open"); } Loading Loading @@ -482,52 +483,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } private static class WritePipe extends Thread { private final ParcelFileDescriptor[] mPipe; private FileDescriptor mTargetFd; private volatile boolean mClosed; public WritePipe() { try { mPipe = ParcelFileDescriptor.createPipe(); } catch (IOException e) { throw new IllegalStateException("Failed to create pipe"); } } public boolean isClosed() { return mClosed; } public void setTargetFd(FileDescriptor targetFd) { mTargetFd = targetFd; } public ParcelFileDescriptor getWriteFd() { return mPipe[1]; } @Override public void run() { FileInputStream in = null; FileOutputStream out = null; try { // TODO: look at switching to sendfile(2) to speed up in = new FileInputStream(mPipe[0].getFileDescriptor()); out = new FileOutputStream(mTargetFd); Streams.copy(in, out); } catch (IOException e) { Slog.w(TAG, "Failed to stream data: " + e); } finally { IoUtils.closeQuietly(mPipe[0]); IoUtils.closeQuietly(mTargetFd); mClosed = true; } } } private class InstallFailedException extends Exception { private final int error; Loading