您的位置:首页 > 移动开发 > Android开发

Android Media Scanner Mechanism Analyze

2011-01-10 11:18 507 查看
Architecture

 

 



 

 

Figure 2-1

As Figure 2-1. MediaScannerReciver start up at anytime where receive intent ACTION_BOOT_COMPLETED, ACTION_MEDIA_MOUNTED or ACTION_MEDIA_SCANNER_SCAN_FILE. Cause on that spend long time to process the media metadata, so that MediaScannerReceiver will call up MediaScannerService.

MediaScannerService invoke a public class which named MediaScanner to do the scan work, MediaScanner handle the media database with a public class which named MediaProvider

MediaScannerReciver support two types of the folder:

1.internal volume, point to $(ANDROID_ROOT)/media.

2.External volume, point $(EXTERNAL_STORAGE).
Scanner Action

ACTION_BOOT_COMPLETED
public static final String ACTION_BOOT_COMPLETED

Broadcast Action: This is broadcast once, after the system has finished booting. It can be used to perform application-specific initialization, such as installing alarms. You must hold theRECEIVE_BOOT_COMPLETED permission in order to receive this broadcast.

This is a protected intent that can only be sent by the system.

Constant Value: "android.intent.action.BOOT_COMPLETED"
ACTION_MEDIA_MOUNTED
public static final String ACTION_MEDIA_MOUNTED

Broadcast Action: External media is present and mounted at its mount point. The path to the mount point for the removed media is contained in the Intent.mData field. The Intent contains an extra with name "read-only" and Boolean value to indicate if the media was mounted read only.

Constant Value: "android.intent.action.MEDIA_MOUNTED"
ACTION_MEDIA_SCANNER_SCAN_FILE.
public static final String ACTION_MEDIA_SCANNER_SCAN_FILE

Broadcast Action: Request the media scanner to scan a file and add it to the media database. The path to the file is contained in the Intent.mData field.

Constant Value: "android.intent.action.MEDIA_SCANNER_SCAN_FILE"
Android Media Scanner Receiver

We can find the source file with path anydroid/packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerReceiver.java

public class MediaScannerReceiver extends BroadcastReceiver

{

@Override

public void 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

String path = 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);

}

}

}

}

private void scan(Context context, String volume) {

Bundle args = new Bundle();

args.putString("volume", volume);

context.startService(

new Intent(context, MediaScannerService.class).putExtras(args));

}

private void scanFile(Context context, String path) {

Bundle args = new Bundle();

args.putString("filepath", path);

context.startService(

new Intent(context, MediaScannerService.class).putExtras(args));

}

}



Figure 4-1

As the source codes, and Figure 4-1, there are two different media database in the system, one is the internal storage , the other is the external storage. Finally, they call the method startService() to start the MediaScannerService.
Android Media Scanner Service

We can find the source file with path /android/packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerService.java .

public void onCreate()

{

......

Thread thr = new Thread(null, this, "MediaScannerService");

thr.start();

}

public int onStartCommand(Intent intent, int flags, int startId)

{

......

Message msg = mServiceHandler.obtainMessage();

......

mServiceHandler.sendMessage(msg); return Service.START_REDELIVER_INTENT;

}

public void run()

{

......

mServiceHandler = new ServiceHandler();

......

}

private final class ServiceHandler extends Handler

{

......

@Override

public void handleMessage(Message msg)

{

......

if (filePath != null) {

......

Uri uri = scanFile(filePath, arguments.getString("mimetype"));

......

}

else {

......

scan(directories, volume);

......

}

}

}

private void scan(String[] directories, String volumeName) {

......

MediaScanner scanner = createMediaScanner();

scanner.scanDirectories(directories, volumeName);

......

}

private Uri scanFile(String path, String mimeType) {

......

MediaScanner scanner = createMediaScanner();

return scanner.scanSingleFile(path, volumeName, mimeType);

}



Figure 5-1

Android application maybe block with invoking service, generic create a thread to run at the backend. First, media service call onCreate() to start the service if it is not exist, then create a thread and run thread.start() to call the runnable method which has implemented with the run(). In the run(), invoke a internal class named ServiceHandler to scan file. In the method scan() and scanFile(), they all invoke a public class named MediaScanner whom has method named createMediaScanner() to process metadata and media dabase.
Android Media Scanner

We can find the relate files with path android/frameworks/base/media/java/android/media,



Figure 6-1

java codes

file://android/frameworks/base/media/java/android/media/MediaScanner.java

public void scanDirectories(String[] directories, String volumeName) {

......

for (int i = 0; i < directories.length; i++) {

processDirectory(directories[i], MediaFile.sFileExtensions, mClient);

}

......

}

}

public void 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);

}

public void scanFile(String path, String mimeType, long lastModified, long fileSize) {

doScanFile(path, mimeType, lastModified, fileSize, false);

}

public Uri doScanFile(String path, String mimeType, long lastModified, long fileSize, 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);

......

}

private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications,

boolean alarms, boolean music, boolean podcasts)

throws RemoteException {

......

mMediaProvider.insert(...) // mMediaProvider.update(...)

......

}

private native void processDirectory(String path, String extensions, MediaScannerClient client);

private native void processFile(String path, String mimeType, MediaScannerClient client);

c++ codes

file://android/frameworks/base/media/jni/android_media_MediaScanner.cpp

static void

android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client)

{

MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);

......

MyMediaScannerClient myClient(env, client);

mp->processDirectory(pathStr, extensionsStr, myClient, ExceptionCheck, env);

......

}

static void

android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, jstring mimeType, jobject client)

{

MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); ......

MyMediaScannerClient myClient(env, client);

mp->processFile(pathStr, mimeTypeStr, myClient);

......

}

file://android/external/opencore/android/mediascanner.cpp

status_t MediaScanner::processDirectory(const char *path, const char* extensions,

MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv)

{

......

result = doProcessDirectory(pathBuffer, pathRemaining, extensions, client, exceptionCheck, exceptionEnv);

......

}

status_t MediaScanner::doProcessDirectory(char *path, int pathRemaining, const char* extensions,

MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv)

{

......

client.scanFile(path, statbuf.st_mtime, statbuf.st_size);

......

}

status_t MediaScanner::processFile(const char *path, const char* mimeType, MediaScannerClient& client)

{

status_t result = PVMFSuccess;

int error = 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 aac files 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 with failure");return PVMFFailure);

return result;

}

As Figure 6-1, If scanDirectory() has called by MediaScannerService, it will invoke c++ llibrary libmedia_jni method processDiretorys() by JNI mechanism, then processDiretorys() invoke JAVA class MyMediaScannerClient by JNI, Finaly, endFile() use to insert or update the database.

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息