blob: f96766a1d3ada880614d232e34b8c18998c925e5 [file] [log] [blame]
Sudheer Shankad4ea5e12020-02-04 16:42:17 -08001/*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.utils.blob;
17
18import static com.google.common.truth.Truth.assertThat;
19
20import android.app.blob.BlobHandle;
21import android.app.blob.BlobStoreManager;
22import android.content.Context;
23import android.os.FileUtils;
24import android.os.ParcelFileDescriptor;
25
26import java.io.BufferedInputStream;
27import java.io.File;
28import java.io.FileDescriptor;
29import java.io.FileInputStream;
30import java.io.FileOutputStream;
31import java.io.InputStream;
32import java.io.OutputStream;
33import java.io.RandomAccessFile;
34import java.nio.file.Files;
35import java.security.MessageDigest;
36import java.util.Random;
37import java.util.concurrent.TimeUnit;
38
39public class DummyBlobData {
40 private static final long DEFAULT_SIZE_BYTES = 10 * 1024L * 1024L;
41 private static final int BUFFER_SIZE_BYTES = 16 * 1024;
42
43 private final Context mContext;
44 private final Random mRandom;
45 private final File mFile;
46 private final long mFileSize;
47 private final String mLabel;
48
49 byte[] mFileDigest;
50 long mExpiryTimeMs;
51
52 public DummyBlobData(Context context) {
53 this(context, new Random(0), "blob_" + System.nanoTime());
54 }
55
56 public DummyBlobData(Context context, long fileSize) {
57 this(context, fileSize, new Random(0), "blob_" + System.nanoTime(), "Test label");
58 }
59
60 public DummyBlobData(Context context, Random random, String fileName) {
61 this(context, DEFAULT_SIZE_BYTES, random, fileName, "Test label");
62 }
63
64 public DummyBlobData(Context context, Random random, String fileName, String label) {
65 this(context, DEFAULT_SIZE_BYTES, random, fileName, label);
66 }
67
68 public DummyBlobData(Context context, long fileSize, Random random, String fileName,
69 String label) {
70 mContext = context;
71 mRandom = random;
72 mFile = new File(mContext.getFilesDir(), fileName);
73 mFileSize = fileSize;
74 mLabel = label;
75 }
76
77 public void prepare() throws Exception {
78 try (RandomAccessFile file = new RandomAccessFile(mFile, "rw")) {
79 writeRandomData(file, mFileSize);
80 }
81 mFileDigest = FileUtils.digest(mFile, "SHA-256");
82 mExpiryTimeMs = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
83 }
84
85 public BlobHandle getBlobHandle() throws Exception {
86 return BlobHandle.createWithSha256(createSha256Digest(mFile), mLabel,
87 mExpiryTimeMs, "test_tag");
88 }
89
90 public long getFileSize() throws Exception {
91 return mFileSize;
92 }
93
94 public long getExpiryTimeMillis() {
95 return mExpiryTimeMs;
96 }
97
98 public void delete() {
99 mFile.delete();
100 }
101
102 public void writeToSession(BlobStoreManager.Session session) throws Exception {
103 writeToSession(session, 0, mFileSize);
104 }
105
106 public void writeToSession(BlobStoreManager.Session session,
107 long offsetBytes, long lengthBytes) throws Exception {
108 try (FileInputStream in = new FileInputStream(mFile)) {
109 in.getChannel().position(offsetBytes);
110 try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
111 session.openWrite(offsetBytes, lengthBytes))) {
112 copy(in, out, lengthBytes);
113 }
114 }
115 }
116
117 public void writeToFd(FileDescriptor fd, long offsetBytes, long lengthBytes) throws Exception {
118 try (FileInputStream in = new FileInputStream(mFile)) {
119 in.getChannel().position(offsetBytes);
120 try (FileOutputStream out = new FileOutputStream(fd)) {
121 copy(in, out, lengthBytes);
122 }
123 }
124 }
125
126 private void copy(InputStream in, OutputStream out, long lengthBytes) throws Exception {
127 final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
128 long bytesWrittern = 0;
129 while (bytesWrittern < lengthBytes) {
130 final int toWrite = (bytesWrittern + buffer.length <= lengthBytes)
131 ? buffer.length : (int) (lengthBytes - bytesWrittern);
132 in.read(buffer, 0, toWrite);
133 out.write(buffer, 0, toWrite);
134 bytesWrittern += toWrite;
135 }
136 }
137
138 public void readFromSessionAndVerifyBytes(BlobStoreManager.Session session,
139 long offsetBytes, int lengthBytes) throws Exception {
140 final byte[] expectedBytes = new byte[lengthBytes];
141 try (FileInputStream in = new FileInputStream(mFile)) {
142 read(in, expectedBytes, offsetBytes, lengthBytes);
143 }
144
145 final byte[] actualBytes = new byte[lengthBytes];
146 try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
147 session.openWrite(0L, 0L))) {
148 read(in, actualBytes, offsetBytes, lengthBytes);
149 }
150
151 assertThat(actualBytes).isEqualTo(expectedBytes);
152
153 }
154
155 private void read(FileInputStream in, byte[] buffer,
156 long offsetBytes, int lengthBytes) throws Exception {
157 in.getChannel().position(offsetBytes);
158 in.read(buffer, 0, lengthBytes);
159 }
160
161 public void readFromSessionAndVerifyDigest(BlobStoreManager.Session session)
162 throws Exception {
163 readFromSessionAndVerifyDigest(session, 0, mFile.length());
164 }
165
166 public void readFromSessionAndVerifyDigest(BlobStoreManager.Session session,
167 long offsetBytes, long lengthBytes) throws Exception {
168 final byte[] actualDigest;
169 try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
170 session.openWrite(0L, 0L))) {
171 actualDigest = createSha256Digest(in, offsetBytes, lengthBytes);
172 }
173
174 assertThat(actualDigest).isEqualTo(mFileDigest);
175 }
176
177 public void verifyBlob(ParcelFileDescriptor pfd) throws Exception {
178 final byte[] actualDigest;
179 try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
180 actualDigest = FileUtils.digest(in, "SHA-256");
181 }
182 assertThat(actualDigest).isEqualTo(mFileDigest);
183 }
184
185 private byte[] createSha256Digest(FileInputStream in, long offsetBytes, long lengthBytes)
186 throws Exception {
187 final MessageDigest digest = MessageDigest.getInstance("SHA-256");
188 in.getChannel().position(offsetBytes);
189 final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
190 long bytesRead = 0;
191 while (bytesRead < lengthBytes) {
192 int toRead = (bytesRead + buffer.length <= lengthBytes)
193 ? buffer.length : (int) (lengthBytes - bytesRead);
194 toRead = in.read(buffer, 0, toRead);
195 digest.update(buffer, 0, toRead);
196 bytesRead += toRead;
197 }
198 return digest.digest();
199 }
200
201 private byte[] createSha256Digest(File file) throws Exception {
202 final MessageDigest digest = MessageDigest.getInstance("SHA-256");
203 try (BufferedInputStream in = new BufferedInputStream(
204 Files.newInputStream(file.toPath()))) {
205 final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
206 int bytesRead;
207 while ((bytesRead = in.read(buffer)) > 0) {
208 digest.update(buffer, 0, bytesRead);
209 }
210 }
211 return digest.digest();
212 }
213
214 private void writeRandomData(RandomAccessFile file, long fileSize)
215 throws Exception {
216 long bytesWritten = 0;
217 final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
218 while (bytesWritten < fileSize) {
219 mRandom.nextBytes(buffer);
220 final int toWrite = (bytesWritten + buffer.length <= fileSize)
221 ? buffer.length : (int) (fileSize - bytesWritten);
222 file.seek(bytesWritten);
223 file.write(buffer, 0, toWrite);
224 bytesWritten += toWrite;
225 }
226 }
227}