Merge "Switch to streaming data for time zone update"

am: f093d4e13e

Change-Id: I3fd22fe47e1d7b86d4f88d527940c6812ecfa31b
This commit is contained in:
Neil Fuller
2017-06-27 12:05:17 +00:00
committed by android-build-merger
4 changed files with 107 additions and 150 deletions

View File

@@ -1,30 +0,0 @@
/*
* Copyright (C) 2017 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 com.android.server.timezone;
import android.os.ParcelFileDescriptor;
import java.io.IOException;
/**
* An easy-to-mock interface around use of {@link ParcelFileDescriptor} for use by
* {@link RulesManagerService}.
*/
interface FileDescriptorHelper {
byte[] readFully(ParcelFileDescriptor parcelFileDescriptor) throws IOException;
}

View File

@@ -20,8 +20,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService; import com.android.server.SystemService;
import com.android.timezone.distro.DistroException; import com.android.timezone.distro.DistroException;
import com.android.timezone.distro.DistroVersion; import com.android.timezone.distro.DistroVersion;
import com.android.timezone.distro.TimeZoneDistro;
import com.android.timezone.distro.StagedDistroOperation; import com.android.timezone.distro.StagedDistroOperation;
import com.android.timezone.distro.TimeZoneDistro;
import android.app.timezone.Callback; import android.app.timezone.Callback;
import android.app.timezone.DistroFormatVersion; import android.app.timezone.DistroFormatVersion;
@@ -36,7 +36,9 @@ import android.os.RemoteException;
import android.util.Slog; import android.util.Slog;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@@ -83,26 +85,22 @@ public final class RulesManagerService extends IRulesManager.Stub {
private final PackageTracker mPackageTracker; private final PackageTracker mPackageTracker;
private final Executor mExecutor; private final Executor mExecutor;
private final TimeZoneDistroInstaller mInstaller; private final TimeZoneDistroInstaller mInstaller;
private final FileDescriptorHelper mFileDescriptorHelper;
private static RulesManagerService create(Context context) { private static RulesManagerService create(Context context) {
RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context); RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
return new RulesManagerService( return new RulesManagerService(
helper /* permissionHelper */, helper /* permissionHelper */,
helper /* executor */, helper /* executor */,
helper /* fileDescriptorHelper */,
PackageTracker.create(context), PackageTracker.create(context),
new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR)); new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
} }
// A constructor that can be used by tests to supply mocked / faked dependencies. // A constructor that can be used by tests to supply mocked / faked dependencies.
RulesManagerService(PermissionHelper permissionHelper, RulesManagerService(PermissionHelper permissionHelper,
Executor executor, Executor executor, PackageTracker packageTracker,
FileDescriptorHelper fileDescriptorHelper, PackageTracker packageTracker,
TimeZoneDistroInstaller timeZoneDistroInstaller) { TimeZoneDistroInstaller timeZoneDistroInstaller) {
mPermissionHelper = permissionHelper; mPermissionHelper = permissionHelper;
mExecutor = executor; mExecutor = executor;
mFileDescriptorHelper = fileDescriptorHelper;
mPackageTracker = packageTracker; mPackageTracker = packageTracker;
mInstaller = timeZoneDistroInstaller; mInstaller = timeZoneDistroInstaller;
} }
@@ -177,55 +175,78 @@ public final class RulesManagerService extends IRulesManager.Stub {
} }
@Override @Override
public int requestInstall( public int requestInstall(ParcelFileDescriptor distroParcelFileDescriptor,
ParcelFileDescriptor timeZoneDistro, byte[] checkTokenBytes, ICallback callback) { byte[] checkTokenBytes, ICallback callback) {
mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
CheckToken checkToken = null; boolean closeParcelFileDescriptorOnExit = true;
if (checkTokenBytes != null) { try {
checkToken = createCheckTokenOrThrow(checkTokenBytes); mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
}
synchronized (this) {
if (timeZoneDistro == null) {
throw new NullPointerException("timeZoneDistro == null");
}
if (callback == null) {
throw new NullPointerException("observer == null");
}
if (mOperationInProgress.get()) {
return RulesManager.ERROR_OPERATION_IN_PROGRESS;
}
mOperationInProgress.set(true);
// Execute the install asynchronously. CheckToken checkToken = null;
mExecutor.execute(new InstallRunnable(timeZoneDistro, checkToken, callback)); if (checkTokenBytes != null) {
checkToken = createCheckTokenOrThrow(checkTokenBytes);
}
return RulesManager.SUCCESS; synchronized (this) {
if (distroParcelFileDescriptor == null) {
throw new NullPointerException("distroParcelFileDescriptor == null");
}
if (callback == null) {
throw new NullPointerException("observer == null");
}
if (mOperationInProgress.get()) {
return RulesManager.ERROR_OPERATION_IN_PROGRESS;
}
mOperationInProgress.set(true);
// Execute the install asynchronously.
mExecutor.execute(
new InstallRunnable(distroParcelFileDescriptor, checkToken, callback));
// The InstallRunnable now owns the ParcelFileDescriptor, so it will close it after
// it executes (and we do not have to).
closeParcelFileDescriptorOnExit = false;
return RulesManager.SUCCESS;
}
} finally {
// We should close() the local ParcelFileDescriptor we were passed if it hasn't been
// passed to another thread to handle.
if (distroParcelFileDescriptor != null && closeParcelFileDescriptorOnExit) {
try {
distroParcelFileDescriptor.close();
} catch (IOException e) {
Slog.w(TAG, "Failed to close distroParcelFileDescriptor", e);
}
}
} }
} }
private class InstallRunnable implements Runnable { private class InstallRunnable implements Runnable {
private final ParcelFileDescriptor mTimeZoneDistro; private final ParcelFileDescriptor mDistroParcelFileDescriptor;
private final CheckToken mCheckToken; private final CheckToken mCheckToken;
private final ICallback mCallback; private final ICallback mCallback;
InstallRunnable( InstallRunnable(ParcelFileDescriptor distroParcelFileDescriptor, CheckToken checkToken,
ParcelFileDescriptor timeZoneDistro, CheckToken checkToken, ICallback callback) { ICallback callback) {
mTimeZoneDistro = timeZoneDistro; mDistroParcelFileDescriptor = distroParcelFileDescriptor;
mCheckToken = checkToken; mCheckToken = checkToken;
mCallback = callback; mCallback = callback;
} }
@Override @Override
public void run() { public void run() {
boolean success = false;
// Adopt the ParcelFileDescriptor into this try-with-resources so it is closed // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed
// when we are done. // when we are done.
boolean success = false; try (ParcelFileDescriptor pfd = mDistroParcelFileDescriptor) {
try { // The ParcelFileDescriptor owns the underlying FileDescriptor and we'll close
byte[] distroBytes = // it at the end of the try-with-resources.
RulesManagerService.this.mFileDescriptorHelper.readFully(mTimeZoneDistro); final boolean isFdOwner = false;
TimeZoneDistro distro = new TimeZoneDistro(distroBytes); InputStream is = new FileInputStream(pfd.getFileDescriptor(), isFdOwner);
TimeZoneDistro distro = new TimeZoneDistro(is);
int installerResult = mInstaller.stageInstallWithErrorCode(distro); int installerResult = mInstaller.stageInstallWithErrorCode(distro);
int resultCode = mapInstallerResultToApiCode(installerResult); int resultCode = mapInstallerResultToApiCode(installerResult);
sendFinishedStatus(mCallback, resultCode); sendFinishedStatus(mCallback, resultCode);

View File

@@ -27,8 +27,7 @@ import libcore.io.Streams;
/** /**
* A single class that implements multiple helper interfaces for use by {@link RulesManagerService}. * A single class that implements multiple helper interfaces for use by {@link RulesManagerService}.
*/ */
final class RulesManagerServiceHelperImpl final class RulesManagerServiceHelperImpl implements PermissionHelper, Executor {
implements PermissionHelper, Executor, FileDescriptorHelper {
private final Context mContext; private final Context mContext;
@@ -47,13 +46,4 @@ final class RulesManagerServiceHelperImpl
// TODO Is there a better way? // TODO Is there a better way?
new Thread(runnable).start(); new Thread(runnable).start();
} }
@Override
public byte[] readFully(ParcelFileDescriptor parcelFileDescriptor) throws IOException {
try (ParcelFileDescriptor pfd = parcelFileDescriptor) {
// Read bytes
FileInputStream in = new FileInputStream(pfd.getFileDescriptor(), false /* isOwner */);
return Streams.readFully(in);
}
}
} }

View File

@@ -30,6 +30,8 @@ import android.app.timezone.RulesManager;
import android.app.timezone.RulesState; import android.app.timezone.RulesState;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -61,7 +63,6 @@ public class RulesManagerServiceTest {
private FakeExecutor mFakeExecutor; private FakeExecutor mFakeExecutor;
private PermissionHelper mMockPermissionHelper; private PermissionHelper mMockPermissionHelper;
private FileDescriptorHelper mMockFileDescriptorHelper;
private PackageTracker mMockPackageTracker; private PackageTracker mMockPackageTracker;
private TimeZoneDistroInstaller mMockTimeZoneDistroInstaller; private TimeZoneDistroInstaller mMockTimeZoneDistroInstaller;
@@ -69,7 +70,6 @@ public class RulesManagerServiceTest {
public void setUp() { public void setUp() {
mFakeExecutor = new FakeExecutor(); mFakeExecutor = new FakeExecutor();
mMockFileDescriptorHelper = mock(FileDescriptorHelper.class);
mMockPackageTracker = mock(PackageTracker.class); mMockPackageTracker = mock(PackageTracker.class);
mMockPermissionHelper = mock(PermissionHelper.class); mMockPermissionHelper = mock(PermissionHelper.class);
mMockTimeZoneDistroInstaller = mock(TimeZoneDistroInstaller.class); mMockTimeZoneDistroInstaller = mock(TimeZoneDistroInstaller.class);
@@ -77,7 +77,6 @@ public class RulesManagerServiceTest {
mRulesManagerService = new RulesManagerService( mRulesManagerService = new RulesManagerService(
mMockPermissionHelper, mMockPermissionHelper,
mFakeExecutor, mFakeExecutor,
mMockFileDescriptorHelper,
mMockPackageTracker, mMockPackageTracker,
mMockTimeZoneDistroInstaller); mMockTimeZoneDistroInstaller);
} }
@@ -273,9 +272,8 @@ public class RulesManagerServiceTest {
revision); revision);
configureInstalledDistroVersion(installedDistroVersion); configureInstalledDistroVersion(installedDistroVersion);
byte[] expectedContent = createArbitraryBytes(1000); ParcelFileDescriptor parcelFileDescriptor =
ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor(); createParcelFileDescriptor(createArbitraryBytes(1000));
configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
// Start an async operation so there is one in progress. The mFakeExecutor won't actually // Start an async operation so there is one in progress. The mFakeExecutor won't actually
// execute it. // execute it.
@@ -298,24 +296,27 @@ public class RulesManagerServiceTest {
public void requestInstall_operationInProgress() throws Exception { public void requestInstall_operationInProgress() throws Exception {
configureCallerHasPermission(); configureCallerHasPermission();
byte[] expectedContent = createArbitraryBytes(1000); ParcelFileDescriptor parcelFileDescriptor1 =
ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor(); createParcelFileDescriptor(createArbitraryBytes(1000));
configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
byte[] tokenBytes = createArbitraryTokenBytes(); byte[] tokenBytes = createArbitraryTokenBytes();
ICallback callback = new StubbedCallback(); ICallback callback = new StubbedCallback();
// First request should succeed. // First request should succeed.
assertEquals(RulesManager.SUCCESS, assertEquals(RulesManager.SUCCESS,
mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback)); mRulesManagerService.requestInstall(parcelFileDescriptor1, tokenBytes, callback));
// Something async should be enqueued. Clear it but do not execute it so we can detect the // Something async should be enqueued. Clear it but do not execute it so we can detect the
// second request does nothing. // second request does nothing.
mFakeExecutor.getAndResetLastCommand(); mFakeExecutor.getAndResetLastCommand();
// Second request should fail. // Second request should fail.
ParcelFileDescriptor parcelFileDescriptor2 =
createParcelFileDescriptor(createArbitraryBytes(1000));
assertEquals(RulesManager.ERROR_OPERATION_IN_PROGRESS, assertEquals(RulesManager.ERROR_OPERATION_IN_PROGRESS,
mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback)); mRulesManagerService.requestInstall(parcelFileDescriptor2, tokenBytes, callback));
assertClosed(parcelFileDescriptor2);
// Assert nothing async was enqueued. // Assert nothing async was enqueued.
mFakeExecutor.assertNothingQueued(); mFakeExecutor.assertNothingQueued();
@@ -327,9 +328,8 @@ public class RulesManagerServiceTest {
public void requestInstall_badToken() throws Exception { public void requestInstall_badToken() throws Exception {
configureCallerHasPermission(); configureCallerHasPermission();
byte[] expectedContent = createArbitraryBytes(1000); ParcelFileDescriptor parcelFileDescriptor =
ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor(); createParcelFileDescriptor(createArbitraryBytes(1000));
configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
byte[] badTokenBytes = new byte[2]; byte[] badTokenBytes = new byte[2];
ICallback callback = new StubbedCallback(); ICallback callback = new StubbedCallback();
@@ -340,6 +340,8 @@ public class RulesManagerServiceTest {
} catch (IllegalArgumentException expected) { } catch (IllegalArgumentException expected) {
} }
assertClosed(parcelFileDescriptor);
// Assert nothing async was enqueued. // Assert nothing async was enqueued.
mFakeExecutor.assertNothingQueued(); mFakeExecutor.assertNothingQueued();
verifyNoInstallerCallsMade(); verifyNoInstallerCallsMade();
@@ -369,7 +371,8 @@ public class RulesManagerServiceTest {
public void requestInstall_nullCallback() throws Exception { public void requestInstall_nullCallback() throws Exception {
configureCallerHasPermission(); configureCallerHasPermission();
ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor(); ParcelFileDescriptor parcelFileDescriptor =
createParcelFileDescriptor(createArbitraryBytes(1000));
byte[] tokenBytes = createArbitraryTokenBytes(); byte[] tokenBytes = createArbitraryTokenBytes();
ICallback callback = null; ICallback callback = null;
@@ -378,6 +381,8 @@ public class RulesManagerServiceTest {
fail(); fail();
} catch (NullPointerException expected) {} } catch (NullPointerException expected) {}
assertClosed(parcelFileDescriptor);
// Assert nothing async was enqueued. // Assert nothing async was enqueued.
mFakeExecutor.assertNothingQueued(); mFakeExecutor.assertNothingQueued();
verifyNoInstallerCallsMade(); verifyNoInstallerCallsMade();
@@ -388,9 +393,8 @@ public class RulesManagerServiceTest {
public void requestInstall_asyncSuccess() throws Exception { public void requestInstall_asyncSuccess() throws Exception {
configureCallerHasPermission(); configureCallerHasPermission();
ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor(); ParcelFileDescriptor parcelFileDescriptor =
byte[] expectedContent = createArbitraryBytes(1000); createParcelFileDescriptor(createArbitraryBytes(1000));
configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
CheckToken token = createArbitraryToken(); CheckToken token = createArbitraryToken();
byte[] tokenBytes = token.toByteArray(); byte[] tokenBytes = token.toByteArray();
@@ -406,14 +410,14 @@ public class RulesManagerServiceTest {
verifyNoInstallerCallsMade(); verifyNoInstallerCallsMade();
verifyNoPackageTrackerCallsMade(); verifyNoPackageTrackerCallsMade();
TimeZoneDistro expectedDistro = new TimeZoneDistro(expectedContent);
// Set up the installer. // Set up the installer.
configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS); configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS);
// Simulate the async execution. // Simulate the async execution.
mFakeExecutor.simulateAsyncExecutionOfLastCommand(); mFakeExecutor.simulateAsyncExecutionOfLastCommand();
assertClosed(parcelFileDescriptor);
// Verify the expected calls were made to other components. // Verify the expected calls were made to other components.
verifyStageInstallCalled(); verifyStageInstallCalled();
verifyPackageTrackerCalled(token, true /* success */); verifyPackageTrackerCalled(token, true /* success */);
@@ -426,9 +430,8 @@ public class RulesManagerServiceTest {
public void requestInstall_nullTokenBytes() throws Exception { public void requestInstall_nullTokenBytes() throws Exception {
configureCallerHasPermission(); configureCallerHasPermission();
ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor(); ParcelFileDescriptor parcelFileDescriptor =
byte[] expectedContent = createArbitraryBytes(1000); createParcelFileDescriptor(createArbitraryBytes(1000));
configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
TestCallback callback = new TestCallback(); TestCallback callback = new TestCallback();
@@ -447,6 +450,8 @@ public class RulesManagerServiceTest {
// Simulate the async execution. // Simulate the async execution.
mFakeExecutor.simulateAsyncExecutionOfLastCommand(); mFakeExecutor.simulateAsyncExecutionOfLastCommand();
assertClosed(parcelFileDescriptor);
// Verify the expected calls were made to other components. // Verify the expected calls were made to other components.
verifyStageInstallCalled(); verifyStageInstallCalled();
verifyPackageTrackerCalled(null /* expectedToken */, true /* success */); verifyPackageTrackerCalled(null /* expectedToken */, true /* success */);
@@ -459,9 +464,8 @@ public class RulesManagerServiceTest {
public void requestInstall_asyncInstallFail() throws Exception { public void requestInstall_asyncInstallFail() throws Exception {
configureCallerHasPermission(); configureCallerHasPermission();
byte[] expectedContent = createArbitraryBytes(1000); ParcelFileDescriptor parcelFileDescriptor =
ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor(); createParcelFileDescriptor(createArbitraryBytes(1000));
configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
CheckToken token = createArbitraryToken(); CheckToken token = createArbitraryToken();
byte[] tokenBytes = token.toByteArray(); byte[] tokenBytes = token.toByteArray();
@@ -482,6 +486,8 @@ public class RulesManagerServiceTest {
// Simulate the async execution. // Simulate the async execution.
mFakeExecutor.simulateAsyncExecutionOfLastCommand(); mFakeExecutor.simulateAsyncExecutionOfLastCommand();
assertClosed(parcelFileDescriptor);
// Verify the expected calls were made to other components. // Verify the expected calls were made to other components.
verifyStageInstallCalled(); verifyStageInstallCalled();
@@ -493,38 +499,6 @@ public class RulesManagerServiceTest {
callback.assertResultReceived(Callback.ERROR_INSTALL_VALIDATION_ERROR); callback.assertResultReceived(Callback.ERROR_INSTALL_VALIDATION_ERROR);
} }
@Test
public void requestInstall_asyncParcelFileDescriptorReadFail() throws Exception {
configureCallerHasPermission();
ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor();
configureParcelFileDescriptorReadFailure(parcelFileDescriptor);
CheckToken token = createArbitraryToken();
byte[] tokenBytes = token.toByteArray();
TestCallback callback = new TestCallback();
// Request the install.
assertEquals(RulesManager.SUCCESS,
mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback));
// Simulate the async execution.
mFakeExecutor.simulateAsyncExecutionOfLastCommand();
// Verify nothing else happened.
verifyNoInstallerCallsMade();
// A failure to read the ParcelFileDescriptor is treated as a failure. It might be the
// result of a file system error. This is a fairly arbitrary choice.
verifyPackageTrackerCalled(token, false /* success */);
verifyNoPackageTrackerCallsMade();
// Check the callback was received.
callback.assertResultReceived(Callback.ERROR_UNKNOWN_FAILURE);
}
@Test @Test
public void requestUninstall_operationInProgress() throws Exception { public void requestUninstall_operationInProgress() throws Exception {
configureCallerHasPermission(); configureCallerHasPermission();
@@ -773,17 +747,6 @@ public class RulesManagerServiceTest {
.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION); .enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
} }
private void configureParcelFileDescriptorReadSuccess(ParcelFileDescriptor parcelFileDescriptor,
byte[] content) throws Exception {
when(mMockFileDescriptorHelper.readFully(parcelFileDescriptor)).thenReturn(content);
}
private void configureParcelFileDescriptorReadFailure(ParcelFileDescriptor parcelFileDescriptor)
throws Exception {
when(mMockFileDescriptorHelper.readFully(parcelFileDescriptor))
.thenThrow(new IOException("Simulated failure"));
}
private void configureStageInstallExpectation(int resultCode) private void configureStageInstallExpectation(int resultCode)
throws Exception { throws Exception {
when(mMockTimeZoneDistroInstaller.stageInstallWithErrorCode(any(TimeZoneDistro.class))) when(mMockTimeZoneDistroInstaller.stageInstallWithErrorCode(any(TimeZoneDistro.class)))
@@ -827,10 +790,6 @@ public class RulesManagerServiceTest {
return new CheckToken(1, new PackageVersions(1, 1)); return new CheckToken(1, new PackageVersions(1, 1));
} }
private ParcelFileDescriptor createFakeParcelFileDescriptor() {
return new ParcelFileDescriptor((ParcelFileDescriptor) null);
}
private void configureDeviceSystemRulesVersion(String systemRulesVersion) throws Exception { private void configureDeviceSystemRulesVersion(String systemRulesVersion) throws Exception {
when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn(systemRulesVersion); when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn(systemRulesVersion);
} }
@@ -870,6 +829,10 @@ public class RulesManagerServiceTest {
.thenThrow(new IOException("Simulated failure")); .thenThrow(new IOException("Simulated failure"));
} }
private static void assertClosed(ParcelFileDescriptor parcelFileDescriptor) {
assertFalse(parcelFileDescriptor.getFileDescriptor().valid());
}
private static class FakeExecutor implements Executor { private static class FakeExecutor implements Executor {
private Runnable mLastCommand; private Runnable mLastCommand;
@@ -926,4 +889,17 @@ public class RulesManagerServiceTest {
fail("Unexpected call"); fail("Unexpected call");
} }
} }
private static ParcelFileDescriptor createParcelFileDescriptor(byte[] bytes)
throws IOException {
File file = File.createTempFile("pfd", null);
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(bytes);
}
ParcelFileDescriptor pfd =
ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
// This should now be safe to delete. The ParcelFileDescriptor has an open fd.
file.delete();
return pfd;
}
} }