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