1 /*
2  * Copyright (C) 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  */
16 
17 package com.android.utils.blob;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import android.app.blob.BlobHandle;
22 import android.app.blob.BlobStoreManager;
23 import android.app.blob.LeaseInfo;
24 import android.content.Context;
25 import android.content.res.Resources;
26 import android.os.FileUtils;
27 import android.os.ParcelFileDescriptor;
28 import android.util.Log;
29 
30 import androidx.test.platform.app.InstrumentationRegistry;
31 import androidx.test.uiautomator.UiDevice;
32 
33 import java.io.File;
34 import java.io.FileInputStream;
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.OutputStream;
39 import java.io.RandomAccessFile;
40 import java.util.Random;
41 
42 public class Utils {
43     public static final String TAG = "BlobStoreTest";
44 
45     public static final int BUFFER_SIZE_BYTES = 16 * 1024;
46 
47     public static final long KB_IN_BYTES = 1000;
48     public static final long MB_IN_BYTES = KB_IN_BYTES * 1000;
49 
copy(InputStream in, OutputStream out, long lengthBytes)50     public static void copy(InputStream in, OutputStream out, long lengthBytes)
51             throws IOException {
52         final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
53         long bytesWrittern = 0;
54         while (bytesWrittern < lengthBytes) {
55             final int toWrite = (bytesWrittern + buffer.length <= lengthBytes)
56                     ? buffer.length : (int) (lengthBytes - bytesWrittern);
57             in.read(buffer, 0, toWrite);
58             out.write(buffer, 0, toWrite);
59             bytesWrittern += toWrite;
60         }
61     }
62 
writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input)63     public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input)
64             throws IOException {
65         try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) {
66             try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
67                     session.openWrite(0, -1))) {
68                 FileUtils.copy(in, out);
69             }
70         }
71     }
72 
writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input, long lengthBytes)73     public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input,
74             long lengthBytes) throws IOException {
75         try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) {
76             writeToSession(session, in, 0, lengthBytes, lengthBytes);
77         }
78     }
79 
writeToSession(BlobStoreManager.Session session, FileInputStream in, long offsetBytes, long lengthBytes, long allocateBytes)80     public static void writeToSession(BlobStoreManager.Session session, FileInputStream in,
81             long offsetBytes, long lengthBytes, long allocateBytes) throws IOException {
82         in.getChannel().position(offsetBytes);
83         try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
84                 session.openWrite(offsetBytes, allocateBytes))) {
85             copy(in, out, lengthBytes);
86         }
87     }
88 
assertLeasedBlobs(BlobStoreManager blobStoreManager, BlobHandle... expectedBlobHandles)89     public static void assertLeasedBlobs(BlobStoreManager blobStoreManager,
90             BlobHandle... expectedBlobHandles) throws IOException {
91         assertThat(blobStoreManager.getLeasedBlobs()).containsExactly(
92                 (Object[]) expectedBlobHandles);
93     }
94 
assertNoLeasedBlobs(BlobStoreManager blobStoreManager)95     public static void assertNoLeasedBlobs(BlobStoreManager blobStoreManager)
96             throws IOException {
97         assertThat(blobStoreManager.getLeasedBlobs()).isEmpty();
98     }
99 
acquireLease(Context context, BlobHandle blobHandle, CharSequence description)100     public static void acquireLease(Context context,
101             BlobHandle blobHandle, CharSequence description) throws IOException {
102         final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
103                 Context.BLOB_STORE_SERVICE);
104         blobStoreManager.acquireLease(blobHandle, description);
105 
106         final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
107         assertLeaseInfo(leaseInfo, context.getPackageName(), 0,
108                 Resources.ID_NULL, description);
109     }
110 
acquireLease(Context context, BlobHandle blobHandle, int descriptionResId)111     public static void acquireLease(Context context,
112             BlobHandle blobHandle, int descriptionResId) throws IOException {
113         final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
114                 Context.BLOB_STORE_SERVICE);
115         blobStoreManager.acquireLease(blobHandle, descriptionResId);
116 
117         final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
118         assertLeaseInfo(leaseInfo, context.getPackageName(), 0,
119                 descriptionResId, context.getString(descriptionResId));
120     }
121 
acquireLease(Context context, BlobHandle blobHandle, CharSequence description, long expiryTimeMs)122     public static void acquireLease(Context context,
123             BlobHandle blobHandle, CharSequence description,
124             long expiryTimeMs) throws IOException {
125         final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
126                 Context.BLOB_STORE_SERVICE);
127         blobStoreManager.acquireLease(blobHandle, description, expiryTimeMs);
128 
129         final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
130         assertLeaseInfo(leaseInfo, context.getPackageName(), expiryTimeMs,
131                 Resources.ID_NULL, description);
132     }
133 
acquireLease(Context context, BlobHandle blobHandle, int descriptionResId, long expiryTimeMs)134     public static void acquireLease(Context context,
135             BlobHandle blobHandle, int descriptionResId,
136             long expiryTimeMs) throws IOException {
137         final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
138                 Context.BLOB_STORE_SERVICE);
139         blobStoreManager.acquireLease(blobHandle, descriptionResId, expiryTimeMs);
140 
141         final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
142         assertLeaseInfo(leaseInfo, context.getPackageName(), expiryTimeMs,
143                 descriptionResId, context.getString(descriptionResId));
144     }
145 
releaseLease(Context context, BlobHandle blobHandle)146     public static void releaseLease(Context context,
147             BlobHandle blobHandle) throws IOException {
148         final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
149                 Context.BLOB_STORE_SERVICE);
150         blobStoreManager.releaseLease(blobHandle);
151         try {
152             assertThat(blobStoreManager.getLeaseInfo(blobHandle)).isNull();
153         } catch (SecurityException e) {
154             // Expected, ignore
155         }
156     }
157 
assertLeaseInfo(LeaseInfo leaseInfo, String packageName, long expiryTimeMs, int descriptionResId, CharSequence description)158     private static void assertLeaseInfo(LeaseInfo leaseInfo, String packageName,
159             long expiryTimeMs, int descriptionResId, CharSequence description) {
160         assertThat(leaseInfo.getPackageName()).isEqualTo(packageName);
161         assertThat(leaseInfo.getExpiryTimeMillis()).isEqualTo(expiryTimeMs);
162         assertThat(leaseInfo.getDescriptionResId()).isEqualTo(descriptionResId);
163         assertThat(leaseInfo.getDescription()).isEqualTo(description);
164     }
165 
triggerIdleMaintenance()166     public static void triggerIdleMaintenance() throws IOException {
167         runShellCmd("cmd blob_store idle-maintenance");
168     }
169 
writeRandomData(File file, long fileSizeBytes)170     public static void writeRandomData(File file, long fileSizeBytes)
171             throws Exception {
172         writeRandomData(new RandomAccessFile(file, "rw"), new Random(0), fileSizeBytes);
173     }
174 
writeRandomData(RandomAccessFile file, Random random, long fileSizeBytes)175     public static void writeRandomData(RandomAccessFile file, Random random, long fileSizeBytes)
176             throws Exception {
177         long bytesWritten = 0;
178         final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
179         while (bytesWritten < fileSizeBytes) {
180             random.nextBytes(buffer);
181             final int toWrite = (bytesWritten + buffer.length <= fileSizeBytes)
182                     ? buffer.length : (int) (fileSizeBytes - bytesWritten);
183             file.seek(bytesWritten);
184             file.write(buffer, 0, toWrite);
185             bytesWritten += toWrite;
186         }
187     }
188 
runShellCmd(String cmd)189     public static String runShellCmd(String cmd) throws IOException {
190         final UiDevice uiDevice = UiDevice.getInstance(
191                 InstrumentationRegistry.getInstrumentation());
192         final String result = uiDevice.executeShellCommand(cmd).trim();
193         Log.i(TAG, "Output of '" + cmd + "': '" + result + "'");
194         return result;
195     }
196 }
197