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