2017-02-12 23:26:30 +08:00
|
|
|
package com.topjohnwu.magisk.database;
|
2017-01-24 14:19:28 +08:00
|
|
|
|
2017-06-01 03:18:41 +08:00
|
|
|
import android.content.ContentValues;
|
2017-01-24 14:19:28 +08:00
|
|
|
import android.content.Context;
|
|
|
|
import android.content.pm.PackageManager;
|
|
|
|
import android.database.Cursor;
|
2017-07-24 00:34:34 +08:00
|
|
|
import android.database.DatabaseUtils;
|
2017-01-24 14:19:28 +08:00
|
|
|
import android.database.sqlite.SQLiteDatabase;
|
|
|
|
import android.database.sqlite.SQLiteOpenHelper;
|
2017-10-20 00:41:44 +08:00
|
|
|
import android.os.Build;
|
2017-07-24 00:34:34 +08:00
|
|
|
import android.text.TextUtils;
|
2017-01-24 14:19:28 +08:00
|
|
|
|
2017-06-01 00:19:52 +08:00
|
|
|
import com.topjohnwu.magisk.MagiskManager;
|
2017-09-30 03:04:23 +08:00
|
|
|
import com.topjohnwu.magisk.container.Policy;
|
|
|
|
import com.topjohnwu.magisk.container.SuLogEntry;
|
2017-10-20 00:41:44 +08:00
|
|
|
import com.topjohnwu.magisk.utils.Shell;
|
2017-06-01 00:19:52 +08:00
|
|
|
import com.topjohnwu.magisk.utils.Utils;
|
2017-02-12 23:26:30 +08:00
|
|
|
|
2017-06-01 00:19:52 +08:00
|
|
|
import java.io.File;
|
2017-07-24 00:34:34 +08:00
|
|
|
import java.text.DateFormat;
|
2017-01-24 14:19:28 +08:00
|
|
|
import java.util.ArrayList;
|
2017-06-01 00:19:52 +08:00
|
|
|
import java.util.Collections;
|
2017-07-24 00:34:34 +08:00
|
|
|
import java.util.Date;
|
2017-01-24 14:19:28 +08:00
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
public class SuDatabaseHelper extends SQLiteOpenHelper {
|
|
|
|
|
2017-06-01 03:18:41 +08:00
|
|
|
public static final String ROOT_ACCESS = "root_access";
|
2017-08-29 01:34:42 +08:00
|
|
|
public static final int ROOT_ACCESS_DISABLED = 0;
|
|
|
|
public static final int ROOT_ACCESS_APPS_ONLY = 1;
|
|
|
|
public static final int ROOT_ACCESS_ADB_ONLY = 2;
|
|
|
|
public static final int ROOT_ACCESS_APPS_AND_ADB = 3;
|
|
|
|
|
2017-06-01 03:18:41 +08:00
|
|
|
public static final String MULTIUSER_MODE = "multiuser_mode";
|
2017-08-29 01:34:42 +08:00
|
|
|
public static final int MULTIUSER_MODE_OWNER_ONLY = 0;
|
|
|
|
public static final int MULTIUSER_MODE_OWNER_MANAGED = 1;
|
|
|
|
public static final int MULTIUSER_MODE_USER = 2;
|
|
|
|
|
2017-06-08 22:27:24 +08:00
|
|
|
public static final String MNT_NS = "mnt_ns";
|
2017-08-29 01:34:42 +08:00
|
|
|
public static final int NAMESPACE_MODE_GLOBAL = 0;
|
|
|
|
public static final int NAMESPACE_MODE_REQUESTER = 1;
|
|
|
|
public static final int NAMESPACE_MODE_ISOLATE = 2;
|
2017-06-01 03:18:41 +08:00
|
|
|
|
2017-09-15 18:03:25 +08:00
|
|
|
public static final String DB_NAME = "su.db";
|
2017-07-24 01:20:03 +08:00
|
|
|
private static final int DATABASE_VER = 3;
|
2017-06-01 00:19:52 +08:00
|
|
|
private static final String POLICY_TABLE = "policies";
|
|
|
|
private static final String LOG_TABLE = "logs";
|
|
|
|
private static final String SETTINGS_TABLE = "settings";
|
|
|
|
|
2017-10-20 00:41:44 +08:00
|
|
|
private static String GLOBAL_DB;
|
|
|
|
|
|
|
|
private Context mContext;
|
2017-06-01 00:19:52 +08:00
|
|
|
private PackageManager pm;
|
2017-07-21 00:46:13 +08:00
|
|
|
private SQLiteDatabase mDb;
|
2017-01-24 14:19:28 +08:00
|
|
|
|
2017-10-20 00:41:44 +08:00
|
|
|
private static Context preProcess() {
|
|
|
|
Context context;
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
|
|
Context ce = MagiskManager.get();
|
|
|
|
context = ce.createDeviceProtectedStorageContext();
|
|
|
|
File oldDB = Utils.getDatabasePath(ce, DB_NAME);
|
|
|
|
if (oldDB.exists()) {
|
|
|
|
// Migrate DB path
|
|
|
|
context.moveDatabaseFrom(ce, DB_NAME);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
context = MagiskManager.get();
|
|
|
|
}
|
|
|
|
GLOBAL_DB = context.getFilesDir().getParentFile().getParent() + "/magisk.db";
|
|
|
|
File db = Utils.getDatabasePath(context, DB_NAME);
|
|
|
|
if (!db.exists() && Utils.itemExist(GLOBAL_DB)) {
|
|
|
|
// Migrate global DB to ours
|
|
|
|
db.getParentFile().mkdirs();
|
|
|
|
Shell.su(
|
|
|
|
"magisk --clone-attr " + context.getFilesDir() + " " + GLOBAL_DB,
|
|
|
|
"chmod 660 " + GLOBAL_DB,
|
|
|
|
"ln " + GLOBAL_DB + " " + db
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return context;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void setupSuDB() {
|
|
|
|
MagiskManager mm = MagiskManager.get();
|
|
|
|
// Check if we need to migrate suDB
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && mm.magiskVersionCode >= 1410 &&
|
|
|
|
Utils.getDatabasePath(mm, SuDatabaseHelper.DB_NAME).exists()) {
|
|
|
|
mm.suDB.close();
|
|
|
|
mm.suDB = new SuDatabaseHelper();
|
|
|
|
}
|
|
|
|
|
|
|
|
File suDbFile = mm.suDB.getDbFile();
|
|
|
|
|
|
|
|
if (!Utils.itemExist(GLOBAL_DB)) {
|
|
|
|
// Hard link our DB globally
|
|
|
|
Shell.su_raw("ln " + suDbFile + " " + GLOBAL_DB);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we are linked globally
|
|
|
|
List<String> ret = Shell.sh("ls -l " + suDbFile);
|
|
|
|
if (Utils.isValidShellResponse(ret)) {
|
|
|
|
int links = Integer.parseInt(ret.get(0).trim().split("\\s+")[1]);
|
|
|
|
if (links < 2) {
|
|
|
|
mm.suDB.close();
|
|
|
|
suDbFile.delete();
|
|
|
|
new File(suDbFile + "-journal").delete();
|
|
|
|
mm.suDB = new SuDatabaseHelper();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public SuDatabaseHelper() {
|
|
|
|
this(preProcess());
|
|
|
|
}
|
|
|
|
|
2017-01-24 14:19:28 +08:00
|
|
|
public SuDatabaseHelper(Context context) {
|
2017-09-15 18:03:25 +08:00
|
|
|
super(context, DB_NAME, null, DATABASE_VER);
|
2017-10-20 00:41:44 +08:00
|
|
|
mContext = context;
|
2017-06-01 00:19:52 +08:00
|
|
|
pm = context.getPackageManager();
|
2017-07-21 00:46:13 +08:00
|
|
|
mDb = getWritableDatabase();
|
2017-06-20 18:25:18 +08:00
|
|
|
cleanup();
|
2017-01-24 14:19:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreate(SQLiteDatabase db) {
|
2017-02-21 03:30:37 +08:00
|
|
|
onUpgrade(db, 0, DATABASE_VER);
|
2017-01-24 14:19:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
2017-02-21 03:30:37 +08:00
|
|
|
if (oldVersion == 0) {
|
2017-06-01 00:19:52 +08:00
|
|
|
createTables(db);
|
|
|
|
oldVersion = 2;
|
|
|
|
}
|
|
|
|
if (oldVersion == 1) {
|
|
|
|
// We're dropping column app_name, rename and re-construct table
|
|
|
|
db.execSQL("ALTER TABLE " + POLICY_TABLE + " RENAME TO " + POLICY_TABLE + "_old");
|
|
|
|
|
|
|
|
// Create the new tables
|
|
|
|
createTables(db);
|
|
|
|
|
|
|
|
// Migrate old data to new tables
|
2017-02-21 03:30:37 +08:00
|
|
|
db.execSQL(
|
2017-06-01 00:19:52 +08:00
|
|
|
"INSERT INTO " + POLICY_TABLE + " SELECT " +
|
|
|
|
"uid, package_name, policy, until, logging, notification " +
|
|
|
|
"FROM " + POLICY_TABLE + "_old");
|
|
|
|
db.execSQL("DROP TABLE " + POLICY_TABLE + "_old");
|
|
|
|
|
2017-10-20 00:41:44 +08:00
|
|
|
File oldDB = Utils.getDatabasePath(MagiskManager.get(), "sulog.db");
|
2017-06-01 00:19:52 +08:00
|
|
|
if (oldDB.exists()) {
|
|
|
|
migrateLegacyLogList(oldDB, db);
|
2017-10-20 00:41:44 +08:00
|
|
|
MagiskManager.get().deleteDatabase("sulog.db");
|
2017-06-01 00:19:52 +08:00
|
|
|
}
|
|
|
|
++oldVersion;
|
2017-02-21 03:30:37 +08:00
|
|
|
}
|
2017-07-24 01:20:03 +08:00
|
|
|
if (oldVersion == 2) {
|
|
|
|
db.execSQL("UPDATE " + LOG_TABLE + " SET time=time*1000");
|
|
|
|
++oldVersion;
|
|
|
|
}
|
2017-10-20 00:41:44 +08:00
|
|
|
|
|
|
|
if (!Utils.itemExist(GLOBAL_DB)) {
|
|
|
|
// Hard link our DB globally
|
|
|
|
Shell.su_raw("ln " + getDbFile() + " " + GLOBAL_DB);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public File getDbFile() {
|
|
|
|
return mContext.getDatabasePath(DB_NAME);
|
2017-01-24 14:19:28 +08:00
|
|
|
}
|
|
|
|
|
2017-06-01 00:19:52 +08:00
|
|
|
private void createTables(SQLiteDatabase db) {
|
|
|
|
// Policies
|
|
|
|
db.execSQL(
|
|
|
|
"CREATE TABLE IF NOT EXISTS " + POLICY_TABLE + " " +
|
|
|
|
"(uid INT, package_name TEXT, policy INT, " +
|
|
|
|
"until INT, logging INT, notification INT, " +
|
|
|
|
"PRIMARY KEY(uid))");
|
|
|
|
|
|
|
|
// Logs
|
|
|
|
db.execSQL(
|
|
|
|
"CREATE TABLE IF NOT EXISTS " + LOG_TABLE + " " +
|
|
|
|
"(from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, " +
|
|
|
|
"to_uid INT, action INT, time INT, command TEXT)");
|
|
|
|
|
|
|
|
// Settings
|
|
|
|
db.execSQL(
|
|
|
|
"CREATE TABLE IF NOT EXISTS " + SETTINGS_TABLE + " " +
|
|
|
|
"(key TEXT, value INT, PRIMARY KEY(key))");
|
|
|
|
}
|
|
|
|
|
2017-06-20 18:25:18 +08:00
|
|
|
private void cleanup() {
|
|
|
|
// Clear outdated policies
|
2017-07-21 00:46:13 +08:00
|
|
|
mDb.delete(POLICY_TABLE, "until > 0 AND until < ?",
|
2017-07-03 08:01:15 +02:00
|
|
|
new String[] { String.valueOf(System.currentTimeMillis() / 1000) });
|
2017-06-20 18:25:18 +08:00
|
|
|
// Clear outdated logs
|
2017-07-21 00:46:13 +08:00
|
|
|
mDb.delete(LOG_TABLE, "time < ?", new String[] { String.valueOf(
|
2017-10-20 00:41:44 +08:00
|
|
|
System.currentTimeMillis() - MagiskManager.get().suLogTimeout * 86400000) });
|
2017-06-20 18:25:18 +08:00
|
|
|
}
|
|
|
|
|
2017-06-01 00:19:52 +08:00
|
|
|
public void deletePolicy(Policy policy) {
|
|
|
|
deletePolicy(policy.packageName);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void deletePolicy(String pkg) {
|
2017-07-21 00:46:13 +08:00
|
|
|
mDb.delete(POLICY_TABLE, "package_name=?", new String[] { pkg });
|
2017-06-01 00:19:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public void deletePolicy(int uid) {
|
2017-07-21 00:46:13 +08:00
|
|
|
mDb.delete(POLICY_TABLE, "uid=?", new String[]{String.valueOf(uid)});
|
2017-06-20 18:25:18 +08:00
|
|
|
}
|
|
|
|
|
2017-01-24 14:19:28 +08:00
|
|
|
public Policy getPolicy(int uid) {
|
|
|
|
Policy policy = null;
|
2017-07-21 00:46:13 +08:00
|
|
|
try (Cursor c = mDb.query(POLICY_TABLE, null, "uid=?", new String[] { String.valueOf(uid) }, null, null, null)) {
|
2017-02-21 03:30:37 +08:00
|
|
|
if (c.moveToNext()) {
|
2017-06-01 00:19:52 +08:00
|
|
|
policy = new Policy(c, pm);
|
2017-02-21 03:30:37 +08:00
|
|
|
}
|
2017-06-01 00:19:52 +08:00
|
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
|
|
deletePolicy(uid);
|
|
|
|
return null;
|
2017-01-24 14:19:28 +08:00
|
|
|
}
|
|
|
|
return policy;
|
|
|
|
}
|
|
|
|
|
2017-05-31 16:31:33 +08:00
|
|
|
public Policy getPolicy(String pkg) {
|
|
|
|
Policy policy = null;
|
2017-07-21 00:46:13 +08:00
|
|
|
try (Cursor c = mDb.query(POLICY_TABLE, null, "package_name=?", new String[] { pkg }, null, null, null)) {
|
2017-05-31 16:31:33 +08:00
|
|
|
if (c.moveToNext()) {
|
2017-06-01 00:19:52 +08:00
|
|
|
policy = new Policy(c, pm);
|
2017-05-31 16:31:33 +08:00
|
|
|
}
|
2017-06-01 00:19:52 +08:00
|
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
|
|
deletePolicy(pkg);
|
|
|
|
return null;
|
2017-05-31 16:31:33 +08:00
|
|
|
}
|
|
|
|
return policy;
|
|
|
|
}
|
|
|
|
|
2017-01-24 14:19:28 +08:00
|
|
|
public void addPolicy(Policy policy) {
|
2017-07-21 00:46:13 +08:00
|
|
|
mDb.replace(POLICY_TABLE, null, policy.getContentValues());
|
2017-01-24 14:19:28 +08:00
|
|
|
}
|
|
|
|
|
2017-05-31 16:31:33 +08:00
|
|
|
public void updatePolicy(Policy policy) {
|
2017-07-21 00:46:13 +08:00
|
|
|
mDb.update(POLICY_TABLE, policy.getContentValues(), "package_name=?",
|
2017-05-31 16:31:33 +08:00
|
|
|
new String[] { policy.packageName });
|
|
|
|
}
|
|
|
|
|
2017-01-24 14:19:28 +08:00
|
|
|
public List<Policy> getPolicyList(PackageManager pm) {
|
2017-07-21 00:46:13 +08:00
|
|
|
try (Cursor c = mDb.query(POLICY_TABLE, null, null, null, null, null, null)) {
|
|
|
|
List<Policy> ret = new ArrayList<>(c.getCount());
|
2017-01-24 14:19:28 +08:00
|
|
|
while (c.moveToNext()) {
|
2017-06-20 17:56:47 +08:00
|
|
|
try {
|
2017-07-21 00:46:13 +08:00
|
|
|
Policy policy = new Policy(c, pm);
|
2017-06-20 17:56:47 +08:00
|
|
|
// The application changed UID for some reason, check user config
|
|
|
|
if (policy.info.uid != policy.uid) {
|
2017-10-20 00:41:44 +08:00
|
|
|
if (MagiskManager.get().suReauth) {
|
2017-06-20 17:56:47 +08:00
|
|
|
// Reauth required, remove from DB
|
2017-07-21 00:46:13 +08:00
|
|
|
deletePolicy(policy);
|
2017-06-20 17:56:47 +08:00
|
|
|
continue;
|
|
|
|
} else {
|
2017-06-20 18:25:18 +08:00
|
|
|
// No reauth, update to use the new UID
|
2017-06-20 17:56:47 +08:00
|
|
|
policy.uid = policy.info.uid;
|
2017-07-21 00:46:13 +08:00
|
|
|
updatePolicy(policy);
|
2017-06-20 17:56:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ret.add(policy);
|
|
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
|
|
// The app no longer exist, remove from DB
|
2017-07-21 00:46:13 +08:00
|
|
|
deletePolicy(c.getInt(c.getColumnIndex("uid")));
|
2017-06-20 17:56:47 +08:00
|
|
|
}
|
2017-01-24 14:19:28 +08:00
|
|
|
}
|
2017-07-21 00:46:13 +08:00
|
|
|
Collections.sort(ret);
|
|
|
|
return ret;
|
2017-06-01 00:19:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-24 00:34:34 +08:00
|
|
|
public List<List<Integer>> getLogStructure() {
|
|
|
|
try (Cursor c = mDb.query(LOG_TABLE, new String[] { "time" }, null, null, null, null, "time DESC")) {
|
|
|
|
List<List<Integer>> ret = new ArrayList<>();
|
|
|
|
List<Integer> list = null;
|
|
|
|
String dateString = null, newString;
|
2017-06-01 00:19:52 +08:00
|
|
|
while (c.moveToNext()) {
|
2017-07-24 01:20:03 +08:00
|
|
|
Date date = new Date(c.getLong(c.getColumnIndex("time")));
|
2017-07-24 00:34:34 +08:00
|
|
|
newString = DateFormat.getDateInstance(DateFormat.MEDIUM, MagiskManager.locale).format(date);
|
|
|
|
if (!TextUtils.equals(dateString, newString)) {
|
|
|
|
dateString = newString;
|
|
|
|
list = new ArrayList<>();
|
|
|
|
ret.add(list);
|
|
|
|
}
|
|
|
|
list.add(c.getPosition());
|
2017-06-01 00:19:52 +08:00
|
|
|
}
|
2017-07-21 00:46:13 +08:00
|
|
|
return ret;
|
2017-01-24 14:19:28 +08:00
|
|
|
}
|
|
|
|
}
|
2017-06-01 00:19:52 +08:00
|
|
|
|
2017-07-24 00:34:34 +08:00
|
|
|
public Cursor getLogCursor() {
|
|
|
|
return getLogCursor(mDb);
|
2017-06-01 00:19:52 +08:00
|
|
|
}
|
|
|
|
|
2017-07-24 00:34:34 +08:00
|
|
|
public Cursor getLogCursor(SQLiteDatabase db) {
|
|
|
|
return db.query(LOG_TABLE, null, null, null, null, null, "time DESC");
|
2017-06-01 00:19:52 +08:00
|
|
|
}
|
|
|
|
|
2017-07-24 00:34:34 +08:00
|
|
|
private void migrateLegacyLogList(File oldDB, SQLiteDatabase newDB) {
|
|
|
|
try (SQLiteDatabase oldDb = SQLiteDatabase.openDatabase(oldDB.getPath(), null, SQLiteDatabase.OPEN_READWRITE);
|
|
|
|
Cursor c = getLogCursor(oldDb)) {
|
|
|
|
while (c.moveToNext()) {
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
DatabaseUtils.cursorRowToContentValues(c, values);
|
|
|
|
newDB.insert(LOG_TABLE, null, values);
|
|
|
|
}
|
|
|
|
}
|
2017-06-01 00:19:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public void addLog(SuLogEntry log) {
|
2017-07-21 00:46:13 +08:00
|
|
|
mDb.insert(LOG_TABLE, null, log.getContentValues());
|
2017-06-01 00:19:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public void clearLogs() {
|
2017-07-21 00:46:13 +08:00
|
|
|
mDb.delete(LOG_TABLE, null, null);
|
2017-06-01 00:19:52 +08:00
|
|
|
}
|
2017-06-01 03:18:41 +08:00
|
|
|
|
|
|
|
public void setSettings(String key, int value) {
|
|
|
|
ContentValues data = new ContentValues();
|
|
|
|
data.put("key", key);
|
|
|
|
data.put("value", value);
|
2017-07-21 00:46:13 +08:00
|
|
|
mDb.replace(SETTINGS_TABLE, null, data);
|
2017-06-01 03:18:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public int getSettings(String key, int defaultValue) {
|
|
|
|
int value = defaultValue;
|
2017-07-21 00:46:13 +08:00
|
|
|
try (Cursor c = mDb.query(SETTINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {
|
|
|
|
if (c.moveToNext()) {
|
2017-06-01 03:18:41 +08:00
|
|
|
value = c.getInt(c.getColumnIndex("value"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
2017-01-24 14:19:28 +08:00
|
|
|
}
|