1 /* 2 * Copyright (C) 2012 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.cts.externalstorageapp; 18 19 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoAccess; 20 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoWriteAccess; 21 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadWriteAccess; 22 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificPaths; 23 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getMountPaths; 24 25 import android.app.DownloadManager; 26 import android.app.DownloadManager.Query; 27 import android.app.DownloadManager.Request; 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.database.Cursor; 33 import android.net.Uri; 34 import android.os.Environment; 35 import android.os.SystemClock; 36 import android.test.AndroidTestCase; 37 import android.text.format.DateUtils; 38 39 import java.io.BufferedReader; 40 import java.io.File; 41 import java.io.FileReader; 42 import java.util.Arrays; 43 import java.util.HashSet; 44 import java.util.List; 45 46 /** 47 * Test external storage from an application that has no external storage 48 * permissions. 49 */ 50 public class ExternalStorageTest extends AndroidTestCase { 51 52 public void testPrimaryNoAccess() throws Exception { 53 assertDirNoAccess(Environment.getExternalStorageDirectory()); 54 } 55 56 /** 57 * Verify that above our package directories we always have no access. 58 */ 59 public void testAllWalkingUpTreeNoAccess() throws Exception { 60 final List<File> paths = getAllPackageSpecificPaths(getContext()); 61 final String packageName = getContext().getPackageName(); 62 63 for (File path : paths) { 64 if (path == null) continue; 65 66 assertTrue(path.getAbsolutePath().contains(packageName)); 67 68 // Walk up until we drop our package 69 while (path.getAbsolutePath().contains(packageName)) { 70 assertDirReadWriteAccess(path); 71 path = path.getParentFile(); 72 } 73 74 // Keep walking up until we leave device 75 while (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState(path))) { 76 assertDirNoAccess(path); 77 path = path.getParentFile(); 78 } 79 } 80 } 81 82 /** 83 * Verify that we don't have read access to any storage mountpoints. 84 */ 85 public void testMountPointsNotReadable() throws Exception { 86 final String userId = Integer.toString(android.os.Process.myUid() / 100000); 87 final List<File> mountPaths = getMountPaths(); 88 for (File path : mountPaths) { 89 if (path.getAbsolutePath().startsWith("/mnt/") 90 || path.getAbsolutePath().startsWith("/storage/")) { 91 // Mount points could be multi-user aware, so try probing both 92 // top level and user-specific directory. 93 final File userPath = new File(path, userId); 94 95 assertDirNoAccess(path); 96 assertDirNoAccess(userPath); 97 } 98 } 99 } 100 101 /** 102 * Verify that we can't download things outside package directory. 103 */ 104 public void testDownloadManager() throws Exception { 105 final DownloadManager dm = getContext().getSystemService(DownloadManager.class); 106 try { 107 final Uri source = Uri.parse("http://www.example.com"); 108 final File target = new File( 109 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "meow"); 110 dm.enqueue(new Request(source).setDestinationUri(Uri.fromFile(target))); 111 fail("Unexpected success writing outside package directory"); 112 } catch (SecurityException expected) { 113 } 114 } 115 116 /** 117 * Verify that we can download things into our package directory. 118 */ 119 public void testDownloadManagerPackage() throws Exception { 120 final DownloadManager dm = getContext().getSystemService(DownloadManager.class); 121 final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver(); 122 try { 123 IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE); 124 mContext.registerReceiver(receiver, intentFilter); 125 126 final Uri source = Uri.parse("http://www.example.com"); 127 final File target = new File( 128 getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "meow"); 129 130 final long id = dm.enqueue(new Request(source).setDestinationUri(Uri.fromFile(target))); 131 try { 132 receiver.waitForDownloadComplete(30 * DateUtils.SECOND_IN_MILLIS, id); 133 assertSuccessfulDownload(id, target); 134 } finally { 135 dm.remove(id); 136 } 137 } finally { 138 mContext.unregisterReceiver(receiver); 139 } 140 } 141 142 /** 143 * Shamelessly borrowed from DownloadManagerTest.java 144 */ 145 private static class DownloadCompleteReceiver extends BroadcastReceiver { 146 private HashSet<Long> mCompleteIds = new HashSet<>(); 147 148 public DownloadCompleteReceiver() { 149 } 150 151 @Override 152 public void onReceive(Context context, Intent intent) { 153 synchronized (mCompleteIds) { 154 mCompleteIds.add(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)); 155 mCompleteIds.notifyAll(); 156 } 157 } 158 159 private boolean isCompleteLocked(long... ids) { 160 for (long id : ids) { 161 if (!mCompleteIds.contains(id)) { 162 return false; 163 } 164 } 165 return true; 166 } 167 168 public void waitForDownloadComplete(long timeoutMillis, long... waitForIds) 169 throws InterruptedException { 170 if (waitForIds.length == 0) { 171 throw new IllegalArgumentException("Missing IDs to wait for"); 172 } 173 174 final long startTime = SystemClock.elapsedRealtime(); 175 do { 176 synchronized (mCompleteIds) { 177 mCompleteIds.wait(timeoutMillis); 178 if (isCompleteLocked(waitForIds)) return; 179 } 180 } while ((SystemClock.elapsedRealtime() - startTime) < timeoutMillis); 181 182 throw new InterruptedException("Timeout waiting for IDs " + Arrays.toString(waitForIds) 183 + "; received " + mCompleteIds.toString() 184 + ". Make sure you have WiFi or some other connectivity for this test."); 185 } 186 } 187 188 private void assertSuccessfulDownload(long id, File location) { 189 final DownloadManager dm = getContext().getSystemService(DownloadManager.class); 190 191 Cursor cursor = null; 192 try { 193 cursor = dm.query(new Query().setFilterById(id)); 194 assertTrue(cursor.moveToNext()); 195 assertEquals(DownloadManager.STATUS_SUCCESSFUL, cursor.getInt( 196 cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))); 197 assertEquals(Uri.fromFile(location).toString(), 198 cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))); 199 assertTrue(location.exists()); 200 } finally { 201 if (cursor != null) { 202 cursor.close(); 203 } 204 } 205 } 206 } 207