package org.commcare.models.legacy;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Pair;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteException;
import org.commcare.CommCareApp;
import org.commcare.CommCareApplication;
import org.commcare.android.javarosa.AndroidLogEntry;
import org.commcare.logging.AndroidLogger;
import org.commcare.android.javarosa.DeviceReportRecord;
import org.commcare.models.database.AndroidDbHelper;
import org.commcare.models.database.SqlStorage;
import org.commcare.android.database.app.models.ResourceModelUpdater;
import org.commcare.android.database.app.models.UserKeyRecord;
import org.commcare.android.database.global.models.ApplicationRecord;
import org.commcare.models.database.user.DatabaseUserOpenHelper;
import org.commcare.models.database.user.UserSandboxUtils;
import org.commcare.android.database.user.models.ACase;
import org.commcare.android.database.user.models.FormRecord;
import org.commcare.android.database.user.models.GeocodeCacheModel;
import org.commcare.android.database.user.models.SessionStateDescriptor;
import org.commcare.models.encryption.CipherPool;
import org.commcare.modern.models.EncryptedModel;
import org.commcare.preferences.CommCarePreferences;
import org.commcare.provider.FormsProviderAPI;
import org.commcare.provider.InstanceProviderAPI.InstanceColumns;
import org.commcare.resources.model.Resource;
import org.commcare.util.CommCarePlatform;
import org.commcare.utils.GlobalConstants;
import org.javarosa.core.model.User;
import org.javarosa.core.model.instance.FormInstance;
import org.javarosa.core.services.Logger;
import org.javarosa.core.services.storage.Persistable;
import org.javarosa.core.util.PropertyUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
/**
* @author ctsims
*/
public class LegacyInstallUtils {
public static final String LEGACY_UPGRADE_PROGRESS = "legacy_upgrade_progress";
public static final String UPGRADE_COMPLETE = "complete";
public static void checkForLegacyInstall(Context c, SqlStorage<ApplicationRecord> currentAppStorage) {
SharedPreferences globalPreferences = PreferenceManager.getDefaultSharedPreferences(c);
if (globalPreferences.getString(LEGACY_UPGRADE_PROGRESS, "").equals(UPGRADE_COMPLETE)) {
return;
}
//Check to see if the legacy database exists on this system
if (!c.getDatabasePath(GlobalConstants.CC_DB_NAME).exists()) {
globalPreferences.edit().putString(LEGACY_UPGRADE_PROGRESS, UPGRADE_COMPLETE).commit();
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "No legacy installs detected. Skipping transition");
return;
}
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| DB Detected");
//There is a legacy DB. First, check whether we've already moved things over (whether a new
//app is already installed)
int installedApps = currentAppStorage.getNumRecords();
ApplicationRecord record = null;
if (installedApps == 0) {
//there are no installed apps
} else if (installedApps == 1) {
for (ApplicationRecord r : currentAppStorage) {
int status = r.getStatus();
if (status == ApplicationRecord.STATUS_SPECIAL_LEGACY) {
//We've already started this process, but it didn't
//finish
record = r;
}
}
//if the application present is either uninitialized or already
//installed, we don't need to proceed.
if (record == null) {
globalPreferences.edit().putString(LEGACY_UPGRADE_PROGRESS, UPGRADE_COMPLETE).commit();
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| app was detected, but new install already covers it");
return;
}
} else {
//there's more than one app installed, which means we
//must have passed through here.
globalPreferences.edit().putString(LEGACY_UPGRADE_PROGRESS, UPGRADE_COMPLETE).commit();
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| More than one app record installed, skipping legacy app detection");
return;
}
//Ok, so now we have an old db, and either an incomplete app install, or
//no app install.
//Next step, determine if there was an old, completely installed app in the old db.
//If this isn't true, we can just bail.
//get the legacy storage
final android.database.sqlite.SQLiteDatabase olddb = new LegacyCommCareOpenHelper(c).getReadableDatabase();
LegacyDbHelper ldbh = new LegacyDbHelper(c) {
@Override
public android.database.sqlite.SQLiteDatabase getHandle() {
return olddb;
}
};
//Ok, so now we need to see whether there's app on the legacy db.
//Use the name of the Pre-DB3 Global Resource table (in case it has changed).
//Note: ResourceModelUpdater is mandatory here to read pre-db3 resource records.
LegacySqlIndexedStorageUtility<Resource> legacyResources = new LegacySqlIndexedStorageUtility<Resource>("GLOBAL_RESOURCE_TABLE", ResourceModelUpdater.class, ldbh);
//see if the resource table is installed
boolean hasProfile = false;
boolean allInstalled = true;
int oldDbSize = 0;
for (Resource r : legacyResources) {
if (r.getStatus() != Resource.RESOURCE_STATUS_INSTALLED) {
allInstalled = false;
}
if (r.getResourceId().equals(CommCarePlatform.APP_PROFILE_RESOURCE_ID)) {
hasProfile = true;
}
oldDbSize++;
}
if (hasProfile && allInstalled) {
//Legacy application is installed and ready to transition
} else {
globalPreferences.edit().putString(LEGACY_UPGRADE_PROGRESS, UPGRADE_COMPLETE).commit();
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy app detected, but it wasn't fully installed");
return;
}
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| application installed. Beginning transition");
//So if we've gotten this far, we definitely have an app we need to copy over. See if we have an application record, and create
//one if not.
if (record == null) {
record = new ApplicationRecord("legacy_application", ApplicationRecord.STATUS_SPECIAL_LEGACY);
}
//Commit? We can skip the searching for the old app if so, maybe?
//Ok, so fire up a seat for the new Application
CommCareApp app = new CommCareApp(record);
//Don't, however, create any of the file roots, we need to keep that namespace
//clean to copy over files.
app.setupSandbox(false);
//Ok, so. We now have a valid application record and can start moving over records.
//App data used to exist in three places, so we'll copy over all three
//1) DB Records
//2) File System Data
//3) Update File system references
//4) Application settings
//5) Stubbed out user keys
//1) DB Records
// The following models need to be moved: Resource Table entries, fixtures, and logs
SqlStorage<Resource> newInstallTable = app.getStorage("GLOBAL_RESOURCE_TABLE", Resource.class);
SqlStorage.cleanCopy(legacyResources, newInstallTable);
//Fixtures
LegacySqlIndexedStorageUtility<FormInstance> legacyFixtures = new LegacySqlIndexedStorageUtility<>("fixture", FormInstance.class, ldbh);
SqlStorage<FormInstance> newFixtures = app.getStorage("fixture", FormInstance.class);
SqlStorage.cleanCopy(legacyFixtures, newFixtures);
//Logs
//There's a twist, here. We only wanna copy over logs once to get a nice clear time-based record ordering.
LegacySqlIndexedStorageUtility<AndroidLogEntry> legacyLogs = new LegacySqlIndexedStorageUtility<>(AndroidLogEntry.STORAGE_KEY, AndroidLogEntry.class, ldbh);
if (legacyLogs.isEmpty()) {
//old logs are empty, no need to wipe new storage
} else {
SqlStorage<AndroidLogEntry> newLogs = CommCareApplication.instance().getGlobalStorage(AndroidLogEntry.STORAGE_KEY, AndroidLogEntry.class);
SqlStorage.cleanCopy(legacyLogs, newLogs);
//logs are copied over, wipe the old ones.
legacyLogs.removeAll();
}
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| Global resources copied");
//TODO: Record Progress?
//2) Ok, so now we want to copy over any old file system storage. Any jr:// prefixes should work fine after this move,
//but form records and such will still need to be updated. Unfortunately if people have placed their media in jr://file
//instead of jr://file/commcare we're going to miss it here, which sucks, but otherwise we run the risk of breaking future
//installs
//We supressed the file system generation, so make sure the root folder exists
new File(app.storageRoot()).mkdirs();
//Copy over the old file root
File oldRoot = new File(getOldFileSystemRoot());
String newRoot = app.fsPath("commcare/");
oldRoot.renameTo(new File(newRoot));
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| Files moved. Updating Handles");
//We also need to tell the XForm Provider that any/all of its forms have been moved
ArrayList<Pair<Uri, String>> toReplace = new ArrayList<>();
Cursor ef = null;
try {
ef = c.getContentResolver().query(FormsProviderAPI.FormsColumns.CONTENT_URI, new String[]{FormsProviderAPI.FormsColumns.FORM_FILE_PATH, FormsProviderAPI.FormsColumns._ID}, null, null, null);
if (ef != null) {
while (ef.moveToNext()) {
String filePath = ef.getString(ef.getColumnIndex(FormsProviderAPI.FormsColumns.FORM_FILE_PATH));
String newFilePath = replaceOldRoot(filePath, getOldFileSystemRoot(), newRoot);
if (!newFilePath.equals(filePath)) {
Uri uri = ContentUris.withAppendedId(FormsProviderAPI.FormsColumns.CONTENT_URI, ef.getLong(ef.getColumnIndex(FormsProviderAPI.FormsColumns._ID)));
toReplace.add(new Pair<>(uri, newFilePath));
}
}
}
} finally {
if (ef != null) {
ef.close();
}
}
for (Pair<Uri, String> p : toReplace) {
ContentValues cv = new ContentValues();
cv.put(FormsProviderAPI.FormsColumns.FORM_FILE_PATH, p.second);
int updated = c.getContentResolver().update(p.first, cv, null, null);
if (updated != 1) {
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| Warning: wrong number of xform content URI's updated: " + updated);
}
}
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| " + toReplace.size() + " xform content file paths updated. Moving prefs");
//3) Ok, so now we have app settings to copy over. Basically everything in the SharedPreferences should get put in the new app
//preferences
Map<String, ?> oldPrefs = globalPreferences.getAll();
//well, this sucks more than anticipated.
Editor e = app.getAppPreferences().edit();
for (String k : oldPrefs.keySet()) {
Object o = oldPrefs.get(k);
if (o instanceof String) {
e.putString(k, (String)o);
} else if (o instanceof Integer) {
e.putInt(k, (Integer)o);
} else if (o instanceof Long) {
e.putLong(k, (Long)o);
} else if (o instanceof Boolean) {
e.putBoolean(k, (Boolean)o);
} else if (o instanceof Float) {
e.putFloat(k, (Float)o);
}
}
e.commit();
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| Prefs updated. Adding user records...");
//4) Finally, we need to register a new UserKeyRecord which will prepare the user-facing records for transition
//when the user logs in again
//Get legacy user storage
LegacySqlIndexedStorageUtility<User> legacyUsers = new LegacySqlIndexedStorageUtility<>("USER", User.class, ldbh);
ArrayList<User> oldUsers = new ArrayList<>();
//So the old user storage wasn't encrypted since it had no actual prod data in it
for (User u : legacyUsers) {
oldUsers.add(u);
}
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| " + oldUsers.size() + " old user records detected");
//we're done with the old storage now.
olddb.close();
SqlStorage<UserKeyRecord> newUserKeyRecords = app.getStorage(UserKeyRecord.class);
User preferred = null;
//go through all of the old users and generate key records for them
for (User u : oldUsers) {
//make sure we haven't already handled this user somehow
if (newUserKeyRecords.getIDsForValue(UserKeyRecord.META_USERNAME, u.getUsername()).size() > 0) {
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| Skipping key record for " + u.getUsername() + " . One already exists?");
continue;
}
//See if we can find a user who was the last to log in
if (preferred == null || u.getUsername().toLowerCase().equals(globalPreferences.getString(CommCarePreferences.LAST_LOGGED_IN_USER, "").toLowerCase())) {
preferred = u;
}
//There's not specific reason to thing this might happen, but might be valuable to double check
if (newUserKeyRecords.getIDsForValue(UserKeyRecord.META_USERNAME, u.getUsername()).size() == 0) {
String sandboxId = PropertyUtils.genUUID().replace("-", "");
UserKeyRecord ukr = new UserKeyRecord(u.getUsername(), u.getPasswordHash(),
u.getWrappedKey(), new Date(), new Date(), sandboxId,
UserKeyRecord.TYPE_LEGACY_TRANSITION);
newUserKeyRecords.write(ukr);
}
}
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| App succesfully Transitioned! Writing progress");
//First off: All of the app resources are now transitioned. We can continue to handle data transitions at login if the following fails
app.writeInstalled();
globalPreferences.edit().putString(LEGACY_UPGRADE_PROGRESS, UPGRADE_COMPLETE).commit();
//Trigger all new resends
app.getAppPreferences().edit().putLong(CommCarePreferences.LOG_LAST_DAILY_SUBMIT, 0).commit();
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Legacy| App labelled. Attempting user transition");
//Now, we should try to transition over legacy user storage if any of the previous users are on test data
//First, see if the preferred user (Only one if available, or the most recent login)
if (preferred != null) {
//There should only be one user key record for that user which is legacy
for (UserKeyRecord ukr : newUserKeyRecords.getRecordsForValues(new String[]{UserKeyRecord.META_USERNAME}, new String[]{preferred.getUsername()})) {
if (ukr.getType() == UserKeyRecord.TYPE_LEGACY_TRANSITION) {
try {
transitionLegacyUserStorage(c, app, generateOldTestKey(c).getEncoded(), ukr);
} catch (RuntimeException re) {
//expected if they used a real key
re.printStackTrace();
}
break;
}
}
}
String toSkip = preferred == null ? null : preferred.getUsername();
for (UserKeyRecord ukr : newUserKeyRecords) {
if (ukr.getUsername().equals(toSkip)) {
continue;
}
if (ukr.getType() == UserKeyRecord.TYPE_LEGACY_TRANSITION) {
try {
transitionLegacyUserStorage(c, app, generateOldTestKey(c).getEncoded(), ukr);
} catch (RuntimeException re) {
//expected if they used a real key
re.printStackTrace();
}
break;
}
}
//Okay! Whew. We're now all set. Anything else that needs to happen should happen when the user logs in and we
//can unwrap their keys
app.teardownSandbox();
}
private static String getOldFileSystemRoot() {
String filesystemHome = CommCareApplication.instance().getAndroidFsRoot();
return filesystemHome + "commcare/";
}
public static void transitionLegacyUserStorage(final Context c, CommCareApp app, final byte[] oldKey, UserKeyRecord ukr) {
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| Beginning transition attempt for " + ukr.getUsername());
final CipherPool pool = new CipherPool() {
final Object lock = new Object();
final byte[] key = oldKey;
@Override
public Cipher generateNewCipher() {
synchronized (lock) {
try {
synchronized (key) {
SecretKeySpec spec = new SecretKeySpec(key, "AES");
Cipher decrypter = Cipher.getInstance("AES");
decrypter.init(Cipher.DECRYPT_MODE, spec);
return decrypter;
}
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
};
//get the legacy storage
final android.database.sqlite.SQLiteDatabase olddb = new LegacyCommCareOpenHelper(c, new LegacyCommCareDBCursorFactory(getLegacyEncryptedModels()) {
@Override
protected CipherPool getCipherPool() {
return pool;
}
}).getReadableDatabase();
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| Legacy DB Opened");
LegacyDbHelper ldbh = new LegacyDbHelper(c, pool.borrow()) {
@Override
public android.database.sqlite.SQLiteDatabase getHandle() {
return olddb;
}
};
final String newFileSystemRoot = app.fsPath("commcare/");
final String oldRoot = getOldFileSystemRoot();
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| Testing keys by attempting to open storage");
//TODO: This doesn't work.
LegacySqlIndexedStorageUtility<User> legacyUserStorage = new LegacySqlIndexedStorageUtility<>("User", User.class, ldbh);
try {
//Test to see if the old db worked
for (User u : legacyUserStorage) {
}
} catch (RuntimeException e) {
e.printStackTrace();
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| Exception " + e.getMessage() + " when testing storage. Keys are probably no good");
//This almost certainly means that we don't have the right key;
return;
}
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| Old keys look good! Creating new DB");
SQLiteDatabase ourDb;
//If we were able to iterate over the users, the key was fine, so let's use it to open our db
try {
ourDb = new DatabaseUserOpenHelper(CommCareApplication.instance(), ukr.getUuid()).getWritableDatabase(UserSandboxUtils.getSqlCipherEncodedKey(oldKey));
} catch (SQLiteException sle) {
//Our database got corrupted. Fortunately this represents a new record, so we can't actually need it.
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Attempted migrated database got corrupted. Deleting it and starting over");
c.getDatabasePath(DatabaseUserOpenHelper.getDbName(ukr.getUuid())).delete();
ourDb = new DatabaseUserOpenHelper(CommCareApplication.instance(), ukr.getUuid()).getWritableDatabase(UserSandboxUtils.getSqlCipherEncodedKey(oldKey));
}
final SQLiteDatabase currentUserDatabase = ourDb;
AndroidDbHelper newAndroidDbHelper = new AndroidDbHelper(c) {
@Override
public SQLiteDatabase getHandle() {
return currentUserDatabase;
}
};
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| All set to get going. Beginning storage copy");
//So we need to copy over a bunch of storage and also make some incidental changes along the way.
LegacySqlIndexedStorageUtility<ACase> legacyCases = new LegacySqlIndexedStorageUtility<>(ACase.STORAGE_KEY, ACase.class, ldbh);
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| " + legacyCases.getNumRecords() + " old cases detected");
Map m = SqlStorage.cleanCopy(legacyCases,
new SqlStorage<>(ACase.STORAGE_KEY, ACase.class, newAndroidDbHelper));
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| " + m.size() + " cases copied. Copying Users");
SqlStorage.cleanCopy(legacyUserStorage,
new SqlStorage<>("USER", User.class, newAndroidDbHelper));
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| Users copied. Copying form records");
final Map<Integer, Integer> formRecordMapping = SqlStorage.cleanCopy(new LegacySqlIndexedStorageUtility<>("FORMRECORDS", FormRecord.class, ldbh),
new SqlStorage<>("FORMRECORDS", FormRecord.class, newAndroidDbHelper), new CopyMapper<FormRecord>() {
@Override
public FormRecord transform(FormRecord t) {
String formRecordPath;
try {
formRecordPath = t.getPath(c);
String newPath = replaceOldRoot(formRecordPath, oldRoot, newFileSystemRoot);
if (!newPath.equals(formRecordPath)) {
ContentValues cv = new ContentValues();
cv.put(InstanceColumns.INSTANCE_FILE_PATH, newPath);
c.getContentResolver().update(t.getInstanceURI(), cv, null, null);
}
return t;
} catch (FileNotFoundException e) {
//This means the form record doesn't
//actually have a URI at all, so we
//can skip this.
return t;
}
}
});
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| Form records copied. Copying sessions.");
SqlStorage.cleanCopy(new LegacySqlIndexedStorageUtility<>("android_cc_session", SessionStateDescriptor.class, ldbh),
new SqlStorage<>("android_cc_session", SessionStateDescriptor.class, newAndroidDbHelper), new CopyMapper<SessionStateDescriptor>() {
@Override
public SessionStateDescriptor transform(SessionStateDescriptor t) {
return t.reMapFormRecordId(formRecordMapping.get(t.getFormRecordId()));
}
});
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| Sessions copied. Copying geocaches.");
SqlStorage.cleanCopy(new LegacySqlIndexedStorageUtility<>(GeocodeCacheModel.STORAGE_KEY, GeocodeCacheModel.class, ldbh),
new SqlStorage<>(GeocodeCacheModel.STORAGE_KEY, GeocodeCacheModel.class, newAndroidDbHelper));
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| geocaches copied. Copying serialized log submissions.");
SqlStorage.cleanCopy(new LegacySqlIndexedStorageUtility<>("log_records", DeviceReportRecord.class, ldbh),
new SqlStorage<>("log_records", DeviceReportRecord.class, newAndroidDbHelper), new CopyMapper<DeviceReportRecord>() {
@Override
public DeviceReportRecord transform(DeviceReportRecord t) {
return new DeviceReportRecord(replaceOldRoot(t.getFilePath(), oldRoot, newFileSystemRoot), t.getKey());
}
});
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| serialized log submissions copied. Copying fixtures");
SqlStorage.cleanCopy(new LegacySqlIndexedStorageUtility<>("fixture", FormInstance.class, ldbh),
new SqlStorage<>("fixture", FormInstance.class, newAndroidDbHelper));
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| Whew, storage copied! Updating key record");
//Now we can update this key record to confirm that it is fully installed
ukr.setType(UserKeyRecord.TYPE_NORMAL);
app.getStorage(UserKeyRecord.class).write(ukr);
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| Eliminating shared data from old install, since new users can't access it");
//Now, if we've copied everything over to this user with no problems, we want to actually go back and wipe out all of the
//data that is linked to specific files, since individual users might delete them out of their sandboxes.
new LegacySqlIndexedStorageUtility<>("log_records", DeviceReportRecord.class, ldbh).removeAll();
new LegacySqlIndexedStorageUtility<>(FormRecord.STORAGE_KEY, FormRecord.class, ldbh).removeAll();
new LegacySqlIndexedStorageUtility<>("android_cc_session", SessionStateDescriptor.class, ldbh).removeAll();
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "LegacyUser| User transitioned! Closing db handles.");
olddb.close();
currentUserDatabase.close();
}
protected static String replaceOldRoot(String filePath, String oldRoot, String newFileSystemRoot) {
try {
//TODO: It's really bad if these don't cannonicalize the same.
oldRoot = new File(oldRoot).getCanonicalPath();
filePath = new File(filePath).getCanonicalPath();
if (filePath.contains(oldRoot)) {
return filePath.replace(oldRoot, newFileSystemRoot);
}
return filePath;
} catch (IOException ioe) {
//This shouldn't happen and kind of sucks if it does. Should only occur if the file paths are invalid
Logger.log(AndroidLogger.TYPE_ERROR_ASSERTION, "Couldn't cannonicalize " + oldRoot + " or " + filePath);
return filePath;
}
}
private static Hashtable<String, EncryptedModel> getLegacyEncryptedModels() {
Hashtable<String, EncryptedModel> models = new Hashtable<>();
models.put(ACase.STORAGE_KEY, new ACase());
models.put("FORMRECORDS", new FormRecord());
models.put(GeocodeCacheModel.STORAGE_KEY, new GeocodeCacheModel());
models.put("log_records", new DeviceReportRecord());
return models;
}
public interface CopyMapper<T extends Persistable> {
T transform(T t);
}
private static SecretKeySpec generateOldTestKey(Context c) {
KeyGenerator generator;
try {
generator = KeyGenerator.getInstance("AES");
generator.init(256, new SecureRandom(getPhoneIdOld(c).getBytes()));
return new SecretKeySpec(generator.generateKey().getEncoded(), "AES");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
private static String getPhoneIdOld(Context c) {
TelephonyManager manager = (TelephonyManager)c.getSystemService(Context.TELEPHONY_SERVICE);
String imei = manager.getDeviceId();
if (imei == null) {
imei = Secure.ANDROID_ID;
}
return imei;
}
}