package biz.bokhorst.xprivacy; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import android.annotation.SuppressLint; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; import android.content.UriMatcher; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import android.os.Binder; import android.os.Process; import android.util.Log; // This code is legacy and only used for updating to newer versions @SuppressWarnings("deprecation") @SuppressLint({ "DefaultLocale", "WorldReadableFiles", "Registered" }) public class PrivacyProvider extends ContentProvider { private static final String AUTHORITY = "biz.bokhorst.xprivacy.provider"; private static final String PREF_RESTRICTION = AUTHORITY; private static final String PREF_USAGE = AUTHORITY + ".usage"; private static final String PREF_SETTINGS = AUTHORITY + ".settings"; private static final String PATH_RESTRICTION = "restriction"; private static final String PATH_USAGE = "usage"; private static final String PATH_SETTINGS = "settings"; public static final Uri URI_RESTRICTION = Uri.parse("content://" + AUTHORITY + "/" + PATH_RESTRICTION); public static final Uri URI_USAGE = Uri.parse("content://" + AUTHORITY + "/" + PATH_USAGE); public static final Uri URI_SETTING = Uri.parse("content://" + AUTHORITY + "/" + PATH_SETTINGS); public static final String COL_UID = "Uid"; public static final String COL_RESTRICTION = "Restriction"; public static final String COL_RESTRICTED = "Restricted"; public static final String COL_METHOD = "Method"; public static final String COL_USED = "Used"; public static final String COL_SETTING = "Setting"; public static final String COL_VALUE = "Value"; private static final UriMatcher sUriMatcher; private static final int TYPE_RESTRICTION = 1; private static final int TYPE_USAGE = 2; private static final int TYPE_SETTING = 3; private static Object mFallbackRestrictionLock = new Object(); private static Object mFallbackSettingsLock = new Object(); private static int mFallbackRestrictionsUid = 0; private static long mFallbackRestrictionsTime = 0; private static long mFallbackSettingsTime = 0; private static SharedPreferencesEx mFallbackRestrictions = null; private static SharedPreferencesEx mFallbackSettings = null; private static ExecutorService mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(AUTHORITY, PATH_RESTRICTION, TYPE_RESTRICTION); sUriMatcher.addURI(AUTHORITY, PATH_USAGE, TYPE_USAGE); sUriMatcher.addURI(AUTHORITY, PATH_SETTINGS, TYPE_SETTING); } @Override public boolean onCreate() { try { convertRestrictions(getContext()); convertSettings(getContext()); fixFilePermissions(); } catch (Throwable ex) { Util.bug(null, ex); } return true; } @Override public String getType(Uri uri) { if (sUriMatcher.match(uri) == TYPE_RESTRICTION) return String.format("vnd.android.cursor.dir/%s.%s", AUTHORITY, PATH_RESTRICTION); else if (sUriMatcher.match(uri) == TYPE_USAGE) return String.format("vnd.android.cursor.dir/%s.%s", AUTHORITY, PATH_USAGE); else if (sUriMatcher.match(uri) == TYPE_SETTING) return String.format("vnd.android.cursor.dir/%s.%s", AUTHORITY, PATH_SETTINGS); throw new IllegalArgumentException(); } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { if (sUriMatcher.match(uri) == TYPE_RESTRICTION && selectionArgs != null && selectionArgs.length >= 2) { // Query restrictions String restrictionName = selection; int uid = Integer.parseInt(selectionArgs[0]); boolean usage = Boolean.parseBoolean(selectionArgs[1]); String methodName = (selectionArgs.length >= 3 ? selectionArgs[2] : null); return queryRestrictions(uid, restrictionName, methodName, usage); } else if (sUriMatcher.match(uri) == TYPE_USAGE && selectionArgs != null && selectionArgs.length >= 1) { // Query usage String restrictionName = selection; int uid = Integer.parseInt(selectionArgs[0]); String methodName = (selectionArgs.length >= 2 ? selectionArgs[1] : null); return queryUsage(uid, restrictionName, methodName); } else if (sUriMatcher.match(uri) == TYPE_SETTING && selectionArgs == null) return querySettings(selection); throw new IllegalArgumentException(uri.toString()); } private Cursor queryRestrictions(final int uid, final String restrictionName, final String methodName, boolean usage) { MatrixCursor cursor = new MatrixCursor(new String[] { COL_UID, COL_RESTRICTION, COL_METHOD, COL_RESTRICTED }); // Build restriction list List<String> listRestrictionName; if (restrictionName == null) listRestrictionName = PrivacyManager.getRestrictions(); else { listRestrictionName = new ArrayList<String>(); listRestrictionName.add(restrictionName); } if (uid == 0) { // Process applications PackageManager pm = getContext().getPackageManager(); for (ApplicationInfo appInfo : pm.getInstalledApplications(PackageManager.GET_META_DATA)) { SharedPreferences prefs = getContext().getSharedPreferences(PREF_RESTRICTION + "." + appInfo.uid, Context.MODE_WORLD_READABLE); // Process restrictions for (String eRestrictionName : listRestrictionName) if (getRestricted(eRestrictionName, null, prefs)) { // Category cursor.addRow(new Object[] { appInfo.uid, eRestrictionName, null, true }); // Exceptions for (Hook md : PrivacyManager.getHooks(eRestrictionName, null)) { boolean restricted = getRestricted(eRestrictionName, md.getName(), prefs); if (!restricted || md.isDangerous()) cursor.addRow(new Object[] { appInfo.uid, eRestrictionName, md.getName(), restricted }); } } } } else { SharedPreferences prefs = getContext().getSharedPreferences(PREF_RESTRICTION + "." + uid, Context.MODE_WORLD_READABLE); // Process restrictions boolean restricted = false; if (methodName != null && methodName.equals("*")) { for (String eRestrictionName : listRestrictionName) { boolean eRestricted = getRestricted(eRestrictionName, null, prefs); cursor.addRow(new Object[] { uid, eRestrictionName, null, Boolean.toString(eRestricted) }); for (Hook md : PrivacyManager.getHooks(eRestrictionName, null)) { eRestricted = getRestricted(eRestrictionName, md.getName(), prefs); cursor.addRow(new Object[] { uid, eRestrictionName, md.getName(), Boolean.toString(eRestricted) }); } } } else { for (String eRestrictionName : listRestrictionName) { boolean eRestricted = getRestricted(eRestrictionName, methodName, prefs); cursor.addRow(new Object[] { uid, eRestrictionName, methodName, Boolean.toString(eRestricted) }); restricted = (restricted || eRestricted); } } // Update usage data if (usage && restrictionName != null && methodName != null && !methodName.equals("*")) { final boolean isRestricted = restricted; final long timeStamp = new Date().getTime(); mExecutor.execute(new Runnable() { public void run() { updateUsage(uid, restrictionName, methodName, isRestricted, timeStamp); } }); } } return cursor; } private static boolean getRestricted(String restrictionName, String methodName, SharedPreferences prefs) { // Check for restriction boolean restricted = prefs.getBoolean(getRestrictionPref(restrictionName), false); // Check for exception if (restricted && methodName != null) if (prefs.getBoolean(getExceptionPref(restrictionName, methodName), false)) restricted = false; return restricted; } private Cursor queryUsage(int uid, String restrictionName, String methodName) { MatrixCursor cursor = new MatrixCursor(new String[] { COL_UID, COL_RESTRICTION, COL_METHOD, COL_RESTRICTED, COL_USED }); List<String> listRestriction; if (restrictionName == null) listRestriction = PrivacyManager.getRestrictions(); else { listRestriction = new ArrayList<String>(); listRestriction.add(restrictionName); } if (uid == 0) { // All for (String eRestrictionName : PrivacyManager.getRestrictions()) { SharedPreferences prefs = getContext().getSharedPreferences(PREF_USAGE + "." + eRestrictionName, Context.MODE_PRIVATE); for (String prefName : prefs.getAll().keySet()) if (prefName.startsWith(COL_USED)) { String[] prefParts = prefName.split("\\."); int rUid = Integer.parseInt(prefParts[1]); String rMethodName = prefName.substring(prefParts[0].length() + 1 + prefParts[1].length() + 1); getUsage(rUid, eRestrictionName, rMethodName, cursor); } } } else { // Selected restrictions/methods for (String eRestrictionName : listRestriction) if (methodName == null) for (Hook md : PrivacyManager.getHooks(eRestrictionName, null)) getUsage(uid, eRestrictionName, md.getName(), cursor); else getUsage(uid, eRestrictionName, methodName, cursor); } return cursor; } private void getUsage(int uid, String restrictionName, String methodName, MatrixCursor cursor) { SharedPreferences prefs = getContext().getSharedPreferences(PREF_USAGE + "." + restrictionName, Context.MODE_PRIVATE); String values = prefs.getString(getUsagePref(uid, methodName), null); if (values != null) { String[] value = values.split(":"); long timeStamp = Long.parseLong(value[0]); boolean restricted = Boolean.parseBoolean(value[1]); cursor.addRow(new Object[] { uid, restrictionName, methodName, restricted, timeStamp }); } } private Cursor querySettings(String name) { SharedPreferences prefs = getContext().getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); MatrixCursor cursor = new MatrixCursor(new String[] { COL_SETTING, COL_VALUE }); if (name == null) for (String settingKey : prefs.getAll().keySet()) try { cursor.addRow(new Object[] { getSettingName(settingKey), prefs.getString(settingKey, null) }); } catch (Throwable ex) { // Legacy boolean } else cursor.addRow(new Object[] { name, prefs.getString(getSettingPref(name), null) }); return cursor; } @Override public Uri insert(Uri uri, ContentValues values) { // Check access enforcePermission(); throw new IllegalArgumentException(uri.toString()); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { if (sUriMatcher.match(uri) == TYPE_RESTRICTION) { // Check access enforcePermission(); // Get arguments String restrictionName = selection; int uid = values.getAsInteger(COL_UID); String methodName = values.getAsString(COL_METHOD); boolean restricted = Boolean.parseBoolean(values.getAsString(COL_RESTRICTED)); updateRestriction(getContext(), uid, restrictionName, methodName, !restricted); return 1; // rows } else if (sUriMatcher.match(uri) == TYPE_USAGE) { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // Get arguments int uid = values.getAsInteger(COL_UID); String restrictionName = values.getAsString(PrivacyProvider.COL_RESTRICTION); String methodName = values.getAsString(COL_METHOD); boolean restricted = false; if (values.containsKey(PrivacyProvider.COL_RESTRICTED)) restricted = values.getAsBoolean(PrivacyProvider.COL_RESTRICTED); long timeStamp = values.getAsLong(PrivacyProvider.COL_USED); Util.log(null, Log.INFO, String.format("Update usage data %d/%s/%s=%b", uid, restrictionName, methodName, restricted)); // Update usage data if (methodName != null) updateUsage(uid, restrictionName, methodName, restricted, timeStamp); return 1; } else if (sUriMatcher.match(uri) == TYPE_SETTING) { // Check access enforcePermission(); // Get arguments String settingName = selection; String value = values.getAsString(COL_VALUE); // Update setting updateSetting(settingName, value); return 1; } throw new IllegalArgumentException(uri.toString()); } private static void updateRestriction(Context context, int uid, String restrictionName, String methodName, boolean allowed) { // Update restriction SharedPreferences prefs = context.getSharedPreferences(PREF_RESTRICTION + "." + uid, Context.MODE_WORLD_READABLE); SharedPreferences.Editor editor = prefs.edit(); if (methodName == null || !allowed) editor.putBoolean(getRestrictionPref(restrictionName), !allowed); if (methodName != null) editor.putBoolean(getExceptionPref(restrictionName, methodName), allowed); editor.commit(); setPrefFileReadable(PREF_RESTRICTION, uid); } private void updateUsage(final int uid, final String restrictionName, final String methodName, final boolean restricted, long timeStamp) { SharedPreferences prefs = getContext().getSharedPreferences(PREF_USAGE + "." + restrictionName, Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); String prefName = getUsagePref(uid, methodName); String prefValue = String.format("%d:%b", timeStamp, restricted); editor.remove(prefName); editor.putString(prefName, prefValue); editor.commit(); } private void updateSetting(String name, String value) { SharedPreferences prefs = getContext().getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); SharedPreferences.Editor editor = prefs.edit(); if (value == null) editor.remove(getSettingPref(name)); else editor.putString(getSettingPref(name), value); editor.commit(); setPrefFileReadable(PREF_SETTINGS); } @Override public int delete(Uri uri, String where, String[] selectionArgs) { // Check access enforcePermission(); if (sUriMatcher.match(uri) == TYPE_RESTRICTION) { int uid = Integer.parseInt(selectionArgs[0]); return deleteRestrictions(uid); } else if (sUriMatcher.match(uri) == TYPE_USAGE) { int uid = Integer.parseInt(selectionArgs[0]); return deleteUsage(uid); } else if (sUriMatcher.match(uri) == TYPE_SETTING) { int uid = Integer.parseInt(selectionArgs[0]); return deleteSettings(uid); } throw new IllegalArgumentException(uri.toString()); } private int deleteRestrictions(int uid) { int rows = 0; SharedPreferences prefs = getContext().getSharedPreferences(PREF_RESTRICTION + "." + uid, Context.MODE_WORLD_READABLE); SharedPreferences.Editor editor = prefs.edit(); for (String pref : prefs.getAll().keySet()) { Util.log(null, Log.INFO, "Removed restriction=" + pref + " uid=" + uid); editor.remove(pref); rows++; } editor.commit(); setPrefFileReadable(PREF_RESTRICTION, uid); return rows; } private int deleteUsage(int uid) { int rows = 0; String sUid = Integer.toString(uid); for (String restrictionName : PrivacyManager.getRestrictions()) { SharedPreferences prefs = getContext().getSharedPreferences(PREF_USAGE + "." + restrictionName, Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); for (String pref : prefs.getAll().keySet()) { String[] component = pref.split("\\."); if (uid == 0 || (component.length >= 2 && component[1].equals(sUid))) { Util.log(null, Log.INFO, "Removed usage=" + pref); editor.remove(pref); rows++; } } editor.commit(); } return rows; } private int deleteSettings(int uid) { int rows = 0; String sUid = Integer.toString(uid); SharedPreferences prefs = getContext().getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); SharedPreferences.Editor editor = prefs.edit(); for (String pref : prefs.getAll().keySet()) { String[] component = pref.split("\\."); if (component.length >= 3 && component[2].equals(sUid)) { Util.log(null, Log.INFO, "Removed setting=" + pref + " uid=" + uid); editor.remove(pref); rows++; } } editor.commit(); setPrefFileReadable(PREF_SETTINGS); return rows; } // The following methods are used as fallback, when: // - there is no context (Java threads) // - the content provider cannot be queried (PackageManagerService) public static boolean getRestrictedFallback(XHook hook, int uid, String restrictionName, String methodName) { try { long now = new Date().getTime(); File file = new File(getPrefFileName(PREF_RESTRICTION, uid)); if (!file.exists()) Util.log(null, Log.INFO, "Not found file=" + file.getAbsolutePath()); synchronized (mFallbackRestrictionLock) { if (mFallbackRestrictions == null || mFallbackRestrictionsUid != uid) { // Initial load mFallbackRestrictions = new SharedPreferencesEx(file); mFallbackRestrictionsUid = uid; mFallbackRestrictionsTime = now; long ms = System.currentTimeMillis() - now; Util.log(null, Log.INFO, "Load fallback restrictions uid=" + uid + "/" + mFallbackRestrictionsUid + " " + ms + " ms"); } else if (mFallbackRestrictionsTime + PrivacyManager.cRestrictionCacheTimeoutMs < now) { // Check update mFallbackRestrictions.reload(); mFallbackRestrictionsUid = uid; mFallbackRestrictionsTime = now; long ms = System.currentTimeMillis() - now; Util.log(null, Log.INFO, "Reload fallback restrictions uid=" + uid + " " + ms + " ms"); } } return getRestricted(restrictionName, methodName, mFallbackRestrictions); } catch (Throwable ex) { Util.bug(hook, ex); return false; } } public static String getSettingFallback(String name, String defaultValue, boolean log) { try { long now = new Date().getTime(); File file = new File(getPrefFileName(PREF_SETTINGS)); if (!file.exists()) if (log) Util.log(null, Log.INFO, "Not found file=" + file.getAbsolutePath()); synchronized (mFallbackSettingsLock) { // Initial load if (mFallbackSettings == null) { mFallbackSettings = new SharedPreferencesEx(file); mFallbackSettingsTime = now; if (log) { long ms = System.currentTimeMillis() - now; Util.log(null, Log.INFO, "Load fallback settings uid=" + Binder.getCallingUid() + " " + ms + " ms"); } } // Get update if (mFallbackSettingsTime + PrivacyManager.cSettingCacheTimeoutMs < now) { mFallbackSettings.reload(); mFallbackSettingsTime = now; if (log) { long ms = System.currentTimeMillis() - now; Util.log(null, Log.INFO, "Reload fallback settings uid=" + Binder.getCallingUid() + " " + ms + " ms"); } } } return mFallbackSettings.getString(getSettingPref(name), defaultValue); } catch (Throwable ex) { if (log) Util.bug(null, ex); return defaultValue; } } public static void flush() { Util.log(null, Log.INFO, "Flush uid=" + Binder.getCallingUid()); synchronized (mFallbackRestrictionLock) { mFallbackRestrictions = null; } synchronized (mFallbackSettingsLock) { mFallbackSettings = null; } } // Helper methods private void enforcePermission() throws SecurityException { if (Binder.getCallingUid() != Process.myUid()) throw new SecurityException(); } private static String getPrefFileName(String preference) { return Util.getUserDataDirectory(Process.myUid()) + File.separator + "shared_prefs" + File.separator + preference + ".xml"; } private static String getPrefFileName(String preference, int uid) { return Util.getUserDataDirectory(uid) + File.separator + "shared_prefs" + File.separator + preference + "." + uid + ".xml"; } private static void setPrefFileReadable(String preference) { new File(getPrefFileName(preference)).setReadable(true, false); } private static void setPrefFileReadable(String preference, int uid) { new File(getPrefFileName(preference, uid)).setReadable(true, false); } public static void fixFilePermissions() { File folder = new File(Util.getUserDataDirectory(Process.myUid()) + File.separator + "shared_prefs"); folder.setReadable(true, false); File[] files = folder.listFiles(); if (files != null) for (File file : files) if (file.getName().startsWith("biz.bokhorst.xprivacy.provider.") && file.getName().endsWith(".xml") && !file.getName().contains(".usage.")) file.setReadable(true, false); } private static String getRestrictionPref(String restrictionName) { return String.format("%s.%s", COL_RESTRICTED, restrictionName); } private static String getExceptionPref(String restrictionName, String methodName) { return String.format("%s.%s.%s", COL_METHOD, restrictionName, methodName); } private static String getUsagePref(int uid, String methodName) { return String.format("%s.%d.%s", COL_USED, uid, methodName); } private static String getSettingPref(String settingName) { return String.format("%s.%s", COL_SETTING, settingName); } private static String getSettingName(String settingKey) { return settingKey.substring(COL_SETTING.length() + 1); } private static void convertRestrictions(Context context) throws IOException { File source = new File(Util.getUserDataDirectory(Process.myUid()) + File.separator + "shared_prefs" + File.separator + "biz.bokhorst.xprivacy.provider.xml"); File backup = new File(source.getAbsoluteFile() + ".orig"); if (source.exists() && !backup.exists()) { Util.log(null, Log.WARN, "Converting restrictions"); SharedPreferences prefs = context.getSharedPreferences(PREF_RESTRICTION, Context.MODE_WORLD_READABLE); for (String key : prefs.getAll().keySet()) { String[] component = key.split("\\."); if (key.startsWith(COL_RESTRICTED)) { String restrictionName = component[1]; String value = prefs.getString(key, null); List<String> listRestriction = new ArrayList<String>(Arrays.asList(value.split(","))); listRestriction.remove(0); for (String uid : listRestriction) updateRestriction(context, Integer.parseInt(uid), restrictionName, null, false); } else if (key.startsWith(COL_METHOD)) { int uid = Integer.parseInt(component[1]); String restrictionName = component[2]; String methodName = component[3]; boolean value = prefs.getBoolean(key, false); updateRestriction(context, uid, restrictionName, methodName, value); } else Util.log(null, Log.WARN, "Unknown key=" + key); } // Backup old file Util.log(null, Log.INFO, "Backup name=" + backup.getAbsolutePath()); Util.copy(source, backup); } } private static void convertSettings(Context context) throws IOException { if (new File(getPrefFileName(PREF_SETTINGS)).exists()) { SharedPreferences prefs = context.getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); SharedPreferences.Editor editor = prefs.edit(); for (String key : prefs.getAll().keySet()) try { String value = prefs.getString(key, null); if (PrivacyManager.cValueRandomLegacy.equals(value)) editor.putString(key, PrivacyManager.cValueRandom); } catch (Throwable ex) { } editor.commit(); setPrefFileReadable(PREF_SETTINGS); } } private static void splitSettings(Context context) { File prefFile = new File(getPrefFileName(PREF_SETTINGS)); File migratedFile = new File(prefFile + ".migrated"); if (prefFile.exists() && !migratedFile.exists()) { Util.log(null, Log.WARN, "Splitting " + prefFile); SharedPreferences prefs = context.getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); for (String settingKey : prefs.getAll().keySet()) try { int uid = 0; String name = getSettingName(settingKey); String value = prefs.getString(settingKey, ""); // Decode setting String[] component = name.split("\\."); if (name.startsWith("Account.") || name.startsWith("Application.") || name.startsWith("Contact.")) { try { // name.uid.key uid = Integer.parseInt(component[1]); name = component[0]; for (int i = 2; i < component.length; i++) name += "." + component[i]; } catch (NumberFormatException ignored) { // Initial uid/name will be used } } else if (component.length > 1) { try { // name.x.y.z.uid uid = Integer.parseInt(component[component.length - 1]); name = component[0]; for (int i = 1; i < component.length - 1; i++) name += "." + component[i]; } catch (NumberFormatException ignored) { // Initial uid/name will be used } } SharedPreferences aprefs = context.getSharedPreferences(PREF_SETTINGS + "." + uid, Context.MODE_WORLD_READABLE); SharedPreferences.Editor editor = aprefs.edit(); editor.putString(name, value); editor.commit(); } catch (Throwable ex) { // Legacy boolean Util.bug(null, ex); } prefFile.renameTo(migratedFile); } } // Migration public static void migrateLegacy(Context context) throws IOException { convertSettings(context); convertRestrictions(context); splitSettings(context); } public static List<PRestriction> migrateRestrictions(Context context, int uid) { List<PRestriction> listWork = new ArrayList<PRestriction>(); File prefFile = new File(getPrefFileName(PREF_RESTRICTION, uid)); File migratedFile = new File(prefFile + ".migrated"); if (prefFile.exists() && !migratedFile.exists()) { Util.log(null, Log.WARN, "Migrating " + prefFile); SharedPreferences prefs = context.getSharedPreferences(PREF_RESTRICTION + "." + uid, Context.MODE_WORLD_READABLE); // Process restrictions for (String restrictionName : PrivacyManager.getRestrictions()) if (getRestricted(restrictionName, null, prefs)) { // Category listWork.add(new PRestriction(uid, restrictionName, null, true)); // Exceptions for (Hook md : PrivacyManager.getHooks(restrictionName, null)) { boolean restricted = getRestricted(restrictionName, md.getName(), prefs); if (!restricted || md.isDangerous()) listWork.add(new PRestriction(uid, restrictionName, md.getName(), restricted)); } } } return listWork; } public static void finishMigrateRestrictions(int uid) { File prefFile = new File(getPrefFileName(PREF_RESTRICTION, uid)); File migratedFile = new File(prefFile + ".migrated"); prefFile.renameTo(migratedFile); } public static List<PSetting> migrateSettings(Context context, int uid) { // Process settings List<PSetting> listWork = new ArrayList<PSetting>(); File prefFile = new File(getPrefFileName(PREF_SETTINGS, uid)); File migratedFile = new File(prefFile + ".migrated"); if (prefFile.exists() && !migratedFile.exists()) { Util.log(null, Log.WARN, "Migrating " + prefFile); SharedPreferences prefs = context.getSharedPreferences(PREF_SETTINGS + "." + uid, Context.MODE_WORLD_READABLE); for (String name : prefs.getAll().keySet()) try { String value = prefs.getString(name, null); if (value != null && !"".equals(value)) { String type; if (name.startsWith("Account.") || name.startsWith("Application.") || name.startsWith("Contact.") || name.startsWith("Template.")) { int dot = name.indexOf('.'); type = name.substring(0, dot); name = name.substring(dot + 1); } else type = ""; listWork.add(new PSetting(uid, type, name, value)); } } catch (Throwable ex) { // Legacy boolean Util.bug(null, ex); } } return listWork; } public static void finishMigrateSettings(int uid) { File prefFile = new File(getPrefFileName(PREF_SETTINGS, uid)); File migratedFile = new File(prefFile + ".migrated"); prefFile.renameTo(migratedFile); } }