公司負責給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來保存數據
查看一下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的關鍵。