blob: ddb3c172b219df93cebfa2e32b354e3a2b9d555e [file] [log] [blame]
/*
* Copyright (C) 2019 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 android.app.cts;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.webkit.cts.CtsTestServer;
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.PollingCheck;
import org.junit.After;
import org.junit.Before;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashSet;
public class DownloadManagerTestBase {
protected static final String TAG = "DownloadManagerTest";
/**
* According to the CDD Section 7.6.1, the DownloadManager implementation must be able to
* download individual files of 100 MB.
*/
protected static final int MINIMUM_DOWNLOAD_BYTES = 100 * 1024 * 1024;
protected static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
protected static final long LONG_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
protected Context mContext;
protected DownloadManager mDownloadManager;
private CtsTestServer mWebServer;
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
mWebServer = new CtsTestServer(mContext);
clearDownloads();
}
@After
public void tearDown() throws Exception {
mWebServer.shutdown();
clearDownloads();
}
protected void updateUri(Uri uri, String column, String value) throws Exception {
final String cmd = String.format("content update --uri %s --bind %s:s:%s",
uri, column, value);
final String res = runShellCommand(cmd).trim();
assertTrue(res, TextUtils.isEmpty(res));
}
protected static byte[] hash(InputStream in) throws Exception {
try (DigestInputStream digestIn = new DigestInputStream(in,
MessageDigest.getInstance("SHA-1"));
OutputStream out = new FileOutputStream(new File("/dev/null"))) {
FileUtils.copy(digestIn, out);
return digestIn.getMessageDigest().digest();
} finally {
FileUtils.closeQuietly(in);
}
}
protected static Uri getMediaStoreUri(Uri downloadUri) throws Exception {
final Context context = InstrumentationRegistry.getTargetContext();
Cursor cursor = context.getContentResolver().query(downloadUri, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
// DownloadManager.COLUMN_MEDIASTORE_URI is not a column in the query result.
// COLUMN_MEDIAPROVIDER_URI value maybe the same as COLUMN_MEDIASTORE_URI but NOT
// guaranteed.
int index = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_MEDIAPROVIDER_URI);
return Uri.parse(cursor.getString(index));
} else {
throw new FileNotFoundException("Failed to find entry for " + downloadUri);
}
}
private static int getUserId() {
return Process.myUserHandle().getIdentifier();
}
protected static String getRawFilePath(Uri uri) throws Exception {
return getFileData(uri, "_data");
}
private static String getFileData(Uri uri, String projection) throws Exception {
final Context context = InstrumentationRegistry.getTargetContext();
final String[] projections = new String[] { projection };
Cursor c = context.getContentResolver().query(uri, projections, null, null, null);
if (c != null && c.getCount() > 0) {
c.moveToFirst();
return c.getString(0);
} else {
String msg = String.format("Failed to find %s for %s", projection, uri);
throw new FileNotFoundException(msg);
}
}
protected static String readContentsFromUri(Uri uri) throws Exception {
final Context context = InstrumentationRegistry.getTargetContext();
try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) {
return readFromInputStream(inputStream);
}
}
protected static String readFromRawFile(String filePath) throws Exception {
Log.d(TAG, "Reading form file: " + filePath);
return readFromFile(
ParcelFileDescriptor.open(new File(filePath), ParcelFileDescriptor.MODE_READ_ONLY));
}
protected static String readFromFile(ParcelFileDescriptor pfd) throws Exception {
BufferedReader br = null;
try (final InputStream in = new FileInputStream(pfd.getFileDescriptor())) {
br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
String str;
StringBuilder out = new StringBuilder();
while ((str = br.readLine()) != null) {
out.append(str);
}
return out.toString();
} finally {
if (br != null) {
br.close();
}
}
}
protected static File createFile(File baseDir, String fileName) {
if (!baseDir.exists()) {
baseDir.mkdirs();
}
return new File(baseDir, fileName);
}
protected static void deleteFromShell(File file) {
runShellCommand("rm " + file);
}
protected static void writeToFile(File file, String contents) throws Exception {
file.getParentFile().mkdirs();
file.delete();
try (final PrintWriter out = new PrintWriter(file)) {
out.print(contents);
}
final String actual;
try (FileInputStream fis = new FileInputStream(file)) {
actual = readFromInputStream(fis);
}
assertEquals(contents, actual);
}
protected static void writeToFileFromShell(File file, String contents) throws Exception {
runShellCommand("mkdir -p " + file.getParentFile());
runShellCommand("rm " + file);
final String cmd = "dd of=" + file.getAbsolutePath();
final ParcelFileDescriptor[] pfds = InstrumentationRegistry.getInstrumentation()
.getUiAutomation().executeShellCommandRw(cmd);
try (final PrintWriter out =
new PrintWriter(new ParcelFileDescriptor.AutoCloseOutputStream(pfds[1]))) {
out.print(contents);
}
final String res;
try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfds[0])) {
res = readFromInputStream(fis);
}
Log.d(TAG, "Output of '" + cmd + "': '" + res + "'");
runShellCommand("sync");
assertFileContents(file, contents);
}
private static String readFromInputStream(InputStream inputStream) throws Exception {
final StringBuffer res = new StringBuffer();
final byte[] buf = new byte[512];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) != -1) {
res.append(new String(buf, 0, bytesRead));
}
return res.toString();
}
protected static void assertFileContents(File file, String contents) {
final String cmd = "cat " + file.getAbsolutePath();
final String output = runShellCommand(cmd);
Log.d(TAG, "Output of '" + cmd + "': '" + output + "'");
assertEquals(contents, output);
}
protected void clearDownloads() {
if (getTotalNumberDownloads() > 0) {
Cursor cursor = null;
try {
DownloadManager.Query query = new DownloadManager.Query();
cursor = mDownloadManager.query(query);
int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
long[] removeIds = new long[cursor.getCount()];
for (int i = 0; cursor.moveToNext(); i++) {
removeIds[i] = cursor.getLong(columnIndex);
}
assertEquals(removeIds.length, mDownloadManager.remove(removeIds));
assertEquals(0, getTotalNumberDownloads());
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
protected Uri getGoodUrl() {
return Uri.parse(mWebServer.getTestDownloadUrl("cts-good-download", 0));
}
protected Uri getBadUrl() {
return Uri.parse(mWebServer.getBaseUri() + "/nosuchurl");
}
protected Uri getMinimumDownloadUrl() {
return Uri.parse(mWebServer.getTestDownloadUrl("cts-minimum-download",
MINIMUM_DOWNLOAD_BYTES));
}
protected Uri getAssetUrl(String asset) {
return Uri.parse(mWebServer.getAssetUrl(asset));
}
protected int getTotalNumberDownloads() {
Cursor cursor = null;
try {
DownloadManager.Query query = new DownloadManager.Query();
cursor = mDownloadManager.query(query);
return cursor.getCount();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
protected void assertDownloadQueryableById(long downloadId) {
Cursor cursor = null;
try {
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
cursor = mDownloadManager.query(query);
assertEquals(1, cursor.getCount());
} finally {
if (cursor != null) {
cursor.close();
}
}
}
protected void assertDownloadQueryableByStatus(final int status) {
new PollingCheck() {
@Override
protected boolean check() {
Cursor cursor= null;
try {
DownloadManager.Query query = new DownloadManager.Query().setFilterByStatus(status);
cursor = mDownloadManager.query(query);
return 1 == cursor.getCount();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}.run();
}
protected void assertSuccessfulDownload(long id, File location) throws Exception {
Cursor cursor = null;
try {
final File expectedLocation = location.getCanonicalFile();
cursor = mDownloadManager.query(new DownloadManager.Query().setFilterById(id));
assertTrue(cursor.moveToNext());
assertEquals(DownloadManager.STATUS_SUCCESSFUL, cursor.getInt(
cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)));
assertEquals(Uri.fromFile(expectedLocation).toString(),
cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)));
assertTrue(expectedLocation.exists());
} finally {
if (cursor != null) {
cursor.close();
}
}
}
protected void assertRemoveDownload(long removeId, int expectedNumDownloads) {
Cursor cursor = null;
try {
assertEquals(1, mDownloadManager.remove(removeId));
DownloadManager.Query query = new DownloadManager.Query();
cursor = mDownloadManager.query(query);
assertEquals(expectedNumDownloads, cursor.getCount());
} finally {
if (cursor != null) {
cursor.close();
}
}
}
protected boolean hasInternetConnection() {
final PackageManager pm = mContext.getPackageManager();
return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
|| pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
|| pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
}
public static class DownloadCompleteReceiver extends BroadcastReceiver {
private HashSet<Long> mCompleteIds = new HashSet<>();
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mCompleteIds) {
mCompleteIds.add(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1));
mCompleteIds.notifyAll();
}
}
private boolean isCompleteLocked(long... ids) {
for (long id : ids) {
if (!mCompleteIds.contains(id)) {
return false;
}
}
return true;
}
public void waitForDownloadComplete(long timeoutMillis, long... waitForIds)
throws InterruptedException {
if (waitForIds.length == 0) {
throw new IllegalArgumentException("Missing IDs to wait for");
}
final long startTime = SystemClock.elapsedRealtime();
do {
synchronized (mCompleteIds) {
mCompleteIds.wait(timeoutMillis);
if (isCompleteLocked(waitForIds)) return;
}
} while ((SystemClock.elapsedRealtime() - startTime) < timeoutMillis);
throw new InterruptedException("Timeout waiting for IDs " + Arrays.toString(waitForIds)
+ "; received " + mCompleteIds.toString()
+ ". Make sure you have WiFi or some other connectivity for this test.");
}
}
}