/* * 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.utils.blob; import static com.google.common.truth.Truth.assertThat; import android.app.blob.BlobHandle; import android.app.blob.BlobStoreManager; import android.app.blob.LeaseInfo; import android.content.Context; import android.content.res.Resources; import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.uiautomator.UiDevice; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.util.Random; public class Utils { public static final String TAG = "BlobStoreTest"; public static final int BUFFER_SIZE_BYTES = 16 * 1024; public static final long KB_IN_BYTES = 1000; public static final long MB_IN_BYTES = KB_IN_BYTES * 1000; public static void copy(InputStream in, OutputStream out, long lengthBytes) throws IOException { 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 static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input) throws IOException { try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) { try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream( session.openWrite(0, -1))) { FileUtils.copy(in, out); } } } public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input, long lengthBytes) throws IOException { try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) { writeToSession(session, in, 0, lengthBytes, lengthBytes); } } public static void writeToSession(BlobStoreManager.Session session, FileInputStream in, long offsetBytes, long lengthBytes, long allocateBytes) throws IOException { in.getChannel().position(offsetBytes); try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream( session.openWrite(offsetBytes, allocateBytes))) { copy(in, out, lengthBytes); } } public static void assertLeasedBlobs(BlobStoreManager blobStoreManager, BlobHandle... expectedBlobHandles) throws IOException { assertThat(blobStoreManager.getLeasedBlobs()).containsExactly( (Object[]) expectedBlobHandles); } public static void assertNoLeasedBlobs(BlobStoreManager blobStoreManager) throws IOException { assertThat(blobStoreManager.getLeasedBlobs()).isEmpty(); } public static void acquireLease(Context context, BlobHandle blobHandle, CharSequence description) throws IOException { final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService( Context.BLOB_STORE_SERVICE); blobStoreManager.acquireLease(blobHandle, description); final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle); assertLeaseInfo(leaseInfo, context.getPackageName(), 0, Resources.ID_NULL, description); } public static void acquireLease(Context context, BlobHandle blobHandle, int descriptionResId) throws IOException { final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService( Context.BLOB_STORE_SERVICE); blobStoreManager.acquireLease(blobHandle, descriptionResId); final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle); assertLeaseInfo(leaseInfo, context.getPackageName(), 0, descriptionResId, context.getString(descriptionResId)); } public static void acquireLease(Context context, BlobHandle blobHandle, CharSequence description, long expiryTimeMs) throws IOException { final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService( Context.BLOB_STORE_SERVICE); blobStoreManager.acquireLease(blobHandle, description, expiryTimeMs); final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle); assertLeaseInfo(leaseInfo, context.getPackageName(), expiryTimeMs, Resources.ID_NULL, description); } public static void acquireLease(Context context, BlobHandle blobHandle, int descriptionResId, long expiryTimeMs) throws IOException { final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService( Context.BLOB_STORE_SERVICE); blobStoreManager.acquireLease(blobHandle, descriptionResId, expiryTimeMs); final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle); assertLeaseInfo(leaseInfo, context.getPackageName(), expiryTimeMs, descriptionResId, context.getString(descriptionResId)); } public static void releaseLease(Context context, BlobHandle blobHandle) throws IOException { final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService( Context.BLOB_STORE_SERVICE); blobStoreManager.releaseLease(blobHandle); try { assertThat(blobStoreManager.getLeaseInfo(blobHandle)).isNull(); } catch (SecurityException e) { // Expected, ignore } } private static void assertLeaseInfo(LeaseInfo leaseInfo, String packageName, long expiryTimeMs, int descriptionResId, CharSequence description) { assertThat(leaseInfo.getPackageName()).isEqualTo(packageName); assertThat(leaseInfo.getExpiryTimeMillis()).isEqualTo(expiryTimeMs); assertThat(leaseInfo.getDescriptionResId()).isEqualTo(descriptionResId); assertThat(leaseInfo.getDescription()).isEqualTo(description); } public static void triggerIdleMaintenance() throws IOException { runShellCmd("cmd blob_store idle-maintenance"); } public static void writeRandomData(File file, long fileSizeBytes) throws Exception { writeRandomData(new RandomAccessFile(file, "rw"), new Random(0), fileSizeBytes); } public static void writeRandomData(RandomAccessFile file, Random random, long fileSizeBytes) throws Exception { long bytesWritten = 0; final byte[] buffer = new byte[BUFFER_SIZE_BYTES]; while (bytesWritten < fileSizeBytes) { random.nextBytes(buffer); final int toWrite = (bytesWritten + buffer.length <= fileSizeBytes) ? buffer.length : (int) (fileSizeBytes - bytesWritten); file.seek(bytesWritten); file.write(buffer, 0, toWrite); bytesWritten += toWrite; } } public static String runShellCmd(String cmd) throws IOException { final UiDevice uiDevice = UiDevice.getInstance( InstrumentationRegistry.getInstrumentation()); final String result = uiDevice.executeShellCommand(cmd).trim(); Log.i(TAG, "Output of '" + cmd + "': '" + result + "'"); return result; } }