MediaProvider下的bug分析

公司負責給sony手機系統解bug,而sony手機複用了MTK系統,MTK系統相比於原來的android原生系統有很大的改動,在patch的整合過程完成後,進入測試階段會有很多bug,這就需要下載sony的代碼,在linux下編譯進行debug,那麼現在我就來描述這個bug的解決路程。

Step1bug描述

General description:
Found phone can’t edit Genre when in “Edit music info ” edit all info


Reproducibility:
10/10


Precondition:
1.Music have songs


Step:
1.Main Menu->Music->tap “Songs”->long Songs->Edit musicinfo->edit all info
2.Cheak “Edit music info” display


Actual result:
Found phone can’t edit Genre when in “Edit music info ” edit all info


Expect result:
phone can edit Genre when in “Edit music info ” edit all info

Step2 bug分析

MusicApp->MediaProvider失敗???
1、執行pull命令,將手機中的db數據導出到電腦。

adb root
pause 
adb remount
pause
adb pull /data/data/com.android.providers.media/databases/external.db D:/protable/databasetest/mediaprovider
pause

使用數據庫查看工具,可以發現external.db下的table和view。我們關注於view 的audio,和table的audio_genres。
這裏寫圖片描述
這裏寫圖片描述
數據時存在的,同時知道了流派時一個單獨存在的表,排除了這種情況。z注意:立刻插入的數據導出到文件不一定會存在,但是一定是插入成功了的因爲db.insert(XXX)成功的返回了插入記錄的rowid。android SQLite數據庫有這個特點,重啓一下會出現。

MediaProvider–>MusicApp顯示時失敗,這個需要看一下code,

  ......
   @Override
    public Cursor query(Uri uri, String[] projectionIn, String selection,
            String[] selectionArgs, String sort) {
     ......
     switch
     .....
       case AUDIO_GENRES:
                /**M: Added for SD hot-plug opimization.@{**/
                if (projectionIn == null) {
                    projectionIn = new String[2];
                    projectionIn[0] = "audio_genres._id AS _id";
                    projectionIn[1] = "audio_genres.name AS name";
                } else {
                    for (int i=0; i<projectionIn.length; i++) {
                        if (projectionIn[i].equalsIgnoreCase("_id")) {
                            projectionIn[i] = "audio_genres._id AS _id";
                        } else if (projectionIn[i].equalsIgnoreCase("name")) {
                            projectionIn[i] = "audio_genres.name AS name";
                        } else {
                            // Do nothing
                        }
                    }
                }
                qb.setTables("audio_genres CROSS JOIN audio_genres_map "
                        + "ON audio_genres._id=audio_genres_map.genre_id"
                        + " CROSS JOIN audio on audio_id=audio._id");
                if(!searchFilter.isEmpty()){
                    qb.appendWhere(searchFilter);
                }
                groupBy = "audio_genres._id";
     .....
       Cursor c = null;
        try {
            c = qb.query(db, projectionIn, selection,
                    combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
        } catch (IllegalStateException e) {
            MtkLog.e(TAG, "query: IllegalStateException! uri=" + uri, e);
        }
     ......
}
.....

當MusicApp調用query方法時,首先會通過URI_MATCHER.match(uri);解析uri,根據uri知道是哪個表。下面的switch語言根據所屬哪一個表,執行對應的內容。switch語句主要負責對projection(哪幾個列),selection(字段等於多少),selectionArgs(字段的值),分類,調用者的id的賦值。SQLiteQueryBuilder.java是一個幫助生成sql語句的幫助類,下面有文檔鏈接。最後會用cursor來保存數據

http://www.android-doc.com/reference/android/database/sqlite/SQLiteQueryBuilder.html#query(android.database.sqlite.SQLiteDatabase,%20java.lang.String[],%20java.lang.String,%20java.lang.String[],%20java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String)

查看一下mtklogquery: uri = content://media/external/audio/genres, projection = [audio_genres._id AS _id, audio_genres.name AS name], selection = name =?, selectionArgs = [hhhh], sort = null, caller pid = 6052
可以更加好的領悟到SQLiteQueryBuilder.java的使用。

嗯,回到bug下,這個查詢語句後面的參數是沒有問題的。問題出在setTables時做了個表和表的關聯qb.setTables(“audio_genres CROSS JOIN audio_genres_map ”
+ “ON audio_genres._id=audio_genres_map.genre_id”
+ ” CROSS JOIN audio on audio_id=audio._id”);。audio_genres_map這個表是沒有數據的,但是卻關聯在一起查詢,最後的結果肯定是沒有數據,所以,把這個地方改成qb.setTables(“audio_genres”);同時下面的
if(!searchFilter.isEmpty()){
qb.appendWhere(searchFilter);
}
也需要去掉,因爲這裏相當於加一個where語句,searchFilter的實例化時調用

        String queryFilter = "";
        String strUri = getUriString(uri);
        if (strUri.isEmpty()) {
            return queryFilter;
        }

        String forceQuerying = uri.getQueryParameter("force");
        int startPos = 0;
        if (strUri.startsWith(EXTERNAL_MEDIA_URI_STRING)) {
            startPos = 1;
                }
        int size = mExternalVolumeIdList.size();
        if (size <= 0) {
            MtkLog.d(TAG, "generateStorageIdFilter() mExternalVolumeIdList is empty");
            }
        for (int i = startPos; i < size; i++) {
            queryFilter += "storage_id='" + mExternalVolumeIdList.get(i) + "'";
                if (isPreScanning()) {
                    if (forceQuerying == null || !forceQuerying.equals("1")) {
                        break;
                    }
                }
                if (size > 1 && i <size-1) {
                    queryFilter += " OR ";
                }
            }
        if (!queryFilter.equals("")) {
            queryFilter = "(" + queryFilter +")";
        }
        if (LOG_QUERY) {
        MtkLog.d(TAG, "generateStorageIdFilter() forceQuerying = " + forceQuerying
            + ", queryFilter = " + queryFilter);
        }
        return queryFilter;

因爲手機又手機自帶存儲和sd卡的存儲,audio表有個storageid字段,存儲着這個字,這裏由於移除掉了audio表,如果不去掉就會crash。

12-10 02:44:33.013 E/DatabaseUtils( 2449): android.database.sqlite.SQLiteException: no such column: storage_id (code 1): , while compiling: SELECT audio_genres._id AS _id FROM audio_genres WHERE ((storage_id=’65537’ OR storage_id=’-566231039’)) GROUP BY audio_genres._id
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:905)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:516)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.database.sqlite.SQLiteProgram.(SQLiteProgram.java:58)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.database.sqlite.SQLiteQuery.(SQLiteQuery.java:37)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:44)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1348)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:399)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:333)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at com.android.providers.media.MediaProvider.query(MediaProvider.java:3836)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.content.ContentProvider.query(ContentProvider.java:1038)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.content.ContentProvider$Transport.query(ContentProvider.java:247)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:112)
12-10 02:44:33.013 E/DatabaseUtils( 2449): at android.os.Binder.execTransact(Binder.java:570)
“`

Step3總結

時刻都要保持清醒的頭腦,絕對的信心,才能找到bug的關鍵。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章