• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.media.IMediaScannerListener;
25 import android.media.IMediaScannerService;
26 import android.media.MediaScanner;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.os.Environment;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.PowerManager;
35 import android.os.Process;
36 import android.os.UserManager;
37 import android.os.storage.StorageManager;
38 import android.provider.MediaStore;
39 import android.util.Log;
40 
41 import com.android.internal.util.ArrayUtils;
42 
43 import java.io.File;
44 import java.util.Arrays;
45 
46 public class MediaScannerService extends Service implements Runnable {
47     private static final String TAG = "MediaScannerService";
48 
49     private volatile Looper mServiceLooper;
50     private volatile ServiceHandler mServiceHandler;
51     private PowerManager.WakeLock mWakeLock;
52     private String[] mExternalStoragePaths;
53 
openDatabase(String volumeName)54     private void openDatabase(String volumeName) {
55         try {
56             ContentValues values = new ContentValues();
57             values.put("name", volumeName);
58             getContentResolver().insert(Uri.parse("content://media/"), values);
59         } catch (IllegalArgumentException ex) {
60             Log.w(TAG, "failed to open media database");
61         }
62     }
63 
scan(String[] directories, String volumeName)64     private void scan(String[] directories, String volumeName) {
65         Uri uri = Uri.parse("file://" + directories[0]);
66         // don't sleep while scanning
67         mWakeLock.acquire();
68 
69         try {
70             ContentValues values = new ContentValues();
71             values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
72             Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);
73 
74             sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
75 
76             try {
77                 if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {
78                     openDatabase(volumeName);
79                 }
80 
81                 try (MediaScanner scanner = new MediaScanner(this, volumeName)) {
82                     scanner.scanDirectories(directories);
83                 }
84             } catch (Exception e) {
85                 Log.e(TAG, "exception in MediaScanner.scan()", e);
86             }
87 
88             getContentResolver().delete(scanUri, null, null);
89 
90         } finally {
91             sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
92             mWakeLock.release();
93         }
94     }
95 
96     @Override
onCreate()97     public void onCreate() {
98         PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
99         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
100         StorageManager storageManager = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
101         mExternalStoragePaths = storageManager.getVolumePaths();
102 
103         // Start up the thread running the service.  Note that we create a
104         // separate thread because the service normally runs in the process's
105         // main thread, which we don't want to block.
106         Thread thr = new Thread(null, this, "MediaScannerService");
107         thr.start();
108     }
109 
110     @Override
onStartCommand(Intent intent, int flags, int startId)111     public int onStartCommand(Intent intent, int flags, int startId) {
112         while (mServiceHandler == null) {
113             synchronized (this) {
114                 try {
115                     wait(100);
116                 } catch (InterruptedException e) {
117                 }
118             }
119         }
120 
121         if (intent == null) {
122             Log.e(TAG, "Intent is null in onStartCommand: ",
123                 new NullPointerException());
124             return Service.START_NOT_STICKY;
125         }
126 
127         Message msg = mServiceHandler.obtainMessage();
128         msg.arg1 = startId;
129         msg.obj = intent.getExtras();
130         mServiceHandler.sendMessage(msg);
131 
132         // Try again later if we are killed before we can finish scanning.
133         return Service.START_REDELIVER_INTENT;
134     }
135 
136     @Override
onDestroy()137     public void onDestroy() {
138         // Make sure thread has started before telling it to quit.
139         while (mServiceLooper == null) {
140             synchronized (this) {
141                 try {
142                     wait(100);
143                 } catch (InterruptedException e) {
144                 }
145             }
146         }
147         mServiceLooper.quit();
148     }
149 
150     @Override
run()151     public void run() {
152         // reduce priority below other background threads to avoid interfering
153         // with other services at boot time.
154         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND +
155                 Process.THREAD_PRIORITY_LESS_FAVORABLE);
156         Looper.prepare();
157 
158         mServiceLooper = Looper.myLooper();
159         mServiceHandler = new ServiceHandler();
160 
161         Looper.loop();
162     }
163 
scanFile(String path, String mimeType)164     private Uri scanFile(String path, String mimeType) {
165         String volumeName = MediaProvider.EXTERNAL_VOLUME;
166 
167         try (MediaScanner scanner = new MediaScanner(this, volumeName)) {
168             // make sure the file path is in canonical form
169             String canonicalPath = new File(path).getCanonicalPath();
170             return scanner.scanSingleFile(canonicalPath, mimeType);
171         } catch (Exception e) {
172             Log.e(TAG, "bad path " + path + " in scanFile()", e);
173             return null;
174         }
175     }
176 
177     @Override
onBind(Intent intent)178     public IBinder onBind(Intent intent) {
179         return mBinder;
180     }
181 
182     private final IMediaScannerService.Stub mBinder =
183             new IMediaScannerService.Stub() {
184         public void requestScanFile(String path, String mimeType, IMediaScannerListener listener) {
185             if (false) {
186                 Log.d(TAG, "IMediaScannerService.scanFile: " + path + " mimeType: " + mimeType);
187             }
188             Bundle args = new Bundle();
189             args.putString("filepath", path);
190             args.putString("mimetype", mimeType);
191             if (listener != null) {
192                 args.putIBinder("listener", listener.asBinder());
193             }
194             startService(new Intent(MediaScannerService.this,
195                     MediaScannerService.class).putExtras(args));
196         }
197 
198         public void scanFile(String path, String mimeType) {
199             requestScanFile(path, mimeType, null);
200         }
201     };
202 
203     private final class ServiceHandler extends Handler {
204         @Override
handleMessage(Message msg)205         public void handleMessage(Message msg) {
206             Bundle arguments = (Bundle) msg.obj;
207             if (arguments == null) {
208                 Log.e(TAG, "null intent, b/20953950");
209                 return;
210             }
211             String filePath = arguments.getString("filepath");
212 
213             try {
214                 if (filePath != null) {
215                     IBinder binder = arguments.getIBinder("listener");
216                     IMediaScannerListener listener =
217                             (binder == null ? null : IMediaScannerListener.Stub.asInterface(binder));
218                     Uri uri = null;
219                     try {
220                         uri = scanFile(filePath, arguments.getString("mimetype"));
221                     } catch (Exception e) {
222                         Log.e(TAG, "Exception scanning file", e);
223                     }
224                     if (listener != null) {
225                         listener.scanCompleted(filePath, uri);
226                     }
227                 } else {
228                     String volume = arguments.getString("volume");
229                     String[] directories = null;
230 
231                     if (MediaProvider.INTERNAL_VOLUME.equals(volume)) {
232                         // scan internal media storage
233                         directories = new String[] {
234                                 Environment.getRootDirectory() + "/media",
235                                 Environment.getOemDirectory() + "/media",
236                         };
237                     }
238                     else if (MediaProvider.EXTERNAL_VOLUME.equals(volume)) {
239                         // scan external storage volumes
240                         if (getSystemService(UserManager.class).isDemoUser()) {
241                             directories = ArrayUtils.appendElement(String.class,
242                                     mExternalStoragePaths,
243                                     Environment.getDataPreloadsMediaDirectory().getAbsolutePath());
244                         } else {
245                             directories = mExternalStoragePaths;
246                         }
247                     }
248 
249                     if (directories != null) {
250                         if (false) Log.d(TAG, "start scanning volume " + volume + ": "
251                                 + Arrays.toString(directories));
252                         scan(directories, volume);
253                         if (false) Log.d(TAG, "done scanning volume " + volume);
254                     }
255                 }
256             } catch (Exception e) {
257                 Log.e(TAG, "Exception in handleMessage", e);
258             }
259 
260             stopSelf(msg.arg1);
261         }
262     };
263 }
264