Android媒体扫描代码分析


http://blog.csdn.net/jsyzgygcj/article/details/44178545

插入u盘后,系统会发送广播android.intent.action.MEDIA_MOUNTED

位于
packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerService.java

MediaScannerService可以接收开机广播和u盘插入广播,收到广播后执行scan方法,将文件或文件夹路径存储到Bundle,并启动MediaScannerService,我们看MediaScannerService中ServiceHandler的代码


<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ServiceHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Handler</span>
    {</span>
        <span class="hljs-annotation">@Override</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleMessage</span>(Message msg)
        {
            Bundle arguments = (Bundle) msg.obj;
            String filePath = arguments.getString(<span class="hljs-string">"filepath"</span>);<span class="hljs-comment">//单个文件</span>
            String path = arguments.getString(<span class="hljs-string">"path"</span>);<span class="hljs-comment">//文件目录</span>

            <span class="hljs-keyword">try</span> {
                <span class="hljs-keyword">if</span> (filePath != <span class="hljs-keyword">null</span>) {
                    IBinder binder = arguments.getIBinder(<span class="hljs-string">"listener"</span>);
                    IMediaScannerListener listener = 
                            (binder == <span class="hljs-keyword">null</span> ? <span class="hljs-keyword">null</span> : IMediaScannerListener.Stub.asInterface(binder));
                    Uri uri = <span class="hljs-keyword">null</span>;
                    <span class="hljs-keyword">try</span> {
                        uri = scanFile(filePath, arguments.getString(<span class="hljs-string">"mimetype"</span>));<span class="hljs-comment">//扫描单个文件</span>
                    } <span class="hljs-keyword">catch</span> (Exception e) {
                        Log.e(TAG, <span class="hljs-string">"Exception scanning file"</span>, e);
                    }
                    <span class="hljs-keyword">if</span> (listener != <span class="hljs-keyword">null</span>) {
                        listener.scanCompleted(filePath, uri);
                    }
                } <span class="hljs-keyword">else</span> {
                    String volume = arguments.getString(<span class="hljs-string">"volume"</span>);
                    String[] directories = <span class="hljs-keyword">null</span>;

                    <span class="hljs-keyword">if</span> (MediaProvider.INTERNAL_VOLUME.equals(volume)) {
                        <span class="hljs-comment">// scan internal media storage</span>
                        directories = <span class="hljs-keyword">new</span> String[] {
                                Environment.getRootDirectory() + <span class="hljs-string">"/media"</span>,
                        };
                    }
                    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (MediaProvider.EXTERNAL_VOLUME.equals(volume)) {
                        <span class="hljs-comment">// scan external storage volumes</span>
            <span class="hljs-keyword">if</span> (path == <span class="hljs-keyword">null</span>) {
                          directories = mExternalStoragePaths;
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (path.startsWith(<span class="hljs-string">"/mnt/usb_storage"</span>)) {
                directories = <span class="hljs-keyword">new</span> String[] {path};<span class="hljs-comment">//{"/mnt/usb_storage"};</span>
                        } <span class="hljs-keyword">else</span> {
                            directories = <span class="hljs-keyword">new</span> String[] {path};
            }
                    }

                    <span class="hljs-keyword">if</span> (directories != <span class="hljs-keyword">null</span>) {
                        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">true</span>) Log.d(TAG, <span class="hljs-string">"start scanning volume "</span> + volume + <span class="hljs-string">": "</span>
                                + Arrays.toString(directories));
                        scan(directories, volume);<span class="hljs-comment">//扫描文件列表</span>
                        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">true</span>) Log.d(TAG, <span class="hljs-string">"done scanning volume "</span> + volume);
                    }
                }
            } <span class="hljs-keyword">catch</span> (Exception e) {
                Log.e(TAG, <span class="hljs-string">"Exception in handleMessage"</span>, e);
            }

            stopSelf(msg.arg1);
        }
    };</code>



可以看到代码会调用scan或者scanFile扫描


<code class="hljs cs has-numbering">    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">scan</span>(String[] directories, String volumeName) {
        Uri uri = Uri.parse(<span class="hljs-string">"file://"</span> + directories[<span class="hljs-number">0</span>]);
        <span class="hljs-comment">// don't sleep while scanning</span>
        mWakeLock.acquire();

        <span class="hljs-keyword">try</span> {
            ContentValues values = <span class="hljs-keyword">new</span> ContentValues();
            values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
            Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);

            sendBroadcast(<span class="hljs-keyword">new</span> Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));

            <span class="hljs-keyword">try</span> {
                <span class="hljs-keyword">if</span> (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {
                    openDatabase(volumeName);
                }

            MediaScanner scanner = createMediaScanner();
        <span class="hljs-keyword">if</span>(directories!=<span class="hljs-keyword">null</span>){
              scanner.scanDirectories(directories, volumeName);
        }
        } <span class="hljs-keyword">catch</span> (Exception e) {
            Log.e(TAG, <span class="hljs-string">"exception in MediaScanner.scan()"</span>, e);
        }

            getContentResolver().delete(scanUri, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>);

        } <span class="hljs-keyword">finally</span> {
            sendBroadcast(<span class="hljs-keyword">new</span> Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
            mWakeLock.release();
        }
    }</code>


