Figure2-1
AsFigure 2-1. MediaScannerReciver start up at anytime where receiveintent ACTION_BOOT_COMPLETED, ACTION_MEDIA_MOUNTED orACTION_MEDIA_SCANNER_SCAN_FILE. Cause on that spend long time to process the media metadata, so that MediaScannerReceiver will call upMediaScannerService.
MediaScannerServiceinvoke a public class which named MediaScanner to do the scan work,MediaScanner handle the media database with a public class whichnamed MediaProvider
MediaScannerReciversupport two types of the folder:
1.internalvolume, point to $(ANDROID_ROOT)/media.
2.Externalvolume, point $(EXTERNAL_STORAGE).
BroadcastAction: This is broadcast once, afterthe system has finished booting. It can be used to performapplication-specific initialization, such as installing alarms. Youmust hold theRECEIVE_BOOT_COMPLETED
permissionin order to receive this broadcast.
Thisis a protected intent that can only be sent by the system.
ConstantValue: "android.intent.action.BOOT_COMPLETED"
BroadcastAction: External media is present and mounted at its mount point. Thepath to the mount point for the removed media is contained in theIntent.mData field. The Intent contains an extra with name"read-only" and Boolean value to indicate if the media wasmounted read only.
ConstantValue: "android.intent.action.MEDIA_MOUNTED"
BroadcastAction: Request the media scanner to scan a file and add it to themedia database. The path to the file is contained in the Intent.mDatafield.
ConstantValue: "android.intent.action.MEDIA_SCANNER_SCAN_FILE"
Wecan find the source file with pathanydroid/packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerReceiver.java
publicclass MediaScannerReceiver extends BroadcastReceiver
{
@Override
publicvoid onReceive(Context context, Intent intent) {
......
if(action.equals(Intent.ACTION_BOOT_COMPLETED)) {
//scan internal storage
scan(context,MediaProvider.INTERNAL_VOLUME);
}else {
if(uri.getScheme().equals("file")) {
//handle intents related to external storage
Stringpath = uri.getPath();
if(action.equals(Intent.ACTION_MEDIA_MOUNTED) &&
externalStoragePath.equals(path)){
scan(context,MediaProvider.EXTERNAL_VOLUME);
}else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&
path!= null && path.startsWith(externalStoragePath + "/")){
scanFile(context,path);
}
}
}
}
privatevoid scan(Context context, String volume) {
Bundleargs = new Bundle();
args.putString("volume",volume);
context.startService(
newIntent(context, MediaScannerService.class).putExtras(args));
}
privatevoid scanFile(Context context, String path) {
Bundleargs = new Bundle();
args.putString("filepath",path);
context.startService(
newIntent(context, MediaScannerService.class).putExtras(args));
}
}
Figure4-1
Asthe source codes, and Figure 4-1, there are two different mediadatabase in the system, oneis the internal storage , the other is the external storage. Finally,they call the method startService() to start the MediaScannerService.
Wecan find the source file with path/android/packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerService.java.
publicvoid onCreate()
{
......
Threadthr = new Thread(null, this, "MediaScannerService");
thr.start();
}
publicint onStartCommand(Intent intent, int flags, int startId)
{
......
Messagemsg = mServiceHandler.obtainMessage();
......
mServiceHandler.sendMessage(msg); returnService.START_REDELIVER_INTENT;
}
publicvoid run()
{
......
mServiceHandler= new ServiceHandler();
......
}
privatefinal class ServiceHandler extends Handler
{
......
@Override
publicvoid handleMessage(Message msg)
{
......
if(filePath != null) {
......
Uriuri = scanFile(filePath, arguments.getString("mimetype"));
......
}
else{
......
scan(directories,volume);
......
}
}
}
privatevoid scan(String[] directories, String volumeName) {
......
MediaScannerscanner = createMediaScanner();
scanner.scanDirectories(directories,volumeName);
......
}
privateUri scanFile(String path, String mimeType) {
......
MediaScannerscanner = createMediaScanner();
returnscanner.scanSingleFile(path, volumeName, mimeType);
}
Figure5-1
Androidapplication maybe block with invoking service, generic create a thread to run at the backend. First, media service call onCreate() tostart the service if it is not exist, then create a thread and runthread.start() to call the runnable method which has implemented withthe run(). In the run(), invoke a internal class named ServiceHandlerto scan file. In the method scan() and scanFile(), they all invoke apublic class named MediaScanner whom has method namedcreateMediaScanner() to process metadata and media dabase.
Wecan find the relate files with pathandroid/frameworks/base/media/java/android/media,
Figure6-1
javacodes
file://android/frameworks/base/media/java/android/media/MediaScanner.java
publicvoid scanDirectories(String[] directories, String volumeName) {
......
for(int i = 0; i < directories.length; i++) {
processDirectory(directories[i],MediaFile.sFileExtensions, mClient);
}
......
}
}
publicvoid scanFile(String path, long lastModified, long fileSize) {
//This is the callback funtion from native codes.
//Log.v(TAG, "scanFile: "+path);
doScanFile(path,null, lastModified, fileSize, false);
}
publicvoid scanFile(String path, String mimeType, long lastModified, longfileSize) {
doScanFile(path,mimeType, lastModified, fileSize, false);
}
publicUri doScanFile(String path, String mimeType, long lastModified, longfileSize, boolean scanAlways) {
......
if(isMetadataSupported(mFileType) ) {
processFile(path,mimeType, this);
}else if (MediaFile.isImageFileType(mFileType)) {
//we used to compute the width and height but it's not worth it
}
result= endFile(entry, ringtones, notifications, alarms, music, podcasts);
......
}
privateUri endFile(FileCacheEntry entry, boolean ringtones, booleannotifications,
boolean alarms,boolean music, boolean podcasts)
throwsRemoteException {
......
mMediaProvider.insert(...)// mMediaProvider.update(...)
......
}
privatenative void processDirectory(String path, String extensions,MediaScannerClient client);
privatenative void processFile(String path, String mimeType,MediaScannerClient client);
c++codes
file://android/frameworks/base/media/jni/android_media_MediaScanner.cpp
staticvoid
android_media_MediaScanner_processDirectory(JNIEnv*env, jobject thiz, jstring path, jstring extensions, jobjectclient)
{
MediaScanner *mp =(MediaScanner *)env->GetIntField(thiz, fields.context);
......
MyMediaScannerClientmyClient(env, client);
mp->processDirectory(pathStr,extensionsStr, myClient, ExceptionCheck, env);
......
}
staticvoid
android_media_MediaScanner_processFile(JNIEnv*env, jobject thiz, jstring path, jstring mimeType, jobject client)
{
MediaScanner *mp =(MediaScanner *)env->GetIntField(thiz, fields.context); ......
MyMediaScannerClientmyClient(env, client);
mp->processFile(pathStr,mimeTypeStr, myClient);
......
}
file://android/external/opencore/android/mediascanner.cpp
status_tMediaScanner::processDirectory(const char *path, const char*extensions,
MediaScannerClient&client, ExceptionCheck exceptionCheck, void* exceptionEnv)
{
......
result= doProcessDirectory(pathBuffer, pathRemaining, extensions, client,exceptionCheck, exceptionEnv);
......
}
status_tMediaScanner::doProcessDirectory(char *path, int pathRemaining, constchar* extensions,
MediaScannerClient&client, ExceptionCheck exceptionCheck, void* exceptionEnv)
{
......
client.scanFile(path,statbuf.st_mtime, statbuf.st_size);
......
}
status_tMediaScanner::processFile(const char *path, const char* mimeType,MediaScannerClient& client)
{
status_t result =PVMFSuccess;
interror = 0;
InitializeForThread();
OSCL_TRY(error,
client.setLocale(mLocale);
client.beginFile();
//LOGD("processFile%s mimeType: %s/n", path, mimeType);
const char* extension =strrchr(path, '.');
if (extension &&
(strcasecmp(extension,".mp3") == 0 || strcasecmp(extension, ".aac") ==0)) {
// Both mp3 and aacfiles use ID3 tags to hold metadata
result =parseID3Tag(path, client);
} else if (extension &&
(strcasecmp(extension,".mp4") == 0 || strcasecmp(extension, ".m4a") ==0 ||
strcasecmp(extension,".3gp") == 0 || strcasecmp(extension, ".3gpp") ==0 ||
strcasecmp(extension,".3g2") == 0 || strcasecmp(extension, ".m4b") ==0 ||
strcasecmp(extension,".3gpp2") == 0)) {
result =parseMP4(path, client);
} else if (extension &&strcasecmp(extension, ".ogg") == 0) {
result =parseOgg(path, client);
} else if (extension &&
(strcasecmp(extension,".mid") == 0 || strcasecmp(extension, ".smf") ==0 ||
strcasecmp(extension,".imy") == 0)) {
result =parseMidi(path, client);
} else if (extension &&
(strcasecmp(extension,".wma") == 0 || strcasecmp(extension, ".wmv") ==0 ||
strcasecmp(extension,".asf") == 0 || strcasecmp(extension, ".amr") ==0 ||
strcasecmp(extension,".wav") == 0 || strcasecmp(extension, ".awb") ==0)) {
result =parseASF(path, client);
} else {
result =PVMFFailure;
}
client.endFile();
);
OSCL_FIRST_CATCH_ANY(error,LOGV("OSCL_LEAVE happened in processFile Exit withfailure");return PVMFFailure);
return result;
}
AsFigure 6-1, If scanDirectory() has called by MediaScannerService, itwill invoke c++ llibrary libmedia_jni method processDiretorys() byJNI mechanism, then processDiretorys() invoke JAVA classMyMediaScannerClient by JNI, Finaly, endFile() use to insert orupdate the database.