Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Including Device Protected Data in Backup #267

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/main/java/dk/jens/backup/AppInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class AppInfo
implements Comparable<AppInfo>, 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;
Expand All @@ -17,14 +17,15 @@ 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;
this.versionName = versionName;
this.versionCode = versionCode;
this.sourceDir = sourceDir;
this.dataDir = dataDir;
this.deviceProtectedDataDir = deviceProtectedDataDir;
this.system = system;
this.installed = installed;
this.backupMode = MODE_UNSET;
Expand Down Expand Up @@ -53,6 +54,10 @@ public String getDataDir()
{
return dataDir;
}

public String getDeviceProtectedDataDir() {
return deviceProtectedDataDir;
}
public int getBackupMode()
{
return backupMode;
Expand Down Expand Up @@ -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});
Expand All @@ -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];
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/dk/jens/backup/AppInfoHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ public static ArrayList<AppInfo> getPackageInfo(Context context,
// 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())
Expand Down Expand Up @@ -115,7 +117,7 @@ public static void addUninstalledBackups(File backupDir, ArrayList<AppInfo> 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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/dk/jens/backup/AppInfoSpecial.java
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/dk/jens/backup/BackupRestoreHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
}
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/dk/jens/backup/LogFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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");
Expand Down Expand Up @@ -79,6 +84,9 @@ public String getDataDir()
{
return dataDir;
}
public String getDeviceProtectedDataDir() {
return deviceProtectedDataDir;
}
public long getLastBackupMillis()
{
return lastBackupMillis;
Expand Down Expand Up @@ -119,6 +127,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());
Expand Down
50 changes: 46 additions & 4 deletions src/main/java/dk/jens/backup/ShellCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,12 +27,12 @@ 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";

CommandHandler commandHandler = new CommandHandler();

private final String oabUtils;
private boolean legacyMode;

SharedPreferences prefs;
String busybox;
ArrayList<String> users;
Expand Down Expand Up @@ -81,7 +82,7 @@ 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());
Log.i(TAG, "backup: " + label);
Expand All @@ -96,16 +97,26 @@ 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:
commands.add("cp " + packageApk + " " + backupSubDirPath);
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;
}
Expand Down Expand Up @@ -169,6 +180,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;
}
Expand All @@ -177,7 +190,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);
Expand Down Expand Up @@ -213,8 +226,36 @@ 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);

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 + " || true");
commands.add("restorecon -R " + deviceProtectedDataDir + " || true");
}
int ret = commandHandler.runCmd("su", commands, line -> {},
line -> writeErrorLog(label, line),
Expand All @@ -237,6 +278,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)
Expand Down Expand Up @@ -487,7 +529,7 @@ public int restoreUserApk(File backupDir, String label, String apk, String ownDa
commands.add(busybox + " rm -r " + swapBackupDirPath(tempPath));
} else {
commands.add(String.format("%s -r %s/%s", installCmd,
backupDir.getAbsolutePath(), apk));
backupDir.getAbsolutePath(), apk));
}
List<String> err = new ArrayList<>();
int ret = commandHandler.runCmd("su", commands, line -> {},
Expand Down