<code class="hljs lasso has-numbering">    <span class="hljs-keyword">private</span> Uri scanFile(<span class="hljs-built_in">String</span> path, <span class="hljs-built_in">String</span> mimeType) {
        <span class="hljs-built_in">String</span> volumeName <span class="hljs-subst">=</span> MediaProvider<span class="hljs-built_in">.</span>EXTERNAL_VOLUME;
        openDatabase(volumeName);
        MediaScanner scanner <span class="hljs-subst">=</span> createMediaScanner();
        try {
            <span class="hljs-comment">// make sure the file path is in canonical form</span>
            <span class="hljs-built_in">String</span> canonicalPath <span class="hljs-subst">=</span> <span class="hljs-literal">new</span> File(path)<span class="hljs-built_in">.</span>getCanonicalPath();
            <span class="hljs-keyword">return</span> scanner<span class="hljs-built_in">.</span>scanSingleFile(canonicalPath, volumeName, mimeType);
        } catch (Exception e) {
            <span class="hljs-keyword">Log</span><span class="hljs-built_in">.</span>e(<span class="hljs-built_in">TAG</span>, <span class="hljs-string">"bad path "</span> <span class="hljs-subst">+</span> path <span class="hljs-subst">+</span> <span class="hljs-string">" in scanFile()"</span>, e);
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">null</span>;
        }
    }</code>
下一步进入到MediaScanner.java,文件在

<code class="hljs cs has-numbering">frameworks/<span class="hljs-keyword">base</span>/media/java/android/media/MediaScanner.java</code>
下面是扫描单个文件的代码


<code class="hljs cs has-numbering">    <span class="hljs-comment">// this function is used to scan a single file</span>
    <span class="hljs-keyword">public</span> Uri <span class="hljs-title">scanSingleFile</span>(String path, String volumeName, String mimeType) {
        <span class="hljs-keyword">try</span> {
            initialize(volumeName);
            prescan(path, <span class="hljs-keyword">true</span>);

            File file = <span class="hljs-keyword">new</span> File(path);
            <span class="hljs-keyword">if</span> (!file.exists()) {
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
            }

            <span class="hljs-comment">// lastModified is in milliseconds on Files.</span>
            <span class="hljs-keyword">long</span> lastModifiedSeconds = file.lastModified() / <span class="hljs-number">1000</span>;

            <span class="hljs-comment">// always scan the file, so we can return the content://media Uri for existing files</span>
            <span class="hljs-keyword">return</span> mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
                    <span class="hljs-keyword">false</span>, <span class="hljs-keyword">true</span>, MediaScanner.isNoMediaPath(path));
        } <span class="hljs-keyword">catch</span> (RemoteException e) {
            Log.e(TAG, <span class="hljs-string">"RemoteException in MediaScanner.scanFile()"</span>, e);
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        }
    }
<span class="hljs-comment">//初始化默认的uri</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initialize</span>(String volumeName) {
        mMediaProvider = mContext.getContentResolver().acquireProvider(<span class="hljs-string">"media"</span>);

        mAudioUri = Audio.Media.getContentUri(volumeName);
        mVideoUri = Video.Media.getContentUri(volumeName);
        mImagesUri = Images.Media.getContentUri(volumeName);
        mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
        mFilesUri = Files.getContentUri(volumeName);
        mFilesUriNoNotify = mFilesUri.buildUpon().appendQueryParameter(<span class="hljs-string">"nonotify"</span>, <span class="hljs-string">"1"</span>).build();

        <span class="hljs-keyword">if</span> (!volumeName.equals(<span class="hljs-string">"internal"</span>)) {
            <span class="hljs-comment">// we only support playlists on external media</span>
            mProcessPlaylists = <span class="hljs-keyword">true</span>;
            mProcessGenres = <span class="hljs-keyword">true</span>;
            mPlaylistsUri = Playlists.getContentUri(volumeName);

            mCaseInsensitivePaths = <span class="hljs-keyword">true</span>;
        }
    }
