1 /*
2  * Copyright (C) 2019 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 package android.app.cts;
17 
18 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
19 
20 import static com.google.common.truth.Truth.assertThat;
21 
22 import static org.junit.Assert.assertArrayEquals;
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertTrue;
25 
26 import android.app.DownloadManager;
27 import android.content.ContentResolver;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.database.Cursor;
31 import android.net.Uri;
32 import android.os.Environment;
33 import android.os.FileUtils;
34 
35 import androidx.test.runner.AndroidJUnit4;
36 
37 import org.junit.Assert;
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 
41 import java.io.File;
42 import java.io.FileInputStream;
43 import java.io.FileOutputStream;
44 import java.io.InputStream;
45 import java.io.OutputStream;
46 
47 @RunWith(AndroidJUnit4.class)
48 public class DownloadManagerApi28Test extends DownloadManagerTestBase {
49 
50     @Test
testSetDestinationUri_publicDir()51     public void testSetDestinationUri_publicDir() throws Exception {
52         File publicLocation = new File(
53                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
54                 "publicFile.bin");
55         deleteFromShell(publicLocation);
56 
57         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
58         try {
59             IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
60             mContext.registerReceiver(receiver, intentFilter);
61 
62             DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
63             requestPublic.setDestinationUri(Uri.fromFile(publicLocation));
64             long id = mDownloadManager.enqueue(requestPublic);
65 
66             int allDownloads = getTotalNumberDownloads();
67             assertEquals(1, allDownloads);
68 
69             receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
70             assertSuccessfulDownload(id, publicLocation);
71 
72             assertRemoveDownload(id, 0);
73         } finally {
74             mContext.unregisterReceiver(receiver);
75         }
76     }
77 
78     @Test
testSetDestinationUri_sdcardPath()79     public void testSetDestinationUri_sdcardPath() throws Exception {
80         final File path = new File("/sdcard/publicFile.bin");
81         deleteFromShell(path);
82 
83         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
84         try {
85             IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
86             mContext.registerReceiver(receiver, intentFilter);
87 
88             DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
89             requestPublic.setDestinationUri(Uri.fromFile(path));
90             long id = mDownloadManager.enqueue(requestPublic);
91 
92             int allDownloads = getTotalNumberDownloads();
93             assertEquals(1, allDownloads);
94 
95             receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
96             assertSuccessfulDownload(id, path);
97 
98             assertRemoveDownload(id, 0);
99         } finally {
100             mContext.unregisterReceiver(receiver);
101         }
102     }
103 
104     @Test
testSetDestinationUri_privateAppDir()105     public void testSetDestinationUri_privateAppDir() throws Exception {
106         // Make sure the private app directory exists
107         runShellCommand("mkdir -p /sdcard/Android/data/com.android.shell -m 2770");
108         final File path = new File("/sdcard/Android/data/com.android.shell/"
109                 + TAG + System.currentTimeMillis());
110 
111         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
112         try {
113             IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
114             mContext.registerReceiver(receiver, intentFilter);
115 
116             DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
117             requestPublic.setDestinationUri(Uri.fromFile(path));
118             mDownloadManager.enqueue(requestPublic);
119             Assert.fail("Cannot download files into other app's private directories");
120         } catch (SecurityException expected) {
121         } finally {
122             mContext.unregisterReceiver(receiver);
123         }
124     }
125 
126     @Test
testDestinationInExternalPublicDir()127     public void testDestinationInExternalPublicDir() throws Exception {
128         File publicLocation = new File(
129                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
130                 "publicFile.bin");
131         deleteFromShell(publicLocation);
132 
133         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
134         try {
135             IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
136             mContext.registerReceiver(receiver, intentFilter);
137 
138             DownloadManager.Request requestPublic = new DownloadManager.Request(getGoodUrl());
139             requestPublic.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOCUMENTS,
140                     "publicFile.bin");
141             long id = mDownloadManager.enqueue(requestPublic);
142 
143             int allDownloads = getTotalNumberDownloads();
144             assertEquals(1, allDownloads);
145 
146             receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
147             assertSuccessfulDownload(id, publicLocation);
148 
149             assertRemoveDownload(id, 0);
150         } finally {
151             mContext.unregisterReceiver(receiver);
152         }
153     }
154 
155     @Test
testAddCompletedDownload_publicDirs()156     public void testAddCompletedDownload_publicDirs() throws Exception {
157         final String[] filePaths = new String[] {
158                 createFile(Environment.getExternalStoragePublicDirectory(
159                         Environment.DIRECTORY_DOWNLOADS), "file1.txt").getPath(),
160                 createFile(Environment.getExternalStoragePublicDirectory(
161                         Environment.DIRECTORY_DOCUMENTS), "file2.txt").getPath(),
162                 "/sdcard/Download/file3.txt",
163         };
164 
165         for (String path : filePaths) {
166             final String fileContents = "Test content:" + path + "_" + System.nanoTime();
167 
168             final File file = new File(path);
169             writeToFile(new File(path), fileContents);
170 
171             final long id = mDownloadManager.addCompletedDownload(file.getName(), "Test desc", true,
172                     "text/plain", path, fileContents.getBytes().length, true);
173             final String actualContents = readFromFile(mDownloadManager.openDownloadedFile(id));
174             assertEquals(fileContents, actualContents);
175 
176             final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(id);
177             mContext.grantUriPermission("com.android.shell", downloadUri,
178                     Intent.FLAG_GRANT_READ_URI_PERMISSION);
179             final String rawFilePath = getRawFilePath(downloadUri);
180             final String rawFileContents = readFromRawFile(rawFilePath);
181             assertEquals(fileContents, rawFileContents);
182             assertRemoveDownload(id, 0);
183         }
184     }
185 
186 
187     @Test
testDownloadManager_mediaStoreEntry()188     public void testDownloadManager_mediaStoreEntry() throws Exception {
189         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
190         try {
191             mContext.registerReceiver(receiver,
192                     new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
193 
194             final String fileName = "noiseandchirps.mp3";
195             final DownloadManager.Request request
196                     = new DownloadManager.Request(getAssetUrl(fileName));
197             final Uri[] downloadPathUris = new Uri[] {
198                     Uri.fromFile(new File(Environment.getExternalStoragePublicDirectory(
199                             Environment.DIRECTORY_DOWNLOADS), "testfile1.mp3")),
200                     Uri.parse("file:///sdcard/testfile2.mp3"),
201             };
202             for (Uri downloadLocation : downloadPathUris) {
203                 request.setDestinationUri(downloadLocation);
204                 final long downloadId = mDownloadManager.enqueue(request);
205                 receiver.waitForDownloadComplete(SHORT_TIMEOUT, downloadId);
206                 assertSuccessfulDownload(downloadId, new File(downloadLocation.getPath()));
207                 final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
208                 final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
209                 final ContentResolver contentResolver = mContext.getContentResolver();
210                 assertArrayEquals(hash(contentResolver.openInputStream(downloadUri)),
211                         hash(contentResolver.openInputStream(mediaStoreUri)));
212 
213                 // Delete entry in DownloadProvider and verify it's deleted from MediaProvider
214                 // as well.
215                 assertRemoveDownload(downloadId, 0);
216                 try (Cursor cursor = mContext.getContentResolver().query(
217                         mediaStoreUri, null, null, null)) {
218                     assertEquals(0, cursor.getCount());
219                 }
220             }
221         } finally {
222             mContext.unregisterReceiver(receiver);
223         }
224     }
225 
226     /**
227      * Add a file using DownloadManager.addCompleted and verify that the file has been added
228      * to MediaStore as well.
229      */
230     @Test
testAddCompletedDownload_mediaStoreEntry()231     public void testAddCompletedDownload_mediaStoreEntry() throws Exception {
232         final String assetName = "noiseandchirps.mp3";
233         final String[] downloadPath = new String[] {
234                 new File(Environment.getExternalStoragePublicDirectory(
235                         Environment.DIRECTORY_DOWNLOADS), "file1.mp3").getPath(),
236                 new File(Environment.getExternalStoragePublicDirectory(
237                         Environment.DIRECTORY_MUSIC), "file2.mp3").getPath(),
238                 "/sdcard/file3.mp3",
239         };
240         for (String downloadLocation : downloadPath) {
241             final File file = new File(Uri.parse(downloadLocation).getPath());
242             final File parentDir = file.getParentFile();
243             if (!parentDir.exists()) {
244                 parentDir.mkdirs();
245                 assertThat(parentDir.exists()).isTrue();
246             }
247             try (InputStream in = mContext.getAssets().open(assetName);
248                  OutputStream out = new FileOutputStream(file)) {
249                 FileUtils.copy(in, out);
250             }
251 
252             final long downloadId = mDownloadManager.addCompletedDownload(file.getName(),
253                     "Test desc", true,
254                     "audio/mp3", downloadLocation, file.length(), true);
255             assertTrue(downloadId >= 0);
256             final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
257             final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
258             assertArrayEquals(hash(new FileInputStream(file)),
259                     hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
260 
261             // Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
262             assertRemoveDownload(downloadId, 0);
263             try (Cursor cursor = mContext.getContentResolver().query(
264                     mediaStoreUri, null, null, null)) {
265                 assertEquals(0, cursor.getCount());
266             }
267         }
268     }
269 }
270