Home | History | Annotate | Download | only in cts
      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 android.media.cts;
     18 
     19 import android.media.cts.R;
     20 
     21 import android.content.ComponentName;
     22 import android.content.ContentResolver;
     23 import android.content.ContentUris;
     24 import android.content.ContentValues;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.res.AssetFileDescriptor;
     29 import android.database.Cursor;
     30 import android.media.MediaMetadataRetriever;
     31 import android.media.MediaScannerConnection;
     32 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
     33 import android.mtp.MtpConstants;
     34 import android.net.Uri;
     35 import android.support.test.filters.SmallTest;
     36 import android.platform.test.annotations.RequiresDevice;
     37 import android.os.Bundle;
     38 import android.os.Environment;
     39 import android.os.IBinder;
     40 import android.os.SystemClock;
     41 import android.provider.MediaStore;
     42 import android.test.AndroidTestCase;
     43 import android.util.Log;
     44 
     45 import com.android.compatibility.common.util.FileCopyHelper;
     46 import com.android.compatibility.common.util.PollingCheck;
     47 
     48 import java.io.File;
     49 import java.io.IOException;
     50 
     51 @SmallTest
     52 @RequiresDevice
     53 public class MediaScannerTest extends AndroidTestCase {
     54 
     55     private static final String MEDIA_TYPE = "audio/mpeg";
     56     private File mMediaFile;
     57     private static final int TIME_OUT = 10000;
     58     private MockMediaScannerConnection mMediaScannerConnection;
     59     private MockMediaScannerConnectionClient mMediaScannerConnectionClient;
     60     private String mFileDir;
     61 
     62     @Override
     63     protected void setUp() throws Exception {
     64         super.setUp();
     65         // prepare the media file.
     66 
     67         mFileDir = Environment.getExternalStorageDirectory() + "/" + getClass().getCanonicalName();
     68         cleanup();
     69         String fileName = mFileDir + "/test" + System.currentTimeMillis() + ".mp3";
     70         writeFile(R.raw.testmp3, fileName);
     71 
     72         mMediaFile = new File(fileName);
     73         assertTrue(mMediaFile.exists());
     74     }
     75 
     76     private void writeFile(int resid, String path) throws IOException {
     77         File out = new File(path);
     78         File dir = out.getParentFile();
     79         dir.mkdirs();
     80         FileCopyHelper copier = new FileCopyHelper(mContext);
     81         copier.copyToExternalStorage(resid, out);
     82     }
     83 
     84     @Override
     85     protected void tearDown() throws Exception {
     86         cleanup();
     87         super.tearDown();
     88     }
     89 
     90     private void cleanup() {
     91         if (mMediaFile != null) {
     92             mMediaFile.delete();
     93         }
     94         if (mFileDir != null) {
     95             String files[] = new File(mFileDir).list();
     96             if (files != null) {
     97                 for (String f: files) {
     98                     new File(mFileDir + "/" + f).delete();
     99                 }
    100             }
    101             new File(mFileDir).delete();
    102         }
    103 
    104         if (mMediaScannerConnection != null) {
    105             mMediaScannerConnection.disconnect();
    106             mMediaScannerConnection = null;
    107         }
    108 
    109         mContext.getContentResolver().delete(MediaStore.Audio.Media.getContentUri("external"),
    110                 "_data like ?", new String[] { mFileDir + "%"});
    111     }
    112 
    113     public void testMediaScanner() throws InterruptedException, IOException {
    114         mMediaScannerConnectionClient = new MockMediaScannerConnectionClient();
    115         mMediaScannerConnection = new MockMediaScannerConnection(getContext(),
    116                                     mMediaScannerConnectionClient);
    117 
    118         assertFalse(mMediaScannerConnection.isConnected());
    119 
    120         // start connection and wait until connected
    121         mMediaScannerConnection.connect();
    122         checkConnectionState(true);
    123 
    124         // start and wait for scan
    125         mMediaScannerConnection.scanFile(mMediaFile.getAbsolutePath(), MEDIA_TYPE);
    126         checkMediaScannerConnection();
    127 
    128         Uri insertUri = mMediaScannerConnectionClient.mediaUri;
    129         long id = Long.valueOf(insertUri.getLastPathSegment());
    130         ContentResolver res = mContext.getContentResolver();
    131 
    132         // check that the file ended up in the audio view
    133         Uri audiouri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
    134         Cursor c = res.query(audiouri, null, null, null, null);
    135         assertEquals(1, c.getCount());
    136         c.close();
    137 
    138         // add nomedia file and insert into database, file should no longer be in audio view
    139         File nomedia = new File(mMediaFile.getParent() + "/.nomedia");
    140         nomedia.createNewFile();
    141         ContentValues values = new ContentValues();
    142         values.put(MediaStore.MediaColumns.DATA, nomedia.getAbsolutePath());
    143         values.put(MediaStore.Files.FileColumns.FORMAT, MtpConstants.FORMAT_UNDEFINED);
    144         Uri nomediauri = res.insert(MediaStore.Files.getContentUri("external"), values);
    145         // clean up nomedia file
    146         nomedia.delete();
    147 
    148         // entry should not be in audio view anymore
    149         c = res.query(audiouri, null, null, null, null);
    150         assertEquals(0, c.getCount());
    151         c.close();
    152 
    153         // with nomedia file removed, do media scan and check that entry is in audio table again
    154         startMediaScanAndWait();
    155 
    156         // Give the 2nd stage scan that makes the unhidden files visible again
    157         // a little more time
    158         SystemClock.sleep(10000);
    159         // entry should be in audio view again
    160         c = res.query(audiouri, null, null, null, null);
    161         assertEquals(1, c.getCount());
    162         c.close();
    163 
    164         // ensure that we don't currently have playlists named ctsmediascanplaylist*
    165         res.delete(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
    166                 MediaStore.Audio.PlaylistsColumns.NAME + "=?",
    167                 new String[] { "ctsmediascanplaylist1"});
    168         res.delete(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
    169                 MediaStore.Audio.PlaylistsColumns.NAME + "=?",
    170                 new String[] { "ctsmediascanplaylist2"});
    171         // delete the playlist file entries, if they exist
    172         res.delete(MediaStore.Files.getContentUri("external"),
    173                 MediaStore.Files.FileColumns.DATA + "=?",
    174                 new String[] { mFileDir + "/ctsmediascanplaylist1.pls"});
    175         res.delete(MediaStore.Files.getContentUri("external"),
    176                 MediaStore.Files.FileColumns.DATA + "=?",
    177                 new String[] { mFileDir + "/ctsmediascanplaylist2.m3u"});
    178 
    179         // write some more files
    180         writeFile(R.raw.testmp3, mFileDir + "/testmp3.mp3");
    181         writeFile(R.raw.testmp3_2, mFileDir + "/testmp3_2.mp3");
    182         writeFile(R.raw.playlist1, mFileDir + "/ctsmediascanplaylist1.pls");
    183         writeFile(R.raw.playlist2, mFileDir + "/ctsmediascanplaylist2.m3u");
    184 
    185         startMediaScanAndWait();
    186 
    187         // verify that the two playlists were created correctly;
    188         c = res.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, null,
    189                 MediaStore.Audio.PlaylistsColumns.NAME + "=?",
    190                 new String[] { "ctsmediascanplaylist1"}, null);
    191         assertEquals(1, c.getCount());
    192         c.moveToFirst();
    193         long playlistid = c.getLong(c.getColumnIndex(MediaStore.MediaColumns._ID));
    194         c.close();
    195 
    196         c = res.query(MediaStore.Audio.Playlists.Members.getContentUri("external", playlistid),
    197                 null, null, null, MediaStore.Audio.Playlists.Members.PLAY_ORDER);
    198         assertEquals(2, c.getCount());
    199         c.moveToNext();
    200         long song1a = c.getLong(c.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID));
    201         c.moveToNext();
    202         long song1b = c.getLong(c.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID));
    203         c.close();
    204         assertTrue("song id should not be 0", song1a != 0);
    205         assertTrue("song id should not be 0", song1b != 0);
    206         assertTrue("song ids should not be same", song1a != song1b);
    207 
    208         // 2nd playlist should have the same songs, in reverse order
    209         c = res.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, null,
    210                 MediaStore.Audio.PlaylistsColumns.NAME + "=?",
    211                 new String[] { "ctsmediascanplaylist2"}, null);
    212         assertEquals(1, c.getCount());
    213         c.moveToFirst();
    214         playlistid = c.getLong(c.getColumnIndex(MediaStore.MediaColumns._ID));
    215         c.close();
    216 
    217         c = res.query(MediaStore.Audio.Playlists.Members.getContentUri("external", playlistid),
    218                 null, null, null, MediaStore.Audio.Playlists.Members.PLAY_ORDER);
    219         assertEquals(2, c.getCount());
    220         c.moveToNext();
    221         long song2a = c.getLong(c.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID));
    222         c.moveToNext();
    223         long song2b = c.getLong(c.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID));
    224         c.close();
    225         assertEquals("mismatched song ids", song1a, song2b);
    226         assertEquals("mismatched song ids", song2a, song1b);
    227 
    228         mMediaScannerConnection.disconnect();
    229 
    230         checkConnectionState(false);
    231     }
    232 
    233     public void testWildcardPaths() throws InterruptedException, IOException {
    234         mMediaScannerConnectionClient = new MockMediaScannerConnectionClient();
    235         mMediaScannerConnection = new MockMediaScannerConnection(getContext(),
    236                                     mMediaScannerConnectionClient);
    237 
    238         assertFalse(mMediaScannerConnection.isConnected());
    239 
    240         // start connection and wait until connected
    241         mMediaScannerConnection.connect();
    242         checkConnectionState(true);
    243 
    244         long now = System.currentTimeMillis();
    245         String dir1 = mFileDir + "/test-" + now;
    246         String file1 = dir1 + "/test.mp3";
    247         String dir2 = mFileDir + "/test_" + now;
    248         String file2 = dir2 + "/test.mp3";
    249         assertTrue(new File(dir1).mkdir());
    250         writeFile(R.raw.testmp3, file1);
    251         mMediaScannerConnection.scanFile(file1, MEDIA_TYPE);
    252         checkMediaScannerConnection();
    253         Uri file1Uri = mMediaScannerConnectionClient.mediaUri;
    254 
    255         assertTrue(new File(dir2).mkdir());
    256         writeFile(R.raw.testmp3, file2);
    257         mMediaScannerConnectionClient.reset();
    258         mMediaScannerConnection.scanFile(file2, MEDIA_TYPE);
    259         checkMediaScannerConnection();
    260         Uri file2Uri = mMediaScannerConnectionClient.mediaUri;
    261 
    262         // if the URIs are the same, then the media scanner likely treated the _ character
    263         // in the second path as a wildcard, and matched it with the first path
    264         assertFalse(file1Uri.equals(file2Uri));
    265 
    266         // rewrite Uris to use the file scheme
    267         long file1id = Long.valueOf(file1Uri.getLastPathSegment());
    268         long file2id = Long.valueOf(file2Uri.getLastPathSegment());
    269         file1Uri = MediaStore.Files.getContentUri("external", file1id);
    270         file2Uri = MediaStore.Files.getContentUri("external", file2id);
    271 
    272         ContentResolver res = mContext.getContentResolver();
    273         Cursor c = res.query(file1Uri, new String[] { "parent" }, null, null, null);
    274         c.moveToFirst();
    275         long parent1id = c.getLong(0);
    276         c.close();
    277         c = res.query(file2Uri, new String[] { "parent" }, null, null, null);
    278         c.moveToFirst();
    279         long parent2id = c.getLong(0);
    280         c.close();
    281         // if the parent ids are the same, then the media provider likely
    282         // treated the _ character in the second path as a wildcard
    283         assertTrue("same parent", parent1id != parent2id);
    284 
    285         // check the parent paths are correct
    286         c = res.query(MediaStore.Files.getContentUri("external", parent1id),
    287                 new String[] { "_data" }, null, null, null);
    288         c.moveToFirst();
    289         assertEquals(dir1, c.getString(0));
    290         c.close();
    291 
    292         c = res.query(MediaStore.Files.getContentUri("external", parent2id),
    293                 new String[] { "_data" }, null, null, null);
    294         c.moveToFirst();
    295         assertEquals(dir2, c.getString(0));
    296         c.close();
    297 
    298         // clean up
    299         new File(file1).delete();
    300         new File(dir1).delete();
    301         new File(file2).delete();
    302         new File(dir2).delete();
    303         res.delete(file1Uri, null, null);
    304         res.delete(file2Uri, null, null);
    305         res.delete(MediaStore.Files.getContentUri("external", parent1id), null, null);
    306         res.delete(MediaStore.Files.getContentUri("external", parent2id), null, null);
    307 
    308         mMediaScannerConnection.disconnect();
    309 
    310         checkConnectionState(false);
    311     }
    312 
    313     public void testCanonicalize() throws Exception {
    314         mMediaScannerConnectionClient = new MockMediaScannerConnectionClient();
    315         mMediaScannerConnection = new MockMediaScannerConnection(getContext(),
    316                                     mMediaScannerConnectionClient);
    317 
    318         assertFalse(mMediaScannerConnection.isConnected());
    319 
    320         // start connection and wait until connected
    321         mMediaScannerConnection.connect();
    322         checkConnectionState(true);
    323 
    324         // write file and scan to insert into database
    325         String fileDir = Environment.getExternalStorageDirectory() + "/"
    326                 + getClass().getCanonicalName() + "/canonicaltest-" + System.currentTimeMillis();
    327         String fileName = fileDir + "/test.mp3";
    328         writeFile(R.raw.testmp3, fileName);
    329         mMediaScannerConnection.scanFile(fileName, MEDIA_TYPE);
    330         checkMediaScannerConnection();
    331 
    332         // check path and uri
    333         Uri uri = mMediaScannerConnectionClient.mediaUri;
    334         String path = mMediaScannerConnectionClient.mediaPath;
    335         assertEquals(fileName, path);
    336         assertNotNull(uri);
    337 
    338         // check canonicalization
    339         ContentResolver res = mContext.getContentResolver();
    340         Uri canonicalUri = res.canonicalize(uri);
    341         assertNotNull(canonicalUri);
    342         assertFalse(uri.equals(canonicalUri));
    343         Uri uncanonicalizedUri = res.uncanonicalize(canonicalUri);
    344         assertEquals(uri, uncanonicalizedUri);
    345 
    346         // remove the entry from the database
    347         assertEquals(1, res.delete(uri, null, null));
    348         assertTrue(new File(path).delete());
    349 
    350         // write same file again and scan to insert into database
    351         mMediaScannerConnectionClient.reset();
    352         String fileName2 = fileDir + "/test2.mp3";
    353         writeFile(R.raw.testmp3, fileName2);
    354         mMediaScannerConnection.scanFile(fileName2, MEDIA_TYPE);
    355         checkMediaScannerConnection();
    356 
    357         // check path and uri
    358         Uri uri2 = mMediaScannerConnectionClient.mediaUri;
    359         String path2 = mMediaScannerConnectionClient.mediaPath;
    360         assertEquals(fileName2, path2);
    361         assertNotNull(uri2);
    362 
    363         // this should be a different entry in the database and not re-use the same database id
    364         assertFalse(uri.equals(uri2));
    365 
    366         Uri canonicalUri2 = res.canonicalize(uri2);
    367         assertNotNull(canonicalUri2);
    368         assertFalse(uri2.equals(canonicalUri2));
    369         Uri uncanonicalizedUri2 = res.uncanonicalize(canonicalUri2);
    370         assertEquals(uri2, uncanonicalizedUri2);
    371 
    372         // uncanonicalize the original canonicalized uri, it should resolve to the new uri
    373         Uri uncanonicalizedUri3 = res.uncanonicalize(canonicalUri);
    374         assertEquals(uri2, uncanonicalizedUri3);
    375 
    376         assertEquals(1, res.delete(uri2, null, null));
    377         assertTrue(new File(path2).delete());
    378     }
    379 
    380     static class MediaScanEntry {
    381         MediaScanEntry(int r, String[] t) {
    382             this.res = r;
    383             this.tags = t;
    384         }
    385         int res;
    386         String[] tags;
    387     }
    388 
    389     MediaScanEntry encodingtestfiles[] = {
    390             new MediaScanEntry(R.raw.gb18030_1,
    391                     new String[] {"", "200911", "", "(TV Version)", null} ),
    392             new MediaScanEntry(R.raw.gb18030_2,
    393                     new String[] {"", "", null, "", null} ),
    394             new MediaScanEntry(R.raw.gb18030_3,
    395                     new String[] {"", "()()", null, "11.Open Arms.( cn808.net )", null} ),
    396             new MediaScanEntry(R.raw.gb18030_4,
    397                     new String[] {"", "", "", "25", ""} ),
    398             new MediaScanEntry(R.raw.gb18030_6,
    399                     new String[] {"", "", "", "", ""} ),
    400             new MediaScanEntry(R.raw.gb18030_7, // this is actually utf-8
    401                     new String[] {"", "", null, "", null} ),
    402             new MediaScanEntry(R.raw.gb18030_8,
    403                     new String[] {"", "Jay", null, "", null} ),
    404             new MediaScanEntry(R.raw.big5_1,
    405                     new String[] {"", "So I Sing 08 Live", "", "", null} ),
    406             new MediaScanEntry(R.raw.big5_2,
    407                     new String[] {"", "So I Sing 08 Live", "", " - ", null} ),
    408             new MediaScanEntry(R.raw.cp1251_v1,
    409                     new String[] {" ", " ", null, ", , ", null} ),
    410             new MediaScanEntry(R.raw.cp1251_v1v2,
    411                     new String[] {"", "", null, "", null} ),
    412             new MediaScanEntry(R.raw.cp1251_3,
    413                     new String[] {" (tATu)", "200   [Limited edi", null, "   ", null} ),
    414             // The following 3 use cp1251 encoding, expanded to 16 bits and stored as utf16
    415             new MediaScanEntry(R.raw.cp1251_4,
    416                     new String[] {" ", " ", null, "   (   )", "."} ),
    417             new MediaScanEntry(R.raw.cp1251_5,
    418                     new String[] {" ", " ", null, "", "."} ),
    419             new MediaScanEntry(R.raw.cp1251_6,
    420                     new String[] {" ", " ", null, ", ...", "."} ),
    421             new MediaScanEntry(R.raw.cp1251_7,
    422                     new String[] {" ", " ", null, " ", null} ),
    423             new MediaScanEntry(R.raw.cp1251_8,
    424                     new String[] {" ", " ", null, "i ", null} ),
    425             new MediaScanEntry(R.raw.shiftjis1,
    426                     new String[] {"", "", null, "", null} ),
    427             new MediaScanEntry(R.raw.shiftjis2,
    428                     new String[] {"", "SoundEffects", null, "", null} ),
    429             new MediaScanEntry(R.raw.shiftjis3,
    430                     new String[] {"", "SoundEffects", null, "", null} ),
    431             new MediaScanEntry(R.raw.shiftjis4,
    432                     new String[] {"", "SoundEffects", null, "", null} ),
    433             new MediaScanEntry(R.raw.shiftjis5,
    434                     new String[] {"", "SoundEffects", null, "", null} ),
    435             new MediaScanEntry(R.raw.shiftjis6,
    436                     new String[] {"", "SoundEffects", null, "", null} ),
    437             new MediaScanEntry(R.raw.shiftjis7,
    438                     new String[] {"", "SoundEffects", null, "", null} ),
    439             new MediaScanEntry(R.raw.shiftjis8,
    440                     new String[] {"", "SoundEffects", null, "", null} ),
    441             new MediaScanEntry(R.raw.iso88591_1,
    442                     new String[] {"Mozart", "Best of Mozart", null, "Overtre (Die Hochzeit des Figaro)", null} ),
    443             new MediaScanEntry(R.raw.iso88591_2, // actually UTF16, but only uses iso8859-1 chars
    444                     new String[] {"Bjrk", "Telegram", "Bjrk", "Possibly Maybe (Lucy Mix)", null} ),
    445             new MediaScanEntry(R.raw.hebrew,
    446                     new String[] {" ", "", null, " ", null } ),
    447             new MediaScanEntry(R.raw.hebrew2,
    448                     new String[] {"   ", "Untitled - 11-11-02 (9)", null, "", null } ),
    449             new MediaScanEntry(R.raw.iso88591_3,
    450                     new String[] {"Mobil", "Kartographie", null, "Zu Wenig", null }),
    451             new MediaScanEntry(R.raw.iso88591_4,
    452                     new String[] {"Mobil", "Kartographie", null, "Rotebeetesalat (Igel Stehlen)", null }),
    453             new MediaScanEntry(R.raw.iso88591_5,
    454                     new String[] {"The Creatures", "Hai! [UK Bonus DVD] Disc 1", "The Creatures", "Imagor", null }),
    455             new MediaScanEntry(R.raw.iso88591_6,
    456                     new String[] {"Forward, Russia!", "Give Me a Wall", "Forward Russia", "Fifteen, Pt. 1", "Canning/Nicholls/Sarah Nicolls/Woodhead"}),
    457             new MediaScanEntry(R.raw.iso88591_7,
    458                     new String[] {"Bjrk", "Homogenic", "Bjrk", "Jga", "Bjrk/Sjn"}),
    459             // this one has a genre of "Ind" which confused the detector
    460             new MediaScanEntry(R.raw.iso88591_8,
    461                     new String[] {"The Black Heart Procession", "3", null, "A Heart Like Mine", null}),
    462             new MediaScanEntry(R.raw.iso88591_9,
    463                     new String[] {"DJ Tisto", "Just Be", "DJ Tisto", "Adagio For Strings", "Samuel Barber"}),
    464             new MediaScanEntry(R.raw.iso88591_10,
    465                     new String[] {"Ratatat", "LP3", null, "Brule", null}),
    466             new MediaScanEntry(R.raw.iso88591_11,
    467                     new String[] {"Semp", "Le Petit Nicolas vol. 1", null, "Les Cow-Boys", null}),
    468             new MediaScanEntry(R.raw.iso88591_12,
    469                     new String[] {"UUVVWWZ", "UUVVWWZ", null, "Neolao", null}),
    470             new MediaScanEntry(R.raw.iso88591_13,
    471                     new String[] {"Michael Bubl", "Crazy Love", "Michael Bubl", "Haven't Met You Yet", null}),
    472             new MediaScanEntry(R.raw.utf16_1,
    473                     new String[] {"Shakira", "Latin Mix USA", "Shakira", "Estoy Aqu", null})
    474     };
    475 
    476     public void testEncodingDetection() throws Exception {
    477         for (int i = 0; i< encodingtestfiles.length; i++) {
    478             MediaScanEntry entry = encodingtestfiles[i];
    479             String name = mContext.getResources().getResourceEntryName(entry.res);
    480             String path =  mFileDir + "/" + name + ".mp3";
    481             writeFile(entry.res, path);
    482         }
    483 
    484         startMediaScanAndWait();
    485 
    486         String columns[] = {
    487                 MediaStore.Audio.Media.ARTIST,
    488                 MediaStore.Audio.Media.ALBUM,
    489                 MediaStore.Audio.Media.ALBUM_ARTIST,
    490                 MediaStore.Audio.Media.TITLE,
    491                 MediaStore.Audio.Media.COMPOSER
    492         };
    493         ContentResolver res = mContext.getContentResolver();
    494         for (int i = 0; i< encodingtestfiles.length; i++) {
    495             MediaScanEntry entry = encodingtestfiles[i];
    496             String name = mContext.getResources().getResourceEntryName(entry.res);
    497             String path =  mFileDir + "/" + name + ".mp3";
    498             Cursor c = res.query(MediaStore.Audio.Media.getContentUri("external"), columns,
    499                     MediaStore.Audio.Media.DATA + "=?", new String[] {path}, null);
    500             assertNotNull("null cursor", c);
    501             assertEquals("wrong number or results", 1, c.getCount());
    502             assertTrue("failed to move cursor", c.moveToFirst());
    503 
    504             for (int j =0; j < 5; j++) {
    505                 String expected = entry.tags[j];
    506                 if ("".equals(expected)) {
    507                     // empty entry in the table means an unset id3 tag that is filled in by
    508                     // the media scanner, e.g. by using "<unknown>". Since this may be localized,
    509                     // don't check it for any particular value.
    510                     assertNotNull("unexpected null entry " + i + " field " + j + "(" + path + ")",
    511                             c.getString(j));
    512                 } else {
    513                     assertEquals("mismatch on entry " + i + " field " + j + "(" + path + ")",
    514                             expected, c.getString(j));
    515                 }
    516             }
    517             // clean up
    518             new File(path).delete();
    519             res.delete(MediaStore.Audio.Media.getContentUri("external"),
    520                     MediaStore.Audio.Media.DATA + "=?", new String[] {path});
    521 
    522             c.close();
    523 
    524             // also test with the MediaMetadataRetriever API
    525             MediaMetadataRetriever woodly = new MediaMetadataRetriever();
    526             AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(entry.res);
    527             woodly.setDataSource(afd.getFileDescriptor(),
    528                     afd.getStartOffset(), afd.getDeclaredLength());
    529 
    530             String[] actual = new String[5];
    531             actual[0] = woodly.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
    532             actual[1] = woodly.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
    533             actual[2] = woodly.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
    534             actual[3] = woodly.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
    535             actual[4] = woodly.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER);
    536 
    537             for (int j = 0; j < 5; j++) {
    538                 if ("".equals(entry.tags[j])) {
    539                     // retriever doesn't insert "unknown artist" and such, it just returns null
    540                     assertNull("retriever: unexpected non-null for entry " + i + " field " + j,
    541                             actual[j]);
    542                 } else {
    543                     Log.i("@@@", "tags: @@" + entry.tags[j] + "@@" + actual[j] + "@@");
    544                     assertEquals("retriever: mismatch on entry " + i + " field " + j,
    545                             entry.tags[j], actual[j]);
    546                 }
    547             }
    548         }
    549     }
    550 
    551     private void startMediaScanAndWait() throws InterruptedException {
    552         ScannerNotificationReceiver finishedReceiver = new ScannerNotificationReceiver(
    553                 Intent.ACTION_MEDIA_SCANNER_FINISHED);
    554         IntentFilter finishedIntentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_FINISHED);
    555         finishedIntentFilter.addDataScheme("file");
    556         mContext.registerReceiver(finishedReceiver, finishedIntentFilter);
    557 
    558         Bundle args = new Bundle();
    559         args.putString("volume", "external");
    560         Intent i = new Intent("android.media.IMediaScannerService").putExtras(args);
    561         i.setClassName("com.android.providers.media",
    562                 "com.android.providers.media.MediaScannerService");
    563         mContext.startService(i);
    564 
    565         finishedReceiver.waitForBroadcast();
    566         mContext.unregisterReceiver(finishedReceiver);
    567     }
    568 
    569     private void checkMediaScannerConnection() {
    570         new PollingCheck(TIME_OUT) {
    571             protected boolean check() {
    572                 return mMediaScannerConnectionClient.isOnMediaScannerConnectedCalled;
    573             }
    574         }.run();
    575         new PollingCheck(TIME_OUT) {
    576             protected boolean check() {
    577                 return mMediaScannerConnectionClient.mediaPath != null;
    578             }
    579         }.run();
    580     }
    581 
    582     private void checkConnectionState(final boolean expected) {
    583         new PollingCheck(TIME_OUT) {
    584             protected boolean check() {
    585                 return mMediaScannerConnection.isConnected() == expected;
    586             }
    587         }.run();
    588     }
    589 
    590     class MockMediaScannerConnection extends MediaScannerConnection {
    591 
    592         public boolean mIsOnServiceConnectedCalled;
    593         public boolean mIsOnServiceDisconnectedCalled;
    594         public MockMediaScannerConnection(Context context, MediaScannerConnectionClient client) {
    595             super(context, client);
    596         }
    597 
    598         @Override
    599         public void onServiceConnected(ComponentName className, IBinder service) {
    600             super.onServiceConnected(className, service);
    601             mIsOnServiceConnectedCalled = true;
    602         }
    603 
    604         @Override
    605         public void onServiceDisconnected(ComponentName className) {
    606             super.onServiceDisconnected(className);
    607             mIsOnServiceDisconnectedCalled = true;
    608             // this is not called.
    609         }
    610     }
    611 
    612     class MockMediaScannerConnectionClient implements MediaScannerConnectionClient {
    613 
    614         public boolean isOnMediaScannerConnectedCalled;
    615         public String mediaPath;
    616         public Uri mediaUri;
    617         public void onMediaScannerConnected() {
    618             isOnMediaScannerConnectedCalled = true;
    619         }
    620 
    621         public void onScanCompleted(String path, Uri uri) {
    622             mediaPath = path;
    623             if (uri != null) {
    624                 mediaUri = uri;
    625             }
    626         }
    627 
    628         public void reset() {
    629             mediaPath = null;
    630             mediaUri = null;
    631         }
    632     }
    633 
    634 }
    635