<span class="hljs-comment">//扫描预处理,暂时不知道做什么用</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">prescan</span>(String filePath, boolean prescanFiles) throws RemoteException {
        Cursor c = <span class="hljs-keyword">null</span>;
        String <span class="hljs-keyword">where</span> = <span class="hljs-keyword">null</span>;
        String[] selectionArgs = <span class="hljs-keyword">null</span>;

        <span class="hljs-keyword">if</span> (mPlayLists == <span class="hljs-keyword">null</span>) {
            mPlayLists = <span class="hljs-keyword">new</span> ArrayList<FileEntry>();
        } <span class="hljs-keyword">else</span> {
            mPlayLists.clear();
        }

        <span class="hljs-keyword">if</span> (filePath != <span class="hljs-keyword">null</span>) {
            <span class="hljs-comment">// query for only one file</span>
            <span class="hljs-keyword">where</span> = MediaStore.Files.FileColumns._ID + <span class="hljs-string">">?"</span> +
                <span class="hljs-string">" AND "</span> + Files.FileColumns.DATA + <span class="hljs-string">" like '"</span>+filePath+<span class="hljs-string">"%' "</span>;
            selectionArgs = <span class="hljs-keyword">new</span> String[] { <span class="hljs-string">""</span> };
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">where</span> = MediaStore.Files.FileColumns._ID + <span class="hljs-string">">?"</span>;
            selectionArgs = <span class="hljs-keyword">new</span> String[] { <span class="hljs-string">""</span> };
        }
        <span class="hljs-keyword">if</span> (DEBUG_MEDIASCANNER)
            Log.d(TAG,<span class="hljs-string">"-----------enter prescan,filePath = "</span>+filePath+<span class="hljs-string">"where = "</span>+<span class="hljs-keyword">where</span>);

        <span class="hljs-comment">// Tell the provider to not delete the file.</span>
        <span class="hljs-comment">// If the file is truly gone the delete is unnecessary, and we want to avoid</span>
        <span class="hljs-comment">// accidentally deleting files that are really there (this may happen if the</span>
        <span class="hljs-comment">// filesystem is mounted and unmounted while the scanner is running).</span>
        Uri.Builder builder = mFilesUri.buildUpon();
        builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, <span class="hljs-string">"false"</span>);
        MediaBulkDeleter deleter = <span class="hljs-keyword">new</span> MediaBulkDeleter(mMediaProvider, mPackageName,
                builder.build());

        <span class="hljs-comment">// Build the list of files from the content provider</span>
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">if</span> (prescanFiles) {
                <span class="hljs-comment">// First read existing files from the files table.</span>
                <span class="hljs-comment">// Because we'll be deleting entries for missing files as we go,</span>
                <span class="hljs-comment">// we need to query the database in small batches, to avoid problems</span>
                <span class="hljs-comment">// with CursorWindow positioning.</span>
                <span class="hljs-keyword">long</span> lastId = Long.MIN_VALUE;
                Uri limitUri = mFilesUri.buildUpon().appendQueryParameter(<span class="hljs-string">"limit"</span>, <span class="hljs-string">"1000"</span>).build();
                mWasEmptyPriorToScan = <span class="hljs-keyword">true</span>;

                <span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) {
                    selectionArgs[<span class="hljs-number">0</span>] = <span class="hljs-string">""</span> + lastId;
                    <span class="hljs-keyword">if</span> (c != <span class="hljs-keyword">null</span>) {
                        c.close();
                        c = <span class="hljs-keyword">null</span>;
                    }
                    c = mMediaProvider.query(mPackageName, limitUri, FILES_PRESCAN_PROJECTION,
                            <span class="hljs-keyword">where</span>, selectionArgs, MediaStore.Files.FileColumns._ID, <span class="hljs-keyword">null</span>);
                    <span class="hljs-keyword">if</span> (c == <span class="hljs-keyword">null</span>) {
                        <span class="hljs-keyword">break</span>;
                    }

                    <span class="hljs-keyword">int</span> num = c.getCount();

                    <span class="hljs-keyword">if</span> (num == <span class="hljs-number">0</span>) {
                        <span class="hljs-keyword">break</span>;
                    }
                    mWasEmptyPriorToScan = <span class="hljs-keyword">false</span>;
                    <span class="hljs-keyword">while</span> (c.moveToNext()) {
                        <span class="hljs-keyword">long</span> rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
                        String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
                        <span class="hljs-keyword">int</span> format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
                        <span class="hljs-keyword">long</span> lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
                        lastId = rowId;

                        <span class="hljs-comment">// Only consider entries with absolute path names.</span>
                        <span class="hljs-comment">// This allows storing URIs in the database without the</span>
                        <span class="hljs-comment">// media scanner removing them.</span>
                        <span class="hljs-keyword">if</span> (path != <span class="hljs-keyword">null</span> && path.startsWith(<span class="hljs-string">"/"</span>)) {
                            boolean exists = <span class="hljs-keyword">false</span>;
                            <span class="hljs-keyword">try</span> {
                                exists = Libcore.os.access(path, libcore.io.OsConstants.F_OK);
                            } <span class="hljs-keyword">catch</span> (ErrnoException e1) {
                            }
                            <span class="hljs-keyword">if</span> (!exists && !MtpConstants.isAbstractObject(format)) {
                                <span class="hljs-comment">// do not delete missing playlists, since they may have been</span>
                                <span class="hljs-comment">// modified by the user.</span>
                                <span class="hljs-comment">// The user can delete them in the media player instead.</span>
                                <span class="hljs-comment">// instead, clear the path and lastModified fields in the row</span>
                                MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
                                <span class="hljs-keyword">int</span> fileType = (mediaFileType == <span class="hljs-keyword">null</span> ? <span class="hljs-number">0</span> : mediaFileType.fileType);

                                <span class="hljs-keyword">if</span> (!MediaFile.isPlayListFileType(fileType)) {
                                    deleter.delete(rowId);
                                    <span class="hljs-keyword">if</span> (path.toLowerCase(Locale.US).endsWith(<span class="hljs-string">"/.nomedia"</span>)) {
                                        deleter.flush();
                                        String parent = <span class="hljs-keyword">new</span> File(path).getParent();
                                        mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL,
                                                parent, <span class="hljs-keyword">null</span>);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        <span class="hljs-keyword">finally</span> {
            <span class="hljs-keyword">if</span> (c != <span class="hljs-keyword">null</span>) {
                c.close();
            }
            deleter.flush();
        }

        <span class="hljs-comment">// compute original size of images</span>
        mOriginalCount = <span class="hljs-number">0</span>;
        c = mMediaProvider.query(mPackageName, mImagesUri, ID_PROJECTION, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>);
        <span class="hljs-keyword">if</span> (c != <span class="hljs-keyword">null</span>) {
            mOriginalCount = c.getCount();
            c.close();
        }
    }</code>
下面是真正的扫描实现


<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> Uri <span class="hljs-title">doScanFile</span>(String path, String mimeType, <span class="hljs-keyword">long</span> lastModified,
                <span class="hljs-keyword">long</span> fileSize, <span class="hljs-keyword">boolean</span> isDirectory, <span class="hljs-keyword">boolean</span> scanAlways, <span class="hljs-keyword">boolean</span> noMedia) {
            Uri result = <span class="hljs-keyword">null</span>;
<span class="hljs-comment">//            long t1 = System.currentTimeMillis();</span>
            <span class="hljs-keyword">try</span> {
                FileEntry entry = beginFile(path, mimeType, lastModified,
                        fileSize, isDirectory, noMedia);
                <span class="hljs-keyword">if</span> (path.startsWith(<span class="hljs-string">"/mnt/usb_storage"</span>))
                    mIsUsbStorage =<span class="hljs-keyword">true</span>;
                <span class="hljs-keyword">else</span>
                    mIsUsbStorage =<span class="hljs-keyword">false</span>;
                <span class="hljs-comment">// if this file was just inserted via mtp, set the rowid to zero</span>
                <span class="hljs-comment">// (even though it already exists in the database), to trigger</span>
                <span class="hljs-comment">// the correct code path for updating its entry</span>
                <span class="hljs-keyword">if</span> (mMtpObjectHandle != <span class="hljs-number">0</span>) {
                    entry.mRowId = <span class="hljs-number">0</span>;
                }
                <span class="hljs-comment">// rescan for metadata if file was modified since last scan</span>
                <span class="hljs-keyword">if</span> (entry != <span class="hljs-keyword">null</span> && (entry.mLastModifiedChanged || scanAlways)) {
                    <span class="hljs-keyword">if</span> (noMedia) {
                        result = endFile(entry, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>);
                    } <span class="hljs-keyword">else</span> {
                        String lowpath = path.toLowerCase(Locale.ROOT);
                        <span class="hljs-keyword">boolean</span> ringtones = (lowpath.indexOf(RINGTONES_DIR) > <span class="hljs-number">0</span>);
                        <span class="hljs-keyword">boolean</span> notifications = (lowpath.indexOf(NOTIFICATIONS_DIR) > <span class="hljs-number">0</span>);
                        <span class="hljs-keyword">boolean</span> alarms = (lowpath.indexOf(ALARMS_DIR) > <span class="hljs-number">0</span>);
                        <span class="hljs-keyword">boolean</span> podcasts = (lowpath.indexOf(PODCAST_DIR) > <span class="hljs-number">0</span>);
                        <span class="hljs-keyword">boolean</span> music = (lowpath.indexOf(MUSIC_DIR) > <span class="hljs-number">0</span>) ||
                            (!ringtones && !notifications && !alarms && !podcasts);

                        <span class="hljs-keyword">boolean</span> isaudio = MediaFile.isAudioFileType(mFileType);
                        <span class="hljs-keyword">boolean</span> isvideo = MediaFile.isVideoFileType(mFileType);
                        <span class="hljs-keyword">boolean</span> isimage = MediaFile.isImageFileType(mFileType);

                        <span class="hljs-keyword">if</span> (isaudio || isvideo || isimage) {
                            <span class="hljs-keyword">if</span> (mExternalIsEmulated && path.startsWith(mExternalStoragePath)) {
                                <span class="hljs-comment">// try to rewrite the path to bypass the sd card fuse layer</span>
                                String directPath = Environment.getMediaStorageDirectory() +
                                        path.substring(mExternalStoragePath.length());
                                File f = <span class="hljs-keyword">new</span> File(directPath);
                                <span class="hljs-keyword">if</span> (f.exists()) {
                                    path = directPath;
                                }
                            }
                        }

                        <span class="hljs-comment">// we only extract metadata for audio and video files</span>
                        <span class="hljs-keyword">if</span> (isaudio <span class="hljs-comment">/*|| isvideo */</span>)
                        {
                        <span class="hljs-comment">//jni方法调用</span>
                            processFile(path, mimeType, <span class="hljs-keyword">this</span>);
                        }

                        <span class="hljs-comment">//if (isimage) {</span>
                        <span class="hljs-comment">//    processImageFile(path);</span>
                        <span class="hljs-comment">//}</span>

                        result = endFile(entry, ringtones, notifications, alarms, music, podcasts);
                    }
                }
            } <span class="hljs-keyword">catch</span> (RemoteException e) {
                Log.e(TAG, <span class="hljs-string">"RemoteException in MediaScanner.scanFile()"</span>, e);
            }
<span class="hljs-comment">//            long t2 = System.currentTimeMillis();</span>
<span class="hljs-comment">//            Log.v(TAG, "scanFile: " + path + " took " + (t2-t1));</span>
            <span class="hljs-keyword">return</span> result;
        }</code>

我们看一下英文说明

<code class="hljs applescript has-numbering"> MediaScanner.processFile().
 * - MediaScanner.processFile() calls one <span class="hljs-keyword">of</span> several methods, depending <span class="hljs-function_start"><span class="hljs-keyword">on</span></span> <span class="hljs-keyword">the</span> type <span class="hljs-keyword">of</span> <span class="hljs-keyword">the</span>
 *   <span class="hljs-type">file</span>: parseMP3, parseMP4, parseMidi, parseOgg <span class="hljs-keyword">or</span> parseWMA.
 * - each <span class="hljs-keyword">of</span> these methods gets metadata key/value pairs <span class="hljs-keyword">from</span> <span class="hljs-keyword">the</span> <span class="hljs-type">file</span>, <span class="hljs-keyword">and</span> repeatedly
 *   calls native MyMediaScannerClient.handleStringTag, which calls <span class="hljs-keyword">back</span> up <span class="hljs-keyword">to</span> <span class="hljs-keyword">its</span> Java
 *   counterparts <span class="hljs-keyword">in</span> this <span class="hljs-type">file</span>.</code>

大意就是,processFile会根据文件类型调用不用的方法,并由MyMediaScannerClient.handleStringTag回调

这段native方法可以在下面的文件目录找到

<code class="hljs cs has-numbering">frameworks/<span class="hljs-keyword">base</span>/media/jni/android_media_MediaScanner.cpp</code>
<code class="hljs lasso has-numbering">static <span class="hljs-literal">void</span>
android_media_MediaScanner_processFile(
        JNIEnv <span class="hljs-subst">*</span>env, jobject thiz, jstring path,
        jstring mimeType, jobject client)
{
    ALOGV(<span class="hljs-string">"processFile"</span>);

    <span class="hljs-comment">// Lock already hold by processDirectory</span>
    MediaScanner <span class="hljs-subst">*</span>mp <span class="hljs-subst">=</span> getNativeScanner_l(env, thiz);
    <span class="hljs-keyword">if</span> (mp <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>) {
        jniThrowException(env, kRunTimeException, <span class="hljs-string">"No scanner available"</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">if</span> (path <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>) {
        jniThrowException(env, kIllegalArgumentException, <span class="hljs-built_in">NULL</span>);
        <span class="hljs-keyword">return</span>;
    }

    const char <span class="hljs-subst">*</span>pathStr <span class="hljs-subst">=</span> env<span class="hljs-subst">-></span>GetStringUTFChars(path, <span class="hljs-built_in">NULL</span>);
    <span class="hljs-keyword">if</span> (pathStr <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>) {  <span class="hljs-comment">// Out of memory</span>
        <span class="hljs-keyword">return</span>;
    }

    const char <span class="hljs-subst">*</span>mimeTypeStr <span class="hljs-subst">=</span>
        (mimeType <span class="hljs-subst">?</span> env<span class="hljs-subst">-></span>GetStringUTFChars(mimeType, <span class="hljs-built_in">NULL</span>) : <span class="hljs-built_in">NULL</span>);
    <span class="hljs-keyword">if</span> (mimeType <span class="hljs-subst">&&</span> mimeTypeStr <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>) {  <span class="hljs-comment">// Out of memory</span>
        <span class="hljs-comment">// ReleaseStringUTFChars can be called with an exception pending.</span>
        env<span class="hljs-subst">-></span>ReleaseStringUTFChars(path, pathStr);
        <span class="hljs-keyword">return</span>;
    }

    MyMediaScannerClient myClient(env, client);
    MediaScanResult result <span class="hljs-subst">=</span> mp<span class="hljs-subst">-></span>processFile(pathStr, mimeTypeStr, myClient);
    <span class="hljs-keyword">if</span> (result <span class="hljs-subst">==</span> MEDIA_SCAN_RESULT_ERROR) {
        ALOGE(<span class="hljs-string">"An error occurred while scanning file '%s'."</span>, pathStr);
    }
    env<span class="hljs-subst">-></span>ReleaseStringUTFChars(path, pathStr);
    <span class="hljs-keyword">if</span> (mimeType) {
        env<span class="hljs-subst">-></span>ReleaseStringUTFChars(mimeType, mimeTypeStr);
    }
}</code>
mp->processFile在路径

<code class="hljs perl has-numbering">通过<span class="hljs-keyword">grep</span>搜索发现,方法定义在文件frameworks/av/media/libmedia/MediaScanner.h</code>
可以看到这是一个虚函数

<code class="hljs cs has-numbering"> <span class="hljs-keyword">virtual</span> MediaScanResult processFile(
            <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *path, <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *mimeType, MediaScannerClient &client) = <span class="hljs-number">0</span>;</code>
虚函数定义如下:如果父类的函数(方法)根本没有必要或者无法实现,完全要依赖子类去实现的话,可以把此函数(方法)设为virtual 函数名=0 我们把这样的函数(方法)称为纯虚函数如果一个类包含了纯虚函数,称此类为抽象类。
我们接着往下跟,找到真正实现的地方
回到前一个jni

<code class="hljs cs has-numbering">frameworks/<span class="hljs-keyword">base</span>/media/jni/android_media_MediaScanner.cpp</code>
<code class="hljs cs has-numbering"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span>
android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
{
    ALOGV(<span class="hljs-string">"native_setup"</span>);
    MediaScanner *mp = <span class="hljs-keyword">new</span> StagefrightMediaScanner;

    <span class="hljs-keyword">if</span> (mp == NULL) {
        jniThrowException(env, kRunTimeException, <span class="hljs-string">"Out of memory"</span>);
        <span class="hljs-keyword">return</span>;
    }

    env->SetIntField(thiz, fields.context, (<span class="hljs-keyword">int</span>)mp);
}</code>
马上就能找到实例化的地方了,打开

<code class="hljs tex has-numbering">frameworks<span class="hljs-command">\av</span><span class="hljs-command">\media</span><span class="hljs-command">\libstagefright</span><span class="hljs-command">\StagefrightMediaScanner</span>.cpp</code>
<code class="hljs cs has-numbering">MediaScanResult StagefrightMediaScanner::processFile(
        <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *path, <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *mimeType,
        MediaScannerClient &client) {
    ALOGV(<span class="hljs-string">"processFile '%s'."</span>, path);

    client.setLocale(locale());
    client.beginFile();
    MediaScanResult result = processFileInternal(path, mimeType, client);
    client.endFile();
    <span class="hljs-keyword">return</span> result;
}

MediaScanResult StagefrightMediaScanner::processFileInternal(
        <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *path, <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *mimeType,
        MediaScannerClient &client) {
    <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *extension = strrchr(path, <span class="hljs-string">'.'</span>);

    <span class="hljs-keyword">if</span> (!extension) {
        <span class="hljs-keyword">return</span> MEDIA_SCAN_RESULT_SKIPPED;
    }

    <span class="hljs-comment">//过滤类型</span>
    <span class="hljs-keyword">if</span> (!FileHasAcceptableExtension(extension)) {
        <span class="hljs-keyword">return</span> MEDIA_SCAN_RESULT_SKIPPED;
    }

    <span class="hljs-comment">//根据扩展名调用不同的解析方法</span>
    <span class="hljs-keyword">if</span> (!strcasecmp(extension, <span class="hljs-string">".mid"</span>)
            || !strcasecmp(extension, <span class="hljs-string">".smf"</span>)
            || !strcasecmp(extension, <span class="hljs-string">".imy"</span>)
            || !strcasecmp(extension, <span class="hljs-string">".midi"</span>)
            || !strcasecmp(extension, <span class="hljs-string">".xmf"</span>)
            || !strcasecmp(extension, <span class="hljs-string">".rtttl"</span>)
            || !strcasecmp(extension, <span class="hljs-string">".rtx"</span>)
            || !strcasecmp(extension, <span class="hljs-string">".ota"</span>)
            || !strcasecmp(extension, <span class="hljs-string">".mxmf"</span>)) {
        <span class="hljs-keyword">return</span> HandleMIDI(path, &client);
    }
    <span class="hljs-comment">//parse ape audio file</span>
    <span class="hljs-keyword">if</span> (!strcasecmp(extension, <span class="hljs-string">".ape"</span>)) {
        <span class="hljs-keyword">return</span> parseAPE(path, client);
    }
    sp<MediaMetadataRetriever> mRetriever(<span class="hljs-keyword">new</span> MediaMetadataRetriever);

    <span class="hljs-keyword">int</span> fd = open(path, O_RDONLY | O_LARGEFILE);
    status_t status;
    <span class="hljs-keyword">if</span> (fd < <span class="hljs-number">0</span>) {
        <span class="hljs-comment">// couldn't open it locally, maybe the media server can?</span>
        status = mRetriever->setDataSource(path);
    } <span class="hljs-keyword">else</span> {
        status = mRetriever->setDataSource(fd, <span class="hljs-number">0</span>, <span class="hljs-number">0x7ffffffffffffff</span>L);
        close(fd);
    }

    <span class="hljs-keyword">if</span> (status) {
        <span class="hljs-keyword">return</span> MEDIA_SCAN_RESULT_ERROR;
    }

    <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *<span class="hljs-keyword">value</span>;
    <span class="hljs-keyword">if</span> ((<span class="hljs-keyword">value</span> = mRetriever->extractMetadata(
                    METADATA_KEY_MIMETYPE)) != NULL) {
        status = client.setMimeType(<span class="hljs-keyword">value</span>);
        <span class="hljs-keyword">if</span> (status) {
            <span class="hljs-keyword">return</span> MEDIA_SCAN_RESULT_ERROR;
        }
    }</code>

client.endFile()这个方法的实现在

<code class="hljs tex has-numbering">frameworks<span class="hljs-command">\av</span><span class="hljs-command">\media</span><span class="hljs-command">\libmedia</span><span class="hljs-command">\MediaScannerClient</span>.cpp</code>
<code class="hljs cpp has-numbering"><span class="hljs-keyword">void</span> MediaScannerClient::endFile()
{
    <span class="hljs-keyword">if</span> (mLocaleEncoding != kEncodingNone) {
        <span class="hljs-keyword">int</span> size = mNames->size();
        uint32_t encoding = kEncodingAll;

        <span class="hljs-comment">// compute a bit mask containing all possible encodings</span>

                <span class="hljs-keyword">char</span>* isUtf8 = <span class="hljs-keyword">new</span> <span class="hljs-keyword">char</span>[size];
                <span class="hljs-built_in">memset</span>(isUtf8,<span class="hljs-number">0</span>,size);
                <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < mNames->size(); i++)
                {
                       <span class="hljs-comment">// ALOGE("I=%d ,names=%s , values=%s,mLocaleEncoding =0x%x \n",i,mNames->getEntry(i),mValues->getEntry(i),mLocaleEncoding);</span>
                        uint32_t tmpEncoding = kEncodingAll;
                        <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> * name = mNames->getEntry(i);

                        <span class="hljs-comment">//when the system languge is russian ,the title/album/artist/must think it is win1251 .</span>
                        <span class="hljs-keyword">if</span>((mLocaleEncoding == kEncodingWin1251 || mLocaleEncoding == kEncodingWin1252) && (!<span class="hljs-built_in">strncmp</span>(name, <span class="hljs-string">"tit"</span>, <span class="hljs-number">3</span>)||!<span class="hljs-built_in">strncmp</span>(name, <span class="hljs-string">"alb"</span>, <span class="hljs-number">3</span>)||!<span class="hljs-built_in">strncmp</span>(name, <span class="hljs-string">"art"</span>, <span class="hljs-number">3</span>)))
                            tmpEncoding = kEncodingNone;
                        <span class="hljs-keyword">else</span>
                          tmpEncoding = possibleEncodings(mValues->getEntry(i));
                        <span class="hljs-keyword">if</span>(tmpEncoding == kEncodingNone)
                        {
                            ALOGI(<span class="hljs-string">"%s value can't discern,the value is %s"</span>,mNames->getEntry(i),mValues->getEntry(i));
                            <span class="hljs-keyword">if</span>( mLocaleEncoding == kEncodingWin1251 || mLocaleEncoding == kEncodingWin1252 )
                            {
                                <span class="hljs-comment">//revert the string value the original string</span>
                                 int8_t ch1, ch2;
                                 int8_t *src = (int8_t *)mValues->getEntry(i);
                                 int8_t *dst = src;
                                 <span class="hljs-keyword">while</span> ((ch1 = *src++)) {
                                        <span class="hljs-keyword">if</span> (ch1 & <span class="hljs-number">0x80</span>) {
                                            ch2 = *src++;
                                            ch1 = ((ch1 << <span class="hljs-number">6</span>) & <span class="hljs-number">0xC0</span>) | (ch2 & <span class="hljs-number">0x3F</span>);
                                        }

                                        *dst++ = ch1;
                                  }
                                  *dst = <span class="hljs-string">'\0'</span>;
                                <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">char</span> value[<span class="hljs-number">1024</span>];
                                <span class="hljs-built_in">memset</span>(value,<span class="hljs-number">0</span>,<span class="hljs-number">1024</span>);
                                <span class="hljs-keyword">if</span>( mLocaleEncoding == kEncodingWin1251)
                                    Win1251ToUtf8((<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">char</span> *)mValues->getEntry(i),<span class="hljs-built_in">strlen</span>(mValues->getEntry(i)),value,<span class="hljs-number">1024</span>);
                                <span class="hljs-keyword">else</span>
                                    Win1252ToUtf8((<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">char</span> *)mValues->getEntry(i),<span class="hljs-built_in">strlen</span>(mValues->getEntry(i)),value,<span class="hljs-number">1024</span>);
                                <span class="hljs-comment">//ALOGI("%s value do change encode type,the value is %s",mNames->getEntry(i),value);</span>
                                handleStringTag(name, (<span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *)value);
                            }
                            <span class="hljs-keyword">else</span>
                            {
                            <span class="hljs-comment">//if the id3 value can't discern the encoding,we can think it is a utf8</span>
                                handleStringTag(name, mValues->getEntry(i));
                            }
                            isUtf8[i] = <span class="hljs-number">0xff</span>; 
                            <span class="hljs-keyword">continue</span>;
                        }
                        <span class="hljs-comment">//LOGI("%s value is %x coding,the value is %s",mNames->getEntry(i),tmpEncoding,mValues->getEntry(i));</span>
                        encoding &= tmpEncoding;
                }


        <span class="hljs-keyword">if</span>(encoding != kEncodingAll)
        {
                    <span class="hljs-keyword">if</span>(encoding == kEncodingNone)
                        encoding = mLocaleEncoding;
                    <span class="hljs-comment">// if the locale encoding matches, then assume we have a native encoding.</span>
                    <span class="hljs-keyword">if</span> (encoding & mLocaleEncoding)
                        convertValues(mLocaleEncoding);
                    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(encoding & kEncodingEUCKR)
                        convertValues(kEncodingEUCKR);
                    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(encoding & kEncodingGBK)
                        convertValues(kEncodingGBK);
                    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(encoding & kEncodingBig5)
                        convertValues(kEncodingBig5);
                    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(encoding & kEncodingShiftJIS)
                        convertValues(kEncodingShiftJIS);
                }

        <span class="hljs-comment">// finally, push all name/value pairs to the client</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < mNames->size(); i++) 
        {
                        <span class="hljs-keyword">if</span>(isUtf8[i] == <span class="hljs-number">0xff</span>)
                            <span class="hljs-keyword">continue</span>;
            status_t status = handleStringTag(mNames->getEntry(i), mValues->getEntry(i));
            <span class="hljs-keyword">if</span> (status) {
                <span class="hljs-keyword">break</span>;
            }
        }

    }
    <span class="hljs-comment">// else addStringTag() has done all the work so we have nothing to do</span>

    <span class="hljs-keyword">delete</span> mNames;
    <span class="hljs-keyword">delete</span> mValues;
    mNames = NULL;
    mValues = NULL;
}</code>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值