公司负责给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的关键。