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的关键。

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