• Stars
    star
    138
  • Rank 263,390 (Top 6 %)
  • Language
    Java
  • License
    MIT License
  • Created over 8 years ago
  • Updated 12 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

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:

./gradlew :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.

More Repositories

1

NekoSMS

A pattern-based text message blocker for Android.
Java
416
star
2

GoogleImageShell

Google image search extension for Windows Explorer.
C#
174
star
3

AbsoluteTouchEx

Turns your Windows precision touchpad into a tablet. Kind of.
C++
115
star
4

uSticker

Custom stickers for Gboard on Android.
Kotlin
71
star
5

AbsoluteTouch

Turns your Synaptics touchpad into a tablet. Kind of.
C++
38
star
6

loliOS

Lightweight & operational Linux-inspired OS.
C
33
star
7

SoundCloudAdAway

Removes ads in the SoundCloud Android app.
Java
30
star
8

AniConvert

Yet another batch file converter for HandBrake.
Python
27
star
9

TumblrAdAway

A simple ad-blocker for the Android Tumblr app.
Java
26
star
10

Cardapter

SoundCloud for Android Auto.
Kotlin
19
star
11

QuoteLock

Quotes on your Android lockscreen!
Java
16
star
12

NoOverlayWarning

Bypasses the screen overlay detected dialog
Java
13
star
13

Stickerboard

Custom stickers in your iOS keyboard.
Swift
12
star
14

Pad2Screen

Turn any Windows precision touchpad into a touchscreen.
C
9
star
15

NoMouseAccel

Disables mouse acceleration on macOS.
C
8
star
16

stfu

Customizable call blocker for Android.
Kotlin
6
star
17

VNaaS

Visual Novel as a Service
Python
5
star
18

XposedPlugin

Xposed support plugin for IntelliJ IDEA/Android Studio
Java
5
star
19

Waifupaper

Live2D wallpaper framework for Android.
Kotlin
5
star
20

NoVpnMonitor

Removes the "connection may be monitored" message on Android.
Java
5
star
21

DebugKit

Tweaks to simplify Android app reverse engineering.
Java
4
star
22

sc2mp3

Adds a download button to SoundCloud tracks.
JavaScript
4
star
23

NoUsbNotification

Removes the USB debugging and MTP/PTP notifications on Android.
Java
4
star
24

CCIKotlin

Cracking the Coding Interview in Kotlin.
Kotlin
3
star
25

Ticketizer

Unofficial Python client for 12306.cn
Python
3
star
26

AndroidRE

Intro to Android reverse engineering repo
Java
3
star
27

LuaBridge

A method binding extension library for LuaJ.
Java
3
star
28

sc-repost-blocker

A browser extension to remove reposts from your SoundCloud stream.
JavaScript
3
star
29

shim

A tool to attack unencrypted HTTP pages.
Go
2
star
30

AutoMoodle

Automatically copy answers from the previous attempt on Moodle
JavaScript
2
star
31

TouchIME

Input text using your touchpad.
C#
2
star
32

VPNHelper

Manage L2TP VPN connections in Mac OS X.
C
1
star
33

Hazuki

Data structures and algorithms in C.
C
1
star
34

EmptySwipe

Disables the "swipe up on home to search" gesture on Android.
Java
1
star
35

PowerPlanChanger

A replacement for the power notification tray icon.
C#
1
star
36

stm32f407

Playground for the STM32F407 (aka STM32F4 Discovery)
C
1
star
37

NoShareServices

Prevents the Android share dialog from displaying services.
Java
1
star
38

taxcalc

A basic federal tax withholding calculator.
Python
1
star
39

gastrak

A basic Costco Gasoline price map.
Go
1
star
40

udm14-for-safari

Redirects Google to 'Web' search mode by default.
Swift
1
star