All Projects → apsun → RemotePreferences

apsun / RemotePreferences

Licence: MIT license
A drop-in solution for inter-app access to SharedPreferences.

Programming Languages

java
68154 projects - #9 most used programming language
kotlin
9241 projects

Projects that are alternatives of or similar to RemotePreferences

SandVXposed
Xposed environment without root (OS 5.0 - 10.0)
Stars: ✭ 832 (+587.6%)
Mutual labels:  xposed
SoundCloudAdAway
Removes ads in the SoundCloud Android app.
Stars: ✭ 25 (-79.34%)
Mutual labels:  xposed
MIUIAnesthetist
Cut out those cancer apps on MIUI painlessly with the help of this Xposed module.
Stars: ✭ 91 (-24.79%)
Mutual labels:  xposed
iMoney
iMoney 金融项目
Stars: ✭ 55 (-54.55%)
Mutual labels:  sharedpreferences
QQCleaner
瘦身模块
Stars: ✭ 512 (+323.14%)
Mutual labels:  xposed
AnyWebView
Any WebView is OK!
Stars: ✭ 64 (-47.11%)
Mutual labels:  xposed
shade
Automatic code generation for the SharedPreferences operation.
Stars: ✭ 26 (-78.51%)
Mutual labels:  sharedpreferences
Snorlax
Xposed module (Android) to check pokemons stats
Stars: ✭ 62 (-48.76%)
Mutual labels:  xposed
OneTapVideoDownload
Download Videos on Android with One Tap.
Stars: ✭ 51 (-57.85%)
Mutual labels:  xposed
NetworkMode-Enabler
A simple Xposed module which adds "3G only", "4G only", "4G/3G"(for compatible devices) options to the network modes setting
Stars: ✭ 14 (-88.43%)
Mutual labels:  xposed
Preference-Rhythm
Android library makes using Shared Preference easier.
Stars: ✭ 16 (-86.78%)
Mutual labels:  sharedpreferences
preferx
A reactive SharedPreferences library for Kotlin
Stars: ✭ 13 (-89.26%)
Mutual labels:  sharedpreferences
JodelXposed
Providing additional features for Jodel via the Xposed Framework
Stars: ✭ 29 (-76.03%)
Mutual labels:  xposed
glitchify
Tweaks for the official twitch.tv android app
Stars: ✭ 33 (-72.73%)
Mutual labels:  xposed
BiliRoaming
哔哩漫游,解除B站客户端番剧区域限制的Xposed模块,并且提供其他小功能。An Xposed module that unblocks bangumi area limit of BILIBILI with miscellaneous features.
Stars: ✭ 4,271 (+3429.75%)
Mutual labels:  xposed
KVStorage
Android 结构化KV存储框架,基于 yaml 生成 java 结构化存储类
Stars: ✭ 228 (+88.43%)
Mutual labels:  sharedpreferences
xposed-sgame
王者荣耀 开启高帧率插件
Stars: ✭ 23 (-80.99%)
Mutual labels:  xposed
Z-Spider
一些爬虫开发的技巧和案例
Stars: ✭ 33 (-72.73%)
Mutual labels:  xposed
Fuck-Dmzj
动漫之家增强模块
Stars: ✭ 49 (-59.5%)
Mutual labels:  xposed
XAutoDaily
一个基于QQ的全自动签到模块
Stars: ✭ 115 (-4.96%)
Mutual labels:  xposed

RemotePreferences

A drop-in solution for inter-app access to SharedPreferences.

Installation

1. Add the dependency to your build.gradle file:

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.crossbowffs.remotepreferences:remotepreferences:0.8'
}

2. Subclass RemotePreferenceProvider and implement a 0-argument constructor which calls the super constructor with an authority (e.g. "com.example.app.preferences") and an array of preference files to expose:

public class MyPreferenceProvider extends RemotePreferenceProvider {
    public MyPreferenceProvider() {
        super("com.example.app.preferences", new String[] {"main_prefs"});
    }
}

3. Add the corresponding entry to AndroidManifest.xml, with android:authorities equal to the authority you picked in the last step, and android:exported set to true:

<provider
    android:name=".MyPreferenceProvider"
    android:authorities="com.example.app.preferences"
    android:exported="true"/>

4. You're all set! To access your preferences, create a new instance of RemotePreferences with the same authority and the name of the preference file:

SharedPreferences prefs = new RemotePreferences(context, "com.example.app.preferences", "main_prefs");
int value = prefs.getInt("my_int_pref", 0);

WARNING: DO NOT use RemotePreferences from within IXposedHookZygoteInit.initZygote, since app providers have not been initialized at this point. Instead, defer preference loading to IXposedHookLoadPackage.handleLoadPackage.

Note that you should still use context.getSharedPreferences("main_prefs", MODE_PRIVATE) if your code is executing within the app that owns the preferences. Only use RemotePreferences when accessing preferences from the context of another app.

Also note that your preference keys cannot be null or "" (empty string).

Security

By default, all preferences have global read/write access. If this is what you want, then no additional configuration is required. However, chances are you'll want to prevent 3rd party apps from reading or writing your preferences. There are two ways to accomplish this:

  1. Use the Android permissions system built into ContentProvider
  2. Override the checkAccess method in RemotePreferenceProvider

Option 1 is the simplest to implement - just add android:readPermission and/or android:writePermission to your preference provider in AndroidManifest.xml. Unfortunately, this does not work very well if you are hooking apps that you do not control (e.g. Xposed), since you cannot modify their permissions.

Option 2 requires a bit of code, but is extremely powerful since you can control exactly which preferences can be accessed. To do this, override the checkAccess method in your preference provider class:

@Override
protected boolean checkAccess(String prefFileName, String prefKey, boolean write) {
    // Only allow read access
    if (write) {
        return false;
    }

    // Only allow access to certain preference keys
    if (!"my_pref_key".equals(prefKey)) {
        return false;
    }

    // Only allow access from certain apps
    if (!"com.example.otherapp".equals(getCallingPackage())) {
        return false;
    }

    return true;
}

Warning: when checking an operation such as getAll() or clear(), prefKey will be an empty string. If you are blacklisting certain keys, make sure to also blacklist the "" key as well!

Device encrypted preferences

By default, devices with Android N+ come with file-based encryption, which prevents RemotePreferences from accessing them before the first unlock after reboot. If preferences need to be accessed before the first unlock, the following modifications are needed.

1. Modify the provider constructor to mark the preference file as device protected:

public class MyPreferenceProvider extends RemotePreferenceProvider {
    public MyPreferenceProvider() {
        super("com.example.app.preferences", new RemotePreferenceFile[] {
            new RemotePreferenceFile("main_prefs", /* isDeviceProtected */ true)
        });
    }
}

This will cause the provider to use context.createDeviceProtectedStorageContext() to access the preferences.

2. Add support for direct boot in your manifest:

<provider
    android:name=".MyPreferenceProvider"
    android:authorities="com.example.app.preferences"
    android:exported="true"
    android:directBootAware="true"/>

3. Update your app to access shared preferences from device protected storage. If you are using PreferenceManager, call setStorageDeviceProtected(). If you are using SharedPreferences, use createDeviceProtectedStorageContext() to create the preferences. For example:

Context prefContext = context.createDeviceProtectedStorageContext();
SharedPreferences prefs = prefContext.getSharedPreferences("main_prefs", MODE_PRIVATE);

Strict mode

To maintain API compatibility with SharedPreferences, by default any errors encountered while accessing the preference provider will be ignored, resulting in default values being returned from the getter methods and apply() silently failing (we advise using commit() and checking the return value, at least). This can be caused by bugs in your code, or the user disabling your app/provider component. To detect and handle this scenario, you may opt-in to strict mode by passing an extra parameter to the RemotePreferences constructor:

SharedPreferences prefs = new RemotePreferences(context, authority, prefFileName, true);

Now, if the preference provider cannot be accessed, a RemotePreferenceAccessException will be thrown. You can handle this by wrapping your preference accesses in a try-catch block:

try {
    int value = prefs.getInt("my_int_pref", 0);
    prefs.edit().putInt("my_int_pref", value + 1).apply();
} catch (RemotePreferenceAccessException e) {
    // Handle the error
}

Why would I need this?

This library was developed to simplify Xposed module preference access. XSharedPreferences has been known to silently fail on some devices, and does not support remote write access or value changed listeners. Thus, RemotePreferences was born.

Of course, feel free to use this library anywhere you like; it's not limited to Xposed at all! :-)

How does it work?

To achieve true inter-process SharedPreferences access, all requests are proxied through a ContentProvider. Preference change callbacks are implemented using ContentObserver.

This solution does not use MODE_WORLD_WRITEABLE (which was deprecated in Android 4.2) or any other file permission hacks.

Running tests

Connect your Android device and run:

gradle :testapp:connectedAndroidTest

License

Distributed under the MIT License.

Changelog

0.8

  • RemotePreferences is now hosted on mavenCentral()
  • Fixed onSharedPreferenceChanged getting the wrong key when calling clear()

0.7

  • Added support for preferences located in device protected storage (thanks to Rijul-A)

0.6

  • Improved error checking
  • Fixed case where strict mode was not applying when editing multiple preferences
  • Added more documentation for library internals
  • Updated project to modern Android Studio layout

0.5

  • Ensure edits are atomic - either all or no edits succeed when committing
  • Minor performance improvement when adding/removing multiple keys

0.4

  • Fixed IllegalArgumentException being thrown instead of RemotePreferenceAccessException

0.3

  • Values can now be null again
  • Improved error checking if you are using the ContentProvider interface directly

0.2

  • Fixed catastrophic security bug allowing anyone to write to preferences
  • Added strict mode to distinguish between "cannot access provider" vs. "key doesn't exist"
  • Keys can no longer be null or "", values can no longer be null

0.1

  • Initial release.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].