Skip to content

Instantly share code, notes, and snippets.

@tnj
Created June 22, 2012 08:08
Show Gist options
  • Save tnj/2971240 to your computer and use it in GitHub Desktop.
Save tnj/2971240 to your computer and use it in GitHub Desktop.
Samsung Galaxy S 2.2.1 broken /dbdata/databases workaround
/*
* Copyright (C) 2012 Yuki Fujisaki
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sh.nothing.android.workaround.samsung;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.app.Application;
import android.content.Context;
import android.content.ContextWrapper;
import android.preference.PreferenceManager;
import android.util.Log;
public class SharedPreferencesPatch {
private static final String TAG = "SharedPreferencesPatch";
private static final String SAMSUNG_PREF_DIR_BASE = "/dbdata/databases";
private static Boolean sIsNeedPreferenceWorkaround = null;
/**
* As a workaround for Galaxy S /dbdata/databases permission bug, this
* function will make an injection to the implementation of Context to
* change a directory used by SharedPreferences. Should be called from
* {@link Application#onCreate()} when the application starts.
*
* You also need to replace every
* {@link PreferenceManager#getDefaultSharedPreferences(Context)} or
* {@link Context#getSharedPreferences(String, int)} calls in your
* application to use application context.
*
* @param context
* injection target context.
*/
public static void injectPrefencesDirIfNeeded(Context context) {
if (isNeedPreferenceWorkaround(context))
injectPreferencesDir(context);
}
private static boolean isNeedPreferenceWorkaround(Context context) {
if (sIsNeedPreferenceWorkaround == null) {
File baseDir = new File(SAMSUNG_PREF_DIR_BASE,
context.getPackageName());
boolean result = baseDir.exists() && !baseDir.canWrite();
if (result)
Log.v(TAG, "need /dbdata workaround");
sIsNeedPreferenceWorkaround = result;
}
return sIsNeedPreferenceWorkaround.booleanValue();
}
private static void injectPreferencesDir(Context context) {
if (ContextWrapper.class.isInstance(context)) {
ContextWrapper wrapper = (ContextWrapper) context;
context = wrapper.getBaseContext();
}
// inject to overwrite inner ContextImpl#mPreferencesDir
try {
Class<?> clazz = Class.forName("android.app.ContextImpl");
Field field = clazz.getDeclaredField("mPreferencesDir");
field.setAccessible(true);
if (field.get(context) != null)
return;
// build shared_prefs path and try to overwrite
Method getDataDirFile = clazz.getDeclaredMethod("getDataDirFile");
getDataDirFile.setAccessible(true);
File dataDir = (File) getDataDirFile.invoke(context);
field.set(context, new File(dataDir, "shared_prefs"));
} catch (Exception ignored) {
Log.w(TAG,
"failed to apply Samsung Galaxy broken /dbdata workaround.",
ignored);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment