Add perf test to measure duration of blob store digest computation.
+ Move BlobStoreTestUtils from cts/ to frameworks/base/tests/. Bug: 148898557 Test: atest ./apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java Change-Id: I2de155d0c0c1fb602c57353ba4819bdc9cda8c0a
This commit is contained in:
28
apct-tests/perftests/blobstore/Android.bp
Normal file
28
apct-tests/perftests/blobstore/Android.bp
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2020 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.
|
||||
|
||||
android_test {
|
||||
name: "BlobStorePerfTests",
|
||||
srcs: ["src/**/*.java"],
|
||||
static_libs: [
|
||||
"BlobStoreTestUtils",
|
||||
"androidx.test.rules",
|
||||
"androidx.annotation_annotation",
|
||||
"apct-perftests-utils",
|
||||
"ub-uiautomator",
|
||||
],
|
||||
platform_apis: true,
|
||||
test_suites: ["device-tests"],
|
||||
certificate: "platform",
|
||||
}
|
||||
27
apct-tests/perftests/blobstore/AndroidManifest.xml
Normal file
27
apct-tests/perftests/blobstore/AndroidManifest.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2020 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.perftests.blob">
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.perftests.blob"/>
|
||||
|
||||
</manifest>
|
||||
28
apct-tests/perftests/blobstore/AndroidTest.xml
Normal file
28
apct-tests/perftests/blobstore/AndroidTest.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2020 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.
|
||||
-->
|
||||
<configuration description="Runs BlobStorePerfTests metric instrumentation.">
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<option name="test-suite-tag" value="apct-metric-instrumentation" />
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="test-file-name" value="BlobStorePerfTests.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||
<option name="package" value="com.android.perftests.blob" />
|
||||
<option name="hidden-api-checks" value="false"/>
|
||||
</test>
|
||||
</configuration>
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.perftests.blob;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.app.UiAutomation;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.perftests.utils.TraceMarkParser;
|
||||
import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
// Copy of com.android.frameworks.perftests.am.util.AtraceUtils. TODO: avoid this duplication.
|
||||
public class AtraceUtils {
|
||||
private static final String TAG = "AtraceUtils";
|
||||
private static final boolean VERBOSE = true;
|
||||
|
||||
private static final String ATRACE_START = "atrace --async_start -b %d -c %s";
|
||||
private static final String ATRACE_DUMP = "atrace --async_dump";
|
||||
private static final String ATRACE_STOP = "atrace --async_stop";
|
||||
private static final int DEFAULT_ATRACE_BUF_SIZE = 1024;
|
||||
|
||||
private UiAutomation mAutomation;
|
||||
private static AtraceUtils sUtils = null;
|
||||
private boolean mStarted = false;
|
||||
|
||||
private AtraceUtils(Instrumentation instrumentation) {
|
||||
mAutomation = instrumentation.getUiAutomation();
|
||||
}
|
||||
|
||||
public static AtraceUtils getInstance(Instrumentation instrumentation) {
|
||||
if (sUtils == null) {
|
||||
sUtils = new AtraceUtils(instrumentation);
|
||||
}
|
||||
return sUtils;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param categories The list of the categories to trace, separated with space.
|
||||
*/
|
||||
public void startTrace(String categories) {
|
||||
synchronized (this) {
|
||||
if (mStarted) {
|
||||
throw new IllegalStateException("atrace already started");
|
||||
}
|
||||
runShellCommand(String.format(
|
||||
ATRACE_START, DEFAULT_ATRACE_BUF_SIZE, categories));
|
||||
mStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void stopTrace() {
|
||||
synchronized (this) {
|
||||
mStarted = false;
|
||||
runShellCommand(ATRACE_STOP);
|
||||
}
|
||||
}
|
||||
|
||||
private String runShellCommand(String cmd) {
|
||||
try {
|
||||
return UiDevice.getInstance(
|
||||
InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parser The function that can accept the buffer of atrace dump and parse it.
|
||||
* @param handler The parse result handler
|
||||
*/
|
||||
public void performDump(TraceMarkParser parser,
|
||||
BiConsumer<String, List<TraceMarkSlice>> handler) {
|
||||
parser.reset();
|
||||
try {
|
||||
if (VERBOSE) {
|
||||
Log.i(TAG, "Collecting atrace dump...");
|
||||
}
|
||||
writeDataToBuf(mAutomation.executeShellCommand(ATRACE_DUMP), parser);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error in reading dump", e);
|
||||
}
|
||||
parser.forAllSlices(handler);
|
||||
}
|
||||
|
||||
// The given file descriptor here will be closed by this function
|
||||
private void writeDataToBuf(ParcelFileDescriptor pfDescriptor,
|
||||
TraceMarkParser parser) throws IOException {
|
||||
InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfDescriptor);
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
parser.visit(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2020 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.perftests.blob;
|
||||
|
||||
import android.app.blob.BlobStoreManager;
|
||||
import android.content.Context;
|
||||
import android.perftests.utils.ManualBenchmarkState;
|
||||
import android.perftests.utils.PerfManualStatusReporter;
|
||||
import android.perftests.utils.TraceMarkParser;
|
||||
import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.utils.blob.DummyBlobData;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
public class BlobStorePerfTests {
|
||||
// From frameworks/native/cmds/atrace/atrace.cpp
|
||||
private static final String ATRACE_CATEGORY_SYSTEM_SERVER = "ss";
|
||||
// From f/b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
|
||||
private static final String ATRACE_COMPUTE_DIGEST_PREFIX = "computeBlobDigest-";
|
||||
|
||||
private Context mContext;
|
||||
private BlobStoreManager mBlobStoreManager;
|
||||
private AtraceUtils mAtraceUtils;
|
||||
private ManualBenchmarkState mState;
|
||||
|
||||
@Rule
|
||||
public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
|
||||
|
||||
@Parameterized.Parameter(0)
|
||||
public int fileSizeInMb;
|
||||
|
||||
@Parameterized.Parameters(name = "{0}MB")
|
||||
public static Collection<Object[]> getParameters() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ 25 },
|
||||
{ 50 },
|
||||
{ 100 },
|
||||
{ 200 },
|
||||
});
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
mBlobStoreManager = (BlobStoreManager) mContext.getSystemService(
|
||||
Context.BLOB_STORE_SERVICE);
|
||||
mAtraceUtils = AtraceUtils.getInstance(InstrumentationRegistry.getInstrumentation());
|
||||
mState = mPerfManualStatusReporter.getBenchmarkState();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
// TODO: Add a blob_store shell command to trigger idle maintenance to avoid hardcoding
|
||||
// job id like this.
|
||||
// From BlobStoreConfig.IDLE_JOB_ID = 191934935.
|
||||
runShellCommand("cmd jobscheduler run -f android 191934935");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComputeDigest() throws Exception {
|
||||
mAtraceUtils.startTrace(ATRACE_CATEGORY_SYSTEM_SERVER);
|
||||
try {
|
||||
final List<Long> durations = new ArrayList<>();
|
||||
final DummyBlobData blobData = prepareDataBlob(fileSizeInMb);
|
||||
final TraceMarkParser parser = new TraceMarkParser(
|
||||
line -> line.name.startsWith(ATRACE_COMPUTE_DIGEST_PREFIX));
|
||||
while (mState.keepRunning(durations)) {
|
||||
commitBlob(blobData);
|
||||
|
||||
durations.clear();
|
||||
collectDigestDurationsFromTrace(parser, durations);
|
||||
// get and delete blobId
|
||||
}
|
||||
} finally {
|
||||
mAtraceUtils.stopTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void collectDigestDurationsFromTrace(TraceMarkParser parser, List<Long> durations) {
|
||||
mAtraceUtils.performDump(parser, (key, slices) -> {
|
||||
for (TraceMarkSlice slice : slices) {
|
||||
durations.add(TimeUnit.MICROSECONDS.toNanos(slice.getDurationInMicroseconds()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DummyBlobData prepareDataBlob(int fileSizeInMb) throws Exception {
|
||||
final DummyBlobData blobData = new DummyBlobData(mContext,
|
||||
fileSizeInMb * 1024 * 1024 /* bytes */);
|
||||
blobData.prepare();
|
||||
return blobData;
|
||||
}
|
||||
|
||||
private void commitBlob(DummyBlobData blobData) throws Exception {
|
||||
final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
|
||||
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
|
||||
blobData.writeToSession(session);
|
||||
final CompletableFuture<Integer> callback = new CompletableFuture<>();
|
||||
session.commit(mContext.getMainExecutor(), callback::complete);
|
||||
// Ignore commit callback result.
|
||||
callback.get();
|
||||
}
|
||||
}
|
||||
|
||||
private String runShellCommand(String cmd) {
|
||||
try {
|
||||
return UiDevice.getInstance(
|
||||
InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import static android.app.blob.XmlTags.ATTR_PACKAGE;
|
||||
import static android.app.blob.XmlTags.ATTR_UID;
|
||||
import static android.app.blob.XmlTags.TAG_ACCESS_MODE;
|
||||
import static android.app.blob.XmlTags.TAG_BLOB_HANDLE;
|
||||
import static android.os.Trace.TRACE_TAG_SYSTEM_SERVER;
|
||||
import static android.system.OsConstants.O_CREAT;
|
||||
import static android.system.OsConstants.O_RDONLY;
|
||||
import static android.system.OsConstants.O_RDWR;
|
||||
@@ -41,6 +42,7 @@ import android.os.FileUtils;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.os.RevocableFileDescriptor;
|
||||
import android.os.Trace;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
@@ -382,9 +384,13 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
|
||||
void verifyBlobData() {
|
||||
byte[] actualDigest = null;
|
||||
try {
|
||||
Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER,
|
||||
"computeBlobDigest-i" + mSessionId + "-l" + getSessionFile().length());
|
||||
actualDigest = FileUtils.digest(getSessionFile(), mBlobHandle.algorithm);
|
||||
} catch (IOException | NoSuchAlgorithmException e) {
|
||||
Slog.e(TAG, "Error computing the digest", e);
|
||||
} finally {
|
||||
Trace.traceEnd(TRACE_TAG_SYSTEM_SERVER);
|
||||
}
|
||||
synchronized (mSessionLock) {
|
||||
if (actualDigest != null && Arrays.equals(actualDigest, mBlobHandle.digest)) {
|
||||
|
||||
20
tests/BlobStoreTestUtils/Android.bp
Normal file
20
tests/BlobStoreTestUtils/Android.bp
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (C) 2020 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.
|
||||
|
||||
java_library {
|
||||
name: "BlobStoreTestUtils",
|
||||
srcs: ["src/**/*.java"],
|
||||
static_libs: ["truth-prebuilt"],
|
||||
platform_apis: true
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2020 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.utils.blob;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.blob.BlobHandle;
|
||||
import android.app.blob.BlobStoreManager;
|
||||
import android.content.Context;
|
||||
import android.os.FileUtils;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.Files;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DummyBlobData {
|
||||
private static final long DEFAULT_SIZE_BYTES = 10 * 1024L * 1024L;
|
||||
private static final int BUFFER_SIZE_BYTES = 16 * 1024;
|
||||
|
||||
private final Context mContext;
|
||||
private final Random mRandom;
|
||||
private final File mFile;
|
||||
private final long mFileSize;
|
||||
private final String mLabel;
|
||||
|
||||
byte[] mFileDigest;
|
||||
long mExpiryTimeMs;
|
||||
|
||||
public DummyBlobData(Context context) {
|
||||
this(context, new Random(0), "blob_" + System.nanoTime());
|
||||
}
|
||||
|
||||
public DummyBlobData(Context context, long fileSize) {
|
||||
this(context, fileSize, new Random(0), "blob_" + System.nanoTime(), "Test label");
|
||||
}
|
||||
|
||||
public DummyBlobData(Context context, Random random, String fileName) {
|
||||
this(context, DEFAULT_SIZE_BYTES, random, fileName, "Test label");
|
||||
}
|
||||
|
||||
public DummyBlobData(Context context, Random random, String fileName, String label) {
|
||||
this(context, DEFAULT_SIZE_BYTES, random, fileName, label);
|
||||
}
|
||||
|
||||
public DummyBlobData(Context context, long fileSize, Random random, String fileName,
|
||||
String label) {
|
||||
mContext = context;
|
||||
mRandom = random;
|
||||
mFile = new File(mContext.getFilesDir(), fileName);
|
||||
mFileSize = fileSize;
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public void prepare() throws Exception {
|
||||
try (RandomAccessFile file = new RandomAccessFile(mFile, "rw")) {
|
||||
writeRandomData(file, mFileSize);
|
||||
}
|
||||
mFileDigest = FileUtils.digest(mFile, "SHA-256");
|
||||
mExpiryTimeMs = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
|
||||
}
|
||||
|
||||
public BlobHandle getBlobHandle() throws Exception {
|
||||
return BlobHandle.createWithSha256(createSha256Digest(mFile), mLabel,
|
||||
mExpiryTimeMs, "test_tag");
|
||||
}
|
||||
|
||||
public long getFileSize() throws Exception {
|
||||
return mFileSize;
|
||||
}
|
||||
|
||||
public long getExpiryTimeMillis() {
|
||||
return mExpiryTimeMs;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
mFile.delete();
|
||||
}
|
||||
|
||||
public void writeToSession(BlobStoreManager.Session session) throws Exception {
|
||||
writeToSession(session, 0, mFileSize);
|
||||
}
|
||||
|
||||
public void writeToSession(BlobStoreManager.Session session,
|
||||
long offsetBytes, long lengthBytes) throws Exception {
|
||||
try (FileInputStream in = new FileInputStream(mFile)) {
|
||||
in.getChannel().position(offsetBytes);
|
||||
try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
|
||||
session.openWrite(offsetBytes, lengthBytes))) {
|
||||
copy(in, out, lengthBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writeToFd(FileDescriptor fd, long offsetBytes, long lengthBytes) throws Exception {
|
||||
try (FileInputStream in = new FileInputStream(mFile)) {
|
||||
in.getChannel().position(offsetBytes);
|
||||
try (FileOutputStream out = new FileOutputStream(fd)) {
|
||||
copy(in, out, lengthBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copy(InputStream in, OutputStream out, long lengthBytes) throws Exception {
|
||||
final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
|
||||
long bytesWrittern = 0;
|
||||
while (bytesWrittern < lengthBytes) {
|
||||
final int toWrite = (bytesWrittern + buffer.length <= lengthBytes)
|
||||
? buffer.length : (int) (lengthBytes - bytesWrittern);
|
||||
in.read(buffer, 0, toWrite);
|
||||
out.write(buffer, 0, toWrite);
|
||||
bytesWrittern += toWrite;
|
||||
}
|
||||
}
|
||||
|
||||
public void readFromSessionAndVerifyBytes(BlobStoreManager.Session session,
|
||||
long offsetBytes, int lengthBytes) throws Exception {
|
||||
final byte[] expectedBytes = new byte[lengthBytes];
|
||||
try (FileInputStream in = new FileInputStream(mFile)) {
|
||||
read(in, expectedBytes, offsetBytes, lengthBytes);
|
||||
}
|
||||
|
||||
final byte[] actualBytes = new byte[lengthBytes];
|
||||
try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
|
||||
session.openWrite(0L, 0L))) {
|
||||
read(in, actualBytes, offsetBytes, lengthBytes);
|
||||
}
|
||||
|
||||
assertThat(actualBytes).isEqualTo(expectedBytes);
|
||||
|
||||
}
|
||||
|
||||
private void read(FileInputStream in, byte[] buffer,
|
||||
long offsetBytes, int lengthBytes) throws Exception {
|
||||
in.getChannel().position(offsetBytes);
|
||||
in.read(buffer, 0, lengthBytes);
|
||||
}
|
||||
|
||||
public void readFromSessionAndVerifyDigest(BlobStoreManager.Session session)
|
||||
throws Exception {
|
||||
readFromSessionAndVerifyDigest(session, 0, mFile.length());
|
||||
}
|
||||
|
||||
public void readFromSessionAndVerifyDigest(BlobStoreManager.Session session,
|
||||
long offsetBytes, long lengthBytes) throws Exception {
|
||||
final byte[] actualDigest;
|
||||
try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
|
||||
session.openWrite(0L, 0L))) {
|
||||
actualDigest = createSha256Digest(in, offsetBytes, lengthBytes);
|
||||
}
|
||||
|
||||
assertThat(actualDigest).isEqualTo(mFileDigest);
|
||||
}
|
||||
|
||||
public void verifyBlob(ParcelFileDescriptor pfd) throws Exception {
|
||||
final byte[] actualDigest;
|
||||
try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
|
||||
actualDigest = FileUtils.digest(in, "SHA-256");
|
||||
}
|
||||
assertThat(actualDigest).isEqualTo(mFileDigest);
|
||||
}
|
||||
|
||||
private byte[] createSha256Digest(FileInputStream in, long offsetBytes, long lengthBytes)
|
||||
throws Exception {
|
||||
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
in.getChannel().position(offsetBytes);
|
||||
final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
|
||||
long bytesRead = 0;
|
||||
while (bytesRead < lengthBytes) {
|
||||
int toRead = (bytesRead + buffer.length <= lengthBytes)
|
||||
? buffer.length : (int) (lengthBytes - bytesRead);
|
||||
toRead = in.read(buffer, 0, toRead);
|
||||
digest.update(buffer, 0, toRead);
|
||||
bytesRead += toRead;
|
||||
}
|
||||
return digest.digest();
|
||||
}
|
||||
|
||||
private byte[] createSha256Digest(File file) throws Exception {
|
||||
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
try (BufferedInputStream in = new BufferedInputStream(
|
||||
Files.newInputStream(file.toPath()))) {
|
||||
final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
|
||||
int bytesRead;
|
||||
while ((bytesRead = in.read(buffer)) > 0) {
|
||||
digest.update(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
return digest.digest();
|
||||
}
|
||||
|
||||
private void writeRandomData(RandomAccessFile file, long fileSize)
|
||||
throws Exception {
|
||||
long bytesWritten = 0;
|
||||
final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
|
||||
while (bytesWritten < fileSize) {
|
||||
mRandom.nextBytes(buffer);
|
||||
final int toWrite = (bytesWritten + buffer.length <= fileSize)
|
||||
? buffer.length : (int) (fileSize - bytesWritten);
|
||||
file.seek(bytesWritten);
|
||||
file.write(buffer, 0, toWrite);
|
||||
bytesWritten += toWrite;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user