前言
最近在做一个实验,从Framework层去调用应用层的数据库,可以开启服务我们开发的App数据库进行一些管理,下面来讲解下我的开发流程。
准备工作
首先,我们准备一个数据库项目,主要就是基础的CRUD操作,我这里准备了一个SqliteDemo可以去下载,下面的内容我使用了项目中的java文件。
修改Framework层代码
我们准备Hook的点是在Application的onCreate()方法进行Hook,预想只要创建进程的时候就打印数据库的信息。
修改的文件路径:frameworks/base/core/java/android/app/Application.java
...
import android.util.Slog;
import android.app.DBHelper;
public class Application{
private static final String PACKAGE_NAME = "com.martin.jetmo";
private static final String TAG = "JetmoApplication";
@CallSuper
public void onCreate() {
if (mLoadedApk.getPackageName().toLowerCase().equals(PACKAGE_NAME)) {
mDBHelper = new DBHelper(this);
ArrayList<String> array_list = mDBHelper.getAllCotacts();//这里需要写String
for (String item : array_list) {
Slog.i(TAG, item);
}
}
}
...
}
接下来,我们将前面项目的DBHelper.java
文件拷贝到和Application的同级目录下。
放入文件的路径:frameworks/base/core/java/android/app/DBHelper.java
package android.app;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.HashMap;
public class DBHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "MyDBName.db";
public static final String CONTACTS_TABLE_NAME = "contacts";
public static final String CONTACTS_COLUMN_ID = "id";
public static final String CONTACTS_COLUMN_NAME = "name";
public static final String CONTACTS_COLUMN_EMAIL = "email";
public static final String CONTACTS_COLUMN_STREET = "street";
public static final String CONTACTS_COLUMN_CITY = "place";
public static final String CONTACTS_COLUMN_PHONE = "phone";
private HashMap hp;
public DBHelper(Context context) {
super(context, DATABASE_NAME , null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(
"create table contacts " +
"(id integer primary key, name text,phone text,email text, street text,place text)"
);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.execSQL("DROP TABLE IF EXISTS contacts");
onCreate(db);
}
public boolean insertContact (String name, String phone, String email, String street,String place) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("name", name);
contentValues.put("phone", phone);
contentValues.put("email", email);
contentValues.put("street", street);
contentValues.put("place", place);
db.insert("contacts", null, contentValues);
return true;
}
public Cursor getData(int id) {
SQLiteDatabase db = this.getReadableDatabase();
Cursor res = db.rawQuery( "select * from contacts where id="+id+"", null );
return res;
}
public int numberOfRows(){
SQLiteDatabase db = this.getReadableDatabase();
int numRows = (int) DatabaseUtils.queryNumEntries(db, CONTACTS_TABLE_NAME);
return numRows;
}
public boolean updateContact (Integer id, String name, String phone, String email, String street,String place) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("name", name);
contentValues.put("phone", phone);
contentValues.put("email", email);
contentValues.put("street", street);
contentValues.put("place", place);
db.update("contacts", contentValues, "id = ? ", new String[] { Integer.toString(id) } );
return true;
}
public Integer deleteContact (Integer id) {
SQLiteDatabase db = this.getWritableDatabase();
return db.delete("contacts",
"id = ? ",
new String[] { Integer.toString(id) });
}
public ArrayList<String> getAllCotacts() {
ArrayList<String> array_list = new ArrayList<String>();
//hp = new HashMap();
SQLiteDatabase db = this.getReadableDatabase();
Cursor res = db.rawQuery( "select * from contacts", null );
res.moveToFirst();
while(res.isAfterLast() == false){
array_list.add(res.getString(res.getColumnIndex(CONTACTS_COLUMN_NAME)));
res.moveToNext();
}
return array_list;
}
}
注意,我们的java文件第一行需要修改为package android.app;
;做好了准备工作接下来就是编译我们的源码。在我们修改好以后,这里有一个小技巧,使用git命令去检查我们的内容。
git命令
- git log:查看提交的记录
- git diff : 代表修改后的差异命令,这里使用键盘的
J
按键进行向下翻页。
上面显示的是我们刚才添加的java文件。
编译Framework模块
make命令的作用
在我们每次修改了源码后,我们都需要将命令make -j8 update-api
,下面是命令的解释:
更多的解释可以看这篇文章《理解 Android Build 系统》。
执行编译命令
在这里,我们需要执行编译命令
source build/envsetup.sh
lunch mk_rolex-userdebug
make -j8 framework
- 这里的
-j8
代表的是我们CPU的核数,这里的数字一般是核数的两倍。 framework
代表的是我们需要编译的模块,指定为framwork模块。
拷贝文件到手机中
当我们执行了编译完成以后,我们需要将<源码目录>/out/target/product/rolex/system/framework
路径下的文件夹arm64
和framework.jar
文件拷贝到我们的手机上。
adb remount //挂载数据库
adb push arm64 /system/framework //arm64文件夹
adb push framework.jar /system/framework //framework.jar文件推送到制定目录
adb reboot //重启手机
验证结果
接下来,我们通过logcat去验证下我们的结果adb logcat -s [tag]
。
adb logcat -s JetmoApplication
到这里我们成功的从framework层的东西去调用应用的数据库了。
小结
这篇文章我们验证了,可以从framework层去调用应用层App的数据库,需要注意的几个点。
- 查找关键的Hook点,这里我们选择了Application的onCreate()方法。
- 添加Java文件时,需要注意文件中需要导入的类库是否存在。
- 将编译的文件,推送到手机的指定目录下。
总体来说文章内容还是比较简单,细心就可以完成了。
参考链接
https://developer.android.com/training/data-storage/sqlite#java