Home | History | Annotate | Download | only in media
      1 /* //device/content/providers/media/src/com/android/providers/media/MediaScannerService.java
      2 **
      3 ** Copyright 2007, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 package com.android.providers.media;
     19 
     20 import android.app.Service;
     21 import android.content.ContentValues;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.res.Configuration;
     25 import android.content.res.Resources;
     26 import android.database.Cursor;
     27 import android.media.IMediaScannerListener;
     28 import android.media.IMediaScannerService;
     29 import android.media.MediaScanner;
     30 import android.net.Uri;
     31 import android.os.Bundle;
     32 import android.os.Environment;
     33 import android.os.FileUtils;
     34 import android.os.Handler;
     35 import android.os.IBinder;
     36 import android.os.Looper;
     37 import android.os.Message;
     38 import android.os.PowerManager;
     39 import android.os.Process;
     40 import android.os.SystemProperties;
     41 import android.provider.MediaStore;
     42 import android.util.Config;
     43 import android.util.Log;
     44 
     45 import java.io.File;
     46 import java.util.Locale;
     47 
     48 public class MediaScannerService extends Service implements Runnable
     49 {
     50     private static final String TAG = "MediaScannerService";
     51 
     52     private volatile Looper mServiceLooper;
     53     private volatile ServiceHandler mServiceHandler;
     54     private PowerManager.WakeLock mWakeLock;
     55 
     56     private void openDatabase(String volumeName) {
     57         try {
     58             ContentValues values = new ContentValues();
     59             values.put("name", volumeName);
     60             getContentResolver().insert(Uri.parse("content://media/"), values);
     61         } catch (IllegalArgumentException ex) {
     62             Log.w(TAG, "failed to open media database");
     63         }
     64     }
     65 
     66     private void closeDatabase(String volumeName) {
     67         try {
     68             getContentResolver().delete(
     69                     Uri.parse("content://media/" + volumeName), null, null);
     70         } catch (Exception e) {
     71             Log.w(TAG, "failed to close media database " + volumeName + " exception: " + e);
     72         }
     73     }
     74 
     75     private MediaScanner createMediaScanner() {
     76         MediaScanner scanner = new MediaScanner(this);
     77         Locale locale = getResources().getConfiguration().locale;
     78         if (locale != null) {
     79             String language = locale.getLanguage();
     80             String country = locale.getCountry();
     81             String localeString = null;
     82             if (language != null) {
     83                 if (country != null) {
     84                     scanner.setLocale(language + "_" + country);
     85                 } else {
     86                     scanner.setLocale(language);
     87                 }
     88             }
     89         }
     90 
     91         return scanner;
     92     }
     93 
     94     private void scan(String[] directories, String volumeName) {
     95         // don't sleep while scanning
     96         mWakeLock.acquire();
     97 
     98         ContentValues values = new ContentValues();
     99         values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
    100         Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);
    101 
    102         Uri uri = Uri.parse("file://" + directories[0]);
    103         sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
    104 
    105         try {
    106             if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {
    107                  openDatabase(volumeName);
    108             }
    109 
    110             MediaScanner scanner = createMediaScanner();
    111             scanner.scanDirectories(directories, volumeName);
    112         } catch (Exception e) {
    113         	Log.e(TAG, "exception in MediaScanner.scan()", e);
    114         }
    115 
    116         getContentResolver().delete(scanUri, null, null);
    117 
    118         sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
    119         mWakeLock.release();
    120     }
    121 
    122     @Override
    123     public void onCreate()
    124     {
    125         PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    126         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
    127 
    128         // Start up the thread running the service.  Note that we create a
    129         // separate thread because the service normally runs in the process's
    130         // main thread, which we don't want to block.
    131         Thread thr = new Thread(null, this, "MediaScannerService");
    132         thr.start();
    133     }
    134 
    135     @Override
    136     public int onStartCommand(Intent intent, int flags, int startId)
    137     {
    138         while (mServiceHandler == null) {
    139             synchronized (this) {
    140                 try {
    141                     wait(100);
    142                 } catch (InterruptedException e) {
    143                 }
    144             }
    145         }
    146 
    147         if (intent == null) {
    148             Log.e(TAG, "Intent is null in onStartCommand: ",
    149                 new NullPointerException());
    150             return Service.START_NOT_STICKY;
    151         }
    152 
    153         Message msg = mServiceHandler.obtainMessage();
    154         msg.arg1 = startId;
    155         msg.obj = intent.getExtras();
    156         mServiceHandler.sendMessage(msg);
    157 
    158         // Try again later if we are killed before we can finish scanning.
    159         return Service.START_REDELIVER_INTENT;
    160     }
    161 
    162     @Override
    163     public void onDestroy()
    164     {
    165         // Make sure thread has started before telling it to quit.
    166         while (mServiceLooper == null) {
    167             synchronized (this) {
    168                 try {
    169                     wait(100);
    170                 } catch (InterruptedException e) {
    171                 }
    172             }
    173         }
    174         mServiceLooper.quit();
    175     }
    176 
    177     public void run()
    178     {
    179         // reduce priority below other background threads to avoid interfering
    180         // with other services at boot time.
    181         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND +
    182                 Process.THREAD_PRIORITY_LESS_FAVORABLE);
    183         Looper.prepare();
    184 
    185         mServiceLooper = Looper.myLooper();
    186         mServiceHandler = new ServiceHandler();
    187 
    188         Looper.loop();
    189     }
    190 
    191     private Uri scanFile(String path, String mimeType) {
    192         String volumeName = MediaProvider.INTERNAL_VOLUME;
    193         String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
    194 
    195         if (path.startsWith(externalStoragePath)) {
    196             volumeName = MediaProvider.EXTERNAL_VOLUME;
    197             openDatabase(volumeName);
    198         }
    199         MediaScanner scanner = createMediaScanner();
    200         return scanner.scanSingleFile(path, volumeName, mimeType);
    201     }
    202 
    203     @Override
    204     public IBinder onBind(Intent intent)
    205     {
    206         return mBinder;
    207     }
    208 
    209     private final IMediaScannerService.Stub mBinder =
    210             new IMediaScannerService.Stub() {
    211         public void requestScanFile(String path, String mimeType, IMediaScannerListener listener)
    212         {
    213             if (Config.LOGD) {
    214                 Log.d(TAG, "IMediaScannerService.scanFile: " + path + " mimeType: " + mimeType);
    215             }
    216             Bundle args = new Bundle();
    217             args.putString("filepath", path);
    218             args.putString("mimetype", mimeType);
    219             if (listener != null) {
    220                 args.putIBinder("listener", listener.asBinder());
    221             }
    222             startService(new Intent(MediaScannerService.this,
    223                     MediaScannerService.class).putExtras(args));
    224         }
    225 
    226         public void scanFile(String path, String mimeType) {
    227             requestScanFile(path, mimeType, null);
    228         }
    229     };
    230 
    231     private final class ServiceHandler extends Handler
    232     {
    233         @Override
    234         public void handleMessage(Message msg)
    235         {
    236             Bundle arguments = (Bundle) msg.obj;
    237             String filePath = arguments.getString("filepath");
    238 
    239             try {
    240                 if (filePath != null) {
    241                     IBinder binder = arguments.getIBinder("listener");
    242                     IMediaScannerListener listener =
    243                             (binder == null ? null : IMediaScannerListener.Stub.asInterface(binder));
    244                     Uri uri = scanFile(filePath, arguments.getString("mimetype"));
    245                     if (listener != null) {
    246                         listener.scanCompleted(filePath, uri);
    247                     }
    248                 } else {
    249                     String volume = arguments.getString("volume");
    250                     String[] directories = null;
    251 
    252                     if (MediaProvider.INTERNAL_VOLUME.equals(volume)) {
    253                         // scan internal media storage
    254                         directories = new String[] {
    255                                 Environment.getRootDirectory() + "/media",
    256                         };
    257                     }
    258                     else if (MediaProvider.EXTERNAL_VOLUME.equals(volume)) {
    259                         // scan external storage
    260                         directories = new String[] {
    261                                 Environment.getExternalStorageDirectory().getPath(),
    262                                 };
    263                     }
    264 
    265                     if (directories != null) {
    266                         if (Config.LOGD) Log.d(TAG, "start scanning volume " + volume);
    267                         scan(directories, volume);
    268                         if (Config.LOGD) Log.d(TAG, "done scanning volume " + volume);
    269                     }
    270                 }
    271             } catch (Exception e) {
    272                 Log.e(TAG, "Exception in handleMessage", e);
    273             }
    274 
    275             stopSelf(msg.arg1);
    276         }
    277     };
    278 }
    279 
    280