From c2311ee47118b7305c8ec268ba085993df758b0b Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 19 Jan 2020 00:14:53 +1100 Subject: [PATCH 1/3] backup now includes user_de - the device encrypted app data --- src/dk/jens/backup/AppInfo.java | 11 +++++++++-- src/dk/jens/backup/AppInfoHelper.java | 6 ++++-- src/dk/jens/backup/AppInfoSpecial.java | 2 +- src/dk/jens/backup/BackupRestoreHelper.java | 2 +- src/dk/jens/backup/LogFile.java | 11 ++++++++++- src/dk/jens/backup/ShellCommands.java | 17 ++++++++++++++++- 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/dk/jens/backup/AppInfo.java b/src/dk/jens/backup/AppInfo.java index 88f0739451..8edffad20f 100644 --- a/src/dk/jens/backup/AppInfo.java +++ b/src/dk/jens/backup/AppInfo.java @@ -8,7 +8,7 @@ public class AppInfo implements Comparable, Parcelable { LogFile logInfo; - String label, packageName, versionName, sourceDir, dataDir; + String label, packageName, versionName, sourceDir, dataDir, deviceProtectedDataDir; int versionCode, backupMode; private boolean system, installed, checked, disabled; public Bitmap icon; @@ -17,7 +17,7 @@ public class AppInfo public static final int MODE_DATA = 2; public static final int MODE_BOTH = 3; - public AppInfo(String packageName, String label, String versionName, int versionCode, String sourceDir, String dataDir, boolean system, boolean installed) + public AppInfo(String packageName, String label, String versionName, int versionCode, String sourceDir, String dataDir, String deviceProtectedDataDir, boolean system, boolean installed) { this.label = label; this.packageName = packageName; @@ -25,6 +25,7 @@ public AppInfo(String packageName, String label, String versionName, int version this.versionCode = versionCode; this.sourceDir = sourceDir; this.dataDir = dataDir; + this.deviceProtectedDataDir = deviceProtectedDataDir; this.system = system; this.installed = installed; this.backupMode = MODE_UNSET; @@ -53,6 +54,10 @@ public String getDataDir() { return dataDir; } + + public String getDeviceProtectedDataDir() { + return deviceProtectedDataDir; + } public int getBackupMode() { return backupMode; @@ -128,6 +133,7 @@ public void writeToParcel(Parcel out, int flags) out.writeString(versionName); out.writeString(sourceDir); out.writeString(dataDir); + out.writeString(deviceProtectedDataDir); out.writeInt(versionCode); out.writeInt(backupMode); out.writeBooleanArray(new boolean[] {system, installed, checked}); @@ -152,6 +158,7 @@ protected AppInfo(Parcel in) versionName = in.readString(); sourceDir = in.readString(); dataDir = in.readString(); + deviceProtectedDataDir = in.readString(); versionCode = in.readInt(); backupMode = in.readInt(); boolean[] bools = new boolean[4]; diff --git a/src/dk/jens/backup/AppInfoHelper.java b/src/dk/jens/backup/AppInfoHelper.java index 4780f6910c..d69786087a 100644 --- a/src/dk/jens/backup/AppInfoHelper.java +++ b/src/dk/jens/backup/AppInfoHelper.java @@ -78,10 +78,12 @@ public static ArrayList getPackageInfo(Context context, File backupDir, // package at least on cm14 if(pinfo.packageName.equals("android") && dataDir == null) dataDir = "/data/system"; + //determine the directory where device-protected data will be stored for android N and above + String deviceProtectedDataDir = pinfo.applicationInfo.deviceProtectedDataDir; AppInfo appInfo = new AppInfo(pinfo.packageName, pinfo.applicationInfo.loadLabel(pm).toString(), pinfo.versionName, pinfo.versionCode, - pinfo.applicationInfo.sourceDir, dataDir, isSystem, + pinfo.applicationInfo.sourceDir, dataDir, deviceProtectedDataDir, isSystem, true); File subdir = new File(backupDir, pinfo.packageName); if(subdir.exists()) @@ -114,7 +116,7 @@ public static void addUninstalledBackups(File backupDir, ArrayList list LogFile logInfo = new LogFile(new File(backupDir.getAbsolutePath() + "/" + folder), folder); if(logInfo.getLastBackupMillis() > 0) { - AppInfo appInfo = new AppInfo(logInfo.getPackageName(), logInfo.getLabel(), logInfo.getVersionName(), logInfo.getVersionCode(), logInfo.getSourceDir(), logInfo.getDataDir(), logInfo.isSystem(), false); + AppInfo appInfo = new AppInfo(logInfo.getPackageName(), logInfo.getLabel(), logInfo.getVersionName(), logInfo.getVersionCode(), logInfo.getSourceDir(), logInfo.getDataDir(), logInfo.getDeviceProtectedDataDir(), logInfo.isSystem(), false); appInfo.setLogInfo(logInfo); list.add(appInfo); } diff --git a/src/dk/jens/backup/AppInfoSpecial.java b/src/dk/jens/backup/AppInfoSpecial.java index 8c2ed9ec50..73ca14f6ee 100644 --- a/src/dk/jens/backup/AppInfoSpecial.java +++ b/src/dk/jens/backup/AppInfoSpecial.java @@ -9,7 +9,7 @@ public class AppInfoSpecial extends AppInfo String[] files; public AppInfoSpecial(String packageName, String label, String versionName, int versionCode) { - super(packageName, label, versionName, versionCode, "", "", true, true); + super(packageName, label, versionName, versionCode, "", "", "", true, true); } public String[] getFilesList() { diff --git a/src/dk/jens/backup/BackupRestoreHelper.java b/src/dk/jens/backup/BackupRestoreHelper.java index ad1dc48ddd..9d968dc819 100644 --- a/src/dk/jens/backup/BackupRestoreHelper.java +++ b/src/dk/jens/backup/BackupRestoreHelper.java @@ -94,7 +94,7 @@ else if(!appInfo.isSpecial()) } else { - restoreRet = shellCommands.doRestore(context, backupSubDir, appInfo.getLabel(), appInfo.getPackageName(), appInfo.getLogInfo().getDataDir()); + restoreRet = shellCommands.doRestore(context, backupSubDir, appInfo.getLabel(), appInfo.getPackageName(), appInfo.getLogInfo().getDataDir(), appInfo.getLogInfo().getDeviceProtectedDataDir()); permRet = shellCommands.setPermissions(dataDir); } diff --git a/src/dk/jens/backup/LogFile.java b/src/dk/jens/backup/LogFile.java index dab6077c72..d749e859f1 100644 --- a/src/dk/jens/backup/LogFile.java +++ b/src/dk/jens/backup/LogFile.java @@ -18,7 +18,7 @@ public class LogFile implements Parcelable { final static String TAG = OAndBackup.TAG; - String label, packageName, versionName, sourceDir, dataDir; + String label, packageName, versionName, sourceDir, dataDir, deviceProtectedDataDir; int versionCode, backupMode; long lastBackupMillis; boolean encrypted, system; @@ -34,6 +34,11 @@ public LogFile(File backupSubDir, String packageName) this.versionName = jsonObject.getString("versionName"); this.sourceDir = jsonObject.getString("sourceDir"); this.dataDir = jsonObject.getString("dataDir"); + //check that the value exists in case backups have come from an old version of oandbackup + if(jsonObject.has("deviceProtectedDataDir")) + this.deviceProtectedDataDir = jsonObject.getString("deviceProtectedDataDir"); + else + deviceProtectedDataDir = null; this.lastBackupMillis = jsonObject.getLong("lastBackupMillis"); this.versionCode = jsonObject.getInt("versionCode"); this.encrypted = jsonObject.optBoolean("isEncrypted"); @@ -79,6 +84,9 @@ public String getDataDir() { return dataDir; } + public String getDeviceProtectedDataDir() { + return deviceProtectedDataDir; + } public long getLastBackupMillis() { return lastBackupMillis; @@ -120,6 +128,7 @@ public static void writeLogFile(File backupSubDir, AppInfo appInfo, int backupMo jsonObject.put("packageName", appInfo.getPackageName()); jsonObject.put("sourceDir", sourceDir); jsonObject.put("dataDir", appInfo.getDataDir()); + jsonObject.put("deviceProtectedDataDir", appInfo.getDeviceProtectedDataDir()); jsonObject.put("lastBackupMillis", System.currentTimeMillis()); jsonObject.put("isEncrypted", encrypted); jsonObject.put("isSystem", appInfo.isSystem()); diff --git a/src/dk/jens/backup/ShellCommands.java b/src/dk/jens/backup/ShellCommands.java index 30852f1f33..8119ed8300 100644 --- a/src/dk/jens/backup/ShellCommands.java +++ b/src/dk/jens/backup/ShellCommands.java @@ -3,6 +3,7 @@ import android.app.ActivityManager; import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.os.Build; import android.text.TextUtils; import android.util.Log; @@ -24,6 +25,7 @@ public class ShellCommands implements CommandHandler.UnexpectedExceptionListener { final static String TAG = OAndBackup.TAG; final static String EXTERNAL_FILES = "external_files"; + final static String DEVICE_PROTECTED_FILES = "device_protected_files"; SharedPreferences prefs; String busybox; ArrayList users; @@ -62,6 +64,7 @@ public void onUnexpectedException(Throwable t) { public int doBackup(Context context, File backupSubDir, String label, String packageData, String packageApk, int backupMode) { String backupSubDirPath = swapBackupDirPath(backupSubDir.getAbsolutePath()); + String deviceProtectedPackageData = appInfo.getDeviceProtectedDataDir(); Log.i(TAG, "backup: " + label); // since api 24 (android 7) ApplicationInfo.dataDir can be null // this doesn't seem to be documented. proper sanity checking is needed @@ -74,6 +77,7 @@ public int doBackup(Context context, File backupSubDir, String label, String pac // -L because fat (which will often be used to store the backup files) // doesn't support symlinks String followSymlinks = prefs.getBoolean("followSymlinks", true) ? "L" : ""; + File backupSubDirDeviceProtectedFiles = null; switch(backupMode) { case AppInfo.MODE_APK: @@ -81,9 +85,18 @@ public int doBackup(Context context, File backupSubDir, String label, String pac break; case AppInfo.MODE_DATA: commands.add("cp -R" + followSymlinks + " " + packageData + " " + backupSubDirPath); + + backupSubDirDeviceProtectedFiles = new File(backupSubDir, DEVICE_PROTECTED_FILES); + if(backupSubDirDeviceProtectedFiles.exists() || backupSubDirDeviceProtectedFiles.mkdir()) + commands.add("cp -R" + followSymlinks + " " + deviceProtectedPackageData + " " + swapBackupDirPath(backupSubDir.getAbsolutePath() + "/" + DEVICE_PROTECTED_FILES)); break; default: // defaults to MODE_BOTH commands.add("cp -R" + followSymlinks + " " + packageData + " " + backupSubDirPath); + + backupSubDirDeviceProtectedFiles = new File(backupSubDir, DEVICE_PROTECTED_FILES); + if(backupSubDirDeviceProtectedFiles.exists() || backupSubDirDeviceProtectedFiles.mkdir()) + commands.add("cp -R" + followSymlinks + " " + deviceProtectedPackageData + " " + swapBackupDirPath(backupSubDir.getAbsolutePath() + "/" + DEVICE_PROTECTED_FILES)); + commands.add("cp " + packageApk + " " + backupSubDirPath); break; } @@ -147,6 +160,8 @@ public int doBackup(Context context, File backupSubDir, String label, String pac int zipret = compress(new File(backupSubDir, folder)); if(backupSubDirExternalFiles != null) zipret += compress(new File(backupSubDirExternalFiles, packageData.substring(packageData.lastIndexOf("/") + 1))); + if(backupSubDirDeviceProtectedFiles != null) + zipret += compress(new File(backupSubDirDeviceProtectedFiles, packageData.substring(packageData.lastIndexOf("/") + 1))); if(zipret != 0) ret += zipret; } @@ -155,7 +170,7 @@ public int doBackup(Context context, File backupSubDir, String label, String pac Crypto.cleanUpEncryptedFiles(backupSubDir, packageApk, packageData, backupMode, prefs.getBoolean("backupExternalFiles", false)); return ret; } - public int doRestore(Context context, File backupSubDir, String label, String packageName, String dataDir) + public int doRestore(Context context, File backupSubDir, String label, String packageName, String dataDir, String deviceProtectedDataDir) { String backupSubDirPath = swapBackupDirPath(backupSubDir.getAbsolutePath()); String dataDirName = dataDir.substring(dataDir.lastIndexOf("/") + 1); From 281c4334b73821d1a5ace5b41f78b459d0641e68 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 19 Jan 2020 00:16:28 +1100 Subject: [PATCH 2/3] fixed issue with apk restore on android p and above --- src/dk/jens/backup/ShellCommands.java | 38 ++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/dk/jens/backup/ShellCommands.java b/src/dk/jens/backup/ShellCommands.java index 8119ed8300..1613008c7c 100644 --- a/src/dk/jens/backup/ShellCommands.java +++ b/src/dk/jens/backup/ShellCommands.java @@ -96,7 +96,7 @@ public int doBackup(Context context, File backupSubDir, String label, String pac backupSubDirDeviceProtectedFiles = new File(backupSubDir, DEVICE_PROTECTED_FILES); if(backupSubDirDeviceProtectedFiles.exists() || backupSubDirDeviceProtectedFiles.mkdir()) commands.add("cp -R" + followSymlinks + " " + deviceProtectedPackageData + " " + swapBackupDirPath(backupSubDir.getAbsolutePath() + "/" + DEVICE_PROTECTED_FILES)); - + commands.add("cp " + packageApk + " " + backupSubDirPath); break; } @@ -206,8 +206,37 @@ public int doRestore(Context context, File backupSubDir, String label, String pa // restored system apps will not necessarily have the data folder (which is otherwise handled by pm) } commands.add(restoreCommand); - if(Build.VERSION.SDK_INT >= 23) + + File deviceProtectedFiles = new File(backupSubDir, DEVICE_PROTECTED_FILES); + if(deviceProtectedDataDir != null && deviceProtectedFiles.exists()) + { + + Compression.unzip(new File(deviceProtectedFiles, dataDirName + ".zip"), deviceProtectedFiles); + restoreCommand = busybox + " cp -r " + deviceProtectedFiles + "/" + dataDirName + "/* " + deviceProtectedDataDir + "\n"; + + try { + PackageManager packageManager = context.getPackageManager(); + String user = String.valueOf(packageManager.getApplicationInfo(dataDirName, PackageManager.GET_META_DATA).uid); + restoreCommand = restoreCommand + " chown -R " + user + ":" + user + " " + deviceProtectedDataDir + "\n"; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + restoreCommand = restoreCommand + " chmod -R 777 " + deviceProtectedDataDir + "\n"; + if(!(new File(deviceProtectedDataDir).exists())) + { + restoreCommand = "mkdir " + deviceProtectedDataDir + "\n" + restoreCommand; + // restored system apps will not necessarily have the data folder (which is otherwise handled by pm) + } + commands.add(restoreCommand); + + } + + + if(Build.VERSION.SDK_INT >= 23) { commands.add("restorecon -R " + dataDir); + commands.add("restorecon -R " + deviceProtectedDataDir); + } int ret = CommandHandler.runCmd("su", commands, line -> {}, line -> writeErrorLog(label, line), e -> Log.e(TAG, "doRestore: " + e.toString()), this); @@ -229,6 +258,7 @@ public int doRestore(Context context, File backupSubDir, String label, String pa { deleteBackup(new File(backupSubDir, dataDirName)); } + deleteBackup(new File(new File(backupSubDir, DEVICE_PROTECTED_FILES), dataDirName)); } } public int backupSpecial(File backupSubDir, String label, String... files) @@ -426,10 +456,10 @@ public int restoreUserApk(File backupDir, String label, String apk, String ownDa commands.add(busybox + " cp " + swapBackupDirPath( backupDir.getAbsolutePath() + "/" + apk) + " " + swapBackupDirPath(tempPath)); - commands.add("pm install -r " + tempPath + "/" + apk); + commands.add("cat" + tempPath + "/" + apk + " | pm install -r -t -S " + new File(tempPath + "/" + apk).length()); commands.add(busybox + " rm -r " + swapBackupDirPath(tempPath)); } else { - commands.add("pm install -r " + backupDir.getAbsolutePath() + "/" + apk); + commands.add("cat " + backupDir.getAbsolutePath() + "/" + apk + " | pm install -r -t -S " + new File(backupDir.getAbsolutePath() + "/" + apk).length()); } List err = new ArrayList<>(); int ret = CommandHandler.runCmd("su", commands, line -> {}, From 7eadaf2157e9b680c1de6246831f5a6ff0b608cb Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 19 Jan 2020 11:05:02 +1100 Subject: [PATCH 3/3] bug fixes --- src/main/java/dk/jens/backup/BackupRestoreHelper.java | 2 +- src/main/java/dk/jens/backup/ShellCommands.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/dk/jens/backup/BackupRestoreHelper.java b/src/main/java/dk/jens/backup/BackupRestoreHelper.java index e3ace32711..1adb258c85 100644 --- a/src/main/java/dk/jens/backup/BackupRestoreHelper.java +++ b/src/main/java/dk/jens/backup/BackupRestoreHelper.java @@ -41,7 +41,7 @@ else if(backupMode != AppInfo.MODE_DATA && appInfo.getSourceDir().length() > 0) } else { - ret = shellCommands.doBackup(context, backupSubDir, appInfo.getLabel(), appInfo.getDataDir(), appInfo.getSourceDir(), backupMode); + ret = shellCommands.doBackup(context, backupSubDir, appInfo.getLabel(), appInfo.getDataDir(), appInfo.getDeviceProtectedDataDir(), appInfo.getSourceDir(), backupMode); appInfo.setBackupMode(backupMode); } diff --git a/src/main/java/dk/jens/backup/ShellCommands.java b/src/main/java/dk/jens/backup/ShellCommands.java index da764995f9..85183171f5 100644 --- a/src/main/java/dk/jens/backup/ShellCommands.java +++ b/src/main/java/dk/jens/backup/ShellCommands.java @@ -82,10 +82,9 @@ public void onUnexpectedException(Throwable t) { errors += t.toString(); } - public int doBackup(Context context, File backupSubDir, String label, String packageData, String packageApk, int backupMode) + public int doBackup(Context context, File backupSubDir, String label, String packageData, String deviceProtectedPackageData, String packageApk, int backupMode) { String backupSubDirPath = swapBackupDirPath(backupSubDir.getAbsolutePath()); - String deviceProtectedPackageData = appInfo.getDeviceProtectedDataDir(); Log.i(TAG, "backup: " + label); // since api 24 (android 7) ApplicationInfo.dataDir can be null // this doesn't seem to be documented. proper sanity checking is needed @@ -258,7 +257,7 @@ public int doRestore(Context context, File backupSubDir, String label, String pa commands.add("restorecon -R " + dataDir + " || true"); commands.add("restorecon -R " + deviceProtectedDataDir + " || true"); } - int ret = CommandHandler.runCmd("su", commands, line -> {}, + int ret = commandHandler.runCmd("su", commands, line -> {}, line -> writeErrorLog(label, line), e -> Log.e(TAG, "doRestore: " + e.toString()), this); if(multiuserEnabled) @@ -526,10 +525,11 @@ public int restoreUserApk(File backupDir, String label, String apk, String ownDa commands.add(busybox + " cp " + swapBackupDirPath( backupDir.getAbsolutePath() + "/" + apk) + " " + swapBackupDirPath(tempPath)); - commands.add("cat" + tempPath + "/" + apk + " | pm install -r -t -S " + new File(tempPath + "/" + apk).length()); + commands.add(String.format("%s -r %s/%s", installCmd, tempPath, apk)); commands.add(busybox + " rm -r " + swapBackupDirPath(tempPath)); } else { - commands.add("cat " + backupDir.getAbsolutePath() + "/" + apk + " | pm install -r -t -S " + new File(backupDir.getAbsolutePath() + "/" + apk).length()); + commands.add(String.format("%s -r %s/%s", installCmd, + backupDir.getAbsolutePath(), apk)); } List err = new ArrayList<>(); int ret = commandHandler.runCmd("su", commands, line -> {},