-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index ba7052b8..af0bbdde 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,30 +1,11 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 2a39d714..8388a2ff 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,8 +2,8 @@
+
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 94a25f7f..35eb1ddf 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/DEFINE.md b/DEFINE.md
new file mode 100644
index 00000000..0f69ed58
--- /dev/null
+++ b/DEFINE.md
@@ -0,0 +1,185 @@
+Defining hooks
+==============
+
+Introduction
+------------
+
+XPrivacyLua allows you do define Xposed hooks without developing an Xposed module.
+
+Defined hooks can be added and updated at run time,
+with the big advantage that no reboot is required to test a new or changed hook
+(with the exception of persistent system apps, which are clearly marked in XPrivacyLua).
+To apply an updated definition an app just needs to be stopped (force closed) and started again.
+An easy way to do this is by toggling a definition off/on in XPrivacyLua.
+
+XPrivacyLua allows you to select which apps a definition should be applied to.
+
+You can edit hook definitions for free with the XPrivacyLua [pro companion app](https://play.google.com/apps/testing/eu.faircode.xlua.pro).
+
+Definition
+----------
+
+Hook definitions describe where to hook and what to do when the hook executes.
+
+The *where to hook* is described as:
+
+* Which [class](https://developer.android.com/reference/java/lang/Class.html)
+* Which [method](https://developer.android.com/reference/java/lang/reflect/Method.html)
+
+In the well documented [Android API](https://developer.android.com/reference/packages.html) you can find class and method names.
+For more advanced hooks, see [here](https://github.com/rovo89/XposedBridge/wiki/Development-tutorial#exploring-your-target-and-finding-a-way-to-modify-it).
+
+The *what to do* is described in the form of a [Lua](https://www.lua.org/pil/contents.html) script.
+
+An exported definition in [JSON](https://en.wikipedia.org/wiki/JSON) format looks like this:
+
+```JSON
+{
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager\/getDeviceId",
+ "author": "M66B",
+ "description": "Let the telephony manager return NULL as device ID",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getDeviceId",
+ "parameterTypes": [],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "maxSdk": 999,
+ "enabled": true,
+ "optional": false,
+ "usage": true,
+ "notify": false,
+ "settings": [
+ "setting_name1",
+ "setting_name2"
+ ],
+ "luaScript": "function after(hook, param)\n local result = param:getResult()\n if result == nil then\n return false\n end\n\n param:setResult(null)\n return true\nend\n"
+}
+```
+
+
+
+Note that you can conveniently edit hook definitions in the pro companion app, so there is no need to edit JSON files.
+
+
+
+* The *collection* and *name* attributes are used to uniquely identify a hook
+* For convenience XPrivacyLua applies hooks by *group*
+* The attributes *minSdk* and *maxSdk* determine for which [Android versions](https://source.android.com/setup/build-numbers) (API level) the hook should be used
+* Setting *enabled* to *false* will switch the hook off (default *true*)
+* Setting *optional* to *true* will suppress error messages about the class or method not being found (default *false*)
+* Setting *usage* to *false* means that executing the hook will not be reported (default *true*)
+* Setting *notify* to *true* will result in showing notifications when the hook is applied (default *false*)
+* Settings (custom values) can be set using the pro companion app and read using *param:getSetting('setting_name1')*
+
+The pro companion app allows you to select which *collection*s of hooks XPrivacyLua should use.
+
+See [here](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html#wp276) about Java type signatures (names).
+
+The Lua script from the above definition without the JSON escapes looks like this:
+
+```Lua
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ param:setResult(null)
+ return true
+end
+```
+
+There should be a *before* and/or an *after* function, which will be executed before/after the hooked method is/has been executed.
+These functions always have exactly two parameters, in the example named *hook* and *param*.
+The *hook* parameter can be used to get meta information about the hook.
+The *param* parameter can be used to get or set the current arguments and the current result.
+The current arguments are typically modified *before* the hooked method is executed.
+The result can be set *before* the hooked method is executed,
+which will result in the actual method not executing (thus replacing the method),
+or can be set *after* the hooked method has been executed.
+
+The most important functions of *hook* are:
+
+* *hook:getName()*
+* *hook:getClassName()*
+* For other functions, see [here](https://github.com/M66B/XPrivacyLua/blob/master/app/src/main/java/eu/faircode/xlua/XHook.java) for the available public methods
+
+The most important functions of *param* are:
+
+* *param:getApplicationContext()*: see remarks below
+* *param:getThis()*: the current object instance or *nil* if the method is static
+* *param:getArgument(index)*: get the argument at the specified index (one based)
+* *param:setArgument(index, value)*
+* *param:getResult()*: only available after the hooked method has been executed
+* *param:setResult(value)*
+* For other functions, see [here](https://github.com/M66B/XPrivacyLua/blob/master/app/src/main/java/eu/faircode/xlua/XParam.java) for the available public methods
+
+The before/after function should return *true* when something was done and *false* otherwise.
+XPrivacyLua will show the last date/time of the last time *true* was returned.
+
+Special cases
+-------------
+
+You can hook into a constructor by omitting the method name.
+
+You can modify field values in an *after* function by prefixing the method name with a # character, for example:
+
+```JSON
+ "methodName": "#SERIAL"
+```
+
+Note that *final static* fields (constants) might be optimized 'away' at compile time,
+so setting such a field might not work because it doesn't exist at run time anymore.
+
+Another special case is hooking a method of a field using the syntax *[field name]:[method name]*, for example:
+
+```JSON
+ "methodName": "CREATOR:createFromParcel"
+```
+
+Remarks
+-------
+
+A common problem when developing an Xposed module is getting [a context](https://developer.android.com/reference/android/content/Context.html).
+With XPrivacyLua you'll never have to worry about this because you can simply get a context like this:
+
+```Lua
+ local context = param:getApplicationContext()
+```
+
+You can write to the Android logcat using the *log* function:
+
+```Lua
+ log('hello world')
+ log(some_object) -- will call toString()
+```
+
+A log line starts with the word *Log* followed by the package name and uid of the app and the name of the hook
+and ends with the log text. This way it is always clear which hook/app logging belongs to.
+
+An error in the definition, like class or method not found, or a compile time or run time error of/in the Lua script will result in a status bar notification.
+By tapping on the error notification you can navigate to the app settings where you can tap on the corresponding **!**-icon to see the details of the error.
+
+[LuaJ](http://www.luaj.org/luaj/3.0/README.html) globals are not thread safe.
+If you need global caching, you can use something like this:
+
+```Lua
+ local scope = param:getApplicationContext()
+ param:putValue(name, value, scope)
+ local value = param:getValue(name, scope)
+```
+
+Using the pro companion app you can edit built-in definitions, which will result in making a copy of the definition.
+You could for example enable usage notifications or change returned fake values.
+Deleting copied definitions will restore the built-in definitions.
+
+The pro companion app can upload defintions to and download definitions from a [hook definition repository](https://lua.xprivacy.eu/repo/),
+making it easy to share hook definitions with others and to use hook definitions provided by others.
+Note that you cannot upload hook definitions with the author name to set to someone else.
+
+You can find some example definitions [here](https://github.com/M66B/XPrivacyLua/tree/master/examples)
+and the definitions built into XPrivacyLua [here](https://github.com/M66B/XPrivacyLua/tree/master/app/src/main/assets).
+
+If you have questions, you can ask them in [this XDA thread](https://forum.xda-developers.com/xposed/modules/xposed-developing-module-t3741692).
diff --git a/FAQ.md b/FAQ.md
index 4cc8300b..ffb74539 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -1,15 +1,220 @@
XPrivacyLua
===========
-Frequently Asked Questions (FAQ)
---------------------------------
+Frequently Asked Questions
+--------------------------
-
+
**(1) How can I clear all data?**
-You can clear all data by uninstalling XPrivacyLua as the owner user.
+Primary users can clear all data of all users by uninstalling XPrivacyLua *while it is running*.
+Secondary users can clear their own data by uninstalling XPrivacyLua *while it is running*.
-
+All data is stored in the system folder */data/system/xlua* and can therefore not be backed up by regular backup apps.
+You can use the pro companion app to backup and restore all restrictions and settings.
+
+
**(2) Can I run XPrivacy and XPrivacyLua side by side?**
-Yes, you can, but be aware that both modules will apply the configured restrictions.
+Since XPrivacyLua is [in many aspects better](#user-content-faq7) than XPrivacy, running XPrivacy and XPrivacyLua side by side isn't supported.
+
+
+**(3) How can I fix 'Module not running or updated'?**
+
+This message means either that:
+
+* The Xposed framework is not running: check if Xposed is enabled and running in the Xposed installer app.
+* The XPrivacyLua module is not running: check if XPrivacyLua is enabled in the Xposed installer app and make sure you restarted your device (hard reboot) after installing/updating XPrivacyLua.
+* There is a problem with the XPrivacyLua service: check the Xposed log in the Xposed installer app for XPrivacyLua problems.
+
+Rebooting too soon after updating an Xposed module (before the Xposed installer shows the update notification) is known to cause problems.
+Disable and enable the module in the Xposed installer and hard reboot again to fix this problem.
+
+
+**(4) Can you add ...?**
+
+* *Network and storage restrictions*: access to the internet and to the device storage can only be prevented by revoking Linux permission from an app, which will often result in the app crashing. Therefore this will not be added.
+* *User interface features* like *templates*: I want to limit the time I put into this project and I want to keep things simple, so don't expect anything more than basic restriction management.
+* *On demand restricting*: It is not really possible to add on demand restricting so that it works stable and can be supported on the long term, so this will not be added. See also [here](https://forum.xda-developers.com/showpost.php?p=75419161&postcount=49). However, you can use *Notify on restriction* (a pro feature) in combination with restricting by default.
+* *App specific*: anything specific for an app will not be added.
+* *Security specific*: features related to security only will not be added.
+* *User choice*: if you can already control the data, like selecting an account, no restriction is needed.
+* *Crowd sourced restrictions*: there are not enough users for this to be useful.
+* *An app settings button*: see [here](https://forum.xda-developers.com/showpost.php?p=75745469&postcount=2071) why this won't be added.
+* *Select contacts groups to allow/block*: the Android contacts provider doesn't support contact groups at all hierarchy levels and working around this has appeared to be not possible reliably.
+
+If you want to confine apps to their own folder, you can download the hook definition *BlockGuardOs.open*
+from the [hook definition repository](https://lua.xprivacy.eu/repo/) using the pro companion app.
+
+Apps having access to the IP address generally have access to the internet and therefore can get your IP address in a simple way,
+see for example [here](https://www.privateinternetaccess.com/pages/whats-my-ip/). Therefore an IP address restriction doesn't make sense.
+
+Revoking internet permission will result in apps crashing
+and faking offline state doesn't prevent apps from accessing the internet.
+Therefore internet restriction cannot properly be implemented.
+You are adviced to use a firewall app to control internet access, for example [NetGuard](https://forum.xda-developers.com/android/apps-games/app-netguard-root-firewall-t3233012).
+
+If you still want to fake offline state, you can download the hook definition *NetworkInfo.createFromParcel*
+from the [hook definition repository](https://lua.xprivacy.eu/repo/) using the pro companion app.
+
+To protect your MAC address, there are hooks available in the [hook definition repository](https://lua.xprivacy.eu/repo/). On Android 8 and higher, you should only protect *NetworkInterface.getHardwareAddress*, since *WifiInfo.getMacAddress* and *BluetoothAdapter.getAddress* are [disabled](https://developer.android.com/training/articles/user-data-ids.html#version_specific_details_identifiers_in_m). However, some manufacturers may override this behavior, so hooks for all 3 methods are available.
+
+Since XPrivacyLua version 1.22 it is possible to enable status bar notifications on applying restrictions using the pro companion app.
+This can be used as a replacement for on demand restricting by removing a restriction when needed.
+
+You can ask for new restrictions, but you'll need to explain how it would improve your privacy as well.
+
+See also [question 7](#user-content-faq7).
+
+
+**(5) How can I fix 'There is a Problem Parsing the Package'?**
+
+This error could mean that the downloaded file is corrupt, which could for example be caused by a connection problem or by a virus scanner.
+
+It could also mean that you are trying to install XPrivacyLua on an unsupported Android version.
+See [here](https://github.com/M66B/XPrivacyLua/blob/master/README.md#compatibility) for which Android versions are supported.
+
+
+**(6) Why is a check box shown grey?**
+
+An app level check box is shown grey when one of the restriction level check boxes is not ticked.
+
+A restriction level check box is shown grey
+if one of the hooks that [compose the restriction](https://github.com/M66B/XPrivacyLua/blob/master/app/src/main/assets/hooks.json) is not enabled
+(hooks are not shown in the app to keep things simple).
+This can happen when a new version adds new hooks. These new hooks are not enabled by default to make sure your apps keeps working.
+You can enable the new hooks by toggling the check box once (turning it off and on once).
+
+
+**(7) How does XPrivacyLua compare to XPrivacy?**
+
+* XPrivacy supports Android 4.0.3 KitKat to Android 5.1.1 Lollipop and XPrivacyLua supports Android version 6.0 Marshmallow and later, see [here](https://github.com/M66B/XPrivacyLua/blob/master/README.md#compatibility) about compatibility
+* The user interface of XPrivacyLua is simpler than of XPrivacy, see also [question 4](#user-content-faq4)
+* The restrictions of XPrivacyLua are designed to prevent apps from crashing, while a number of XPrivacy restrictions can apps cause to crash, see also [question 4](#user-content-faq4)
+* XPrivacyLua has no on demand restricting for stability and maintenance reasons, see also [question 4](#user-content-faq4)
+* XPrivacyLua can unlike XPrivacy restrict analytics services like [Google Analytics](https://www.google.com/analytics/) and [Fabric/Crashlytics](https://get.fabric.io/)
+* XPrivacyLua [is user extensible](https://github.com/M66B/XPrivacyLua/blob/master/DEFINE.md) while XPrivacy is not
+* XPrivacyLua is easier to maintain than XPrivacy, so XPrivacyLua is easier to update for new Android versions
+
+In general XPrivacyLua and XPrivacy are comparable in protecting your privacy.
+For a detailed comparison with XPrivacy see [here](https://github.com/M66B/XPrivacyLua/blob/master/XPRIVACY.md).
+
+
+**(8) How can I define custom restrictions?**
+
+Yes, see [here](https://github.com/M66B/XPrivacyLua/blob/master/DEFINE.md) for the documentation on defining hooks.
+
+
+**(9) Why can an app still access my accounts?**
+
+If you see an app accessing the list of accounts while the accounts restriction is being applied,
+it is likely the Android account selector dialog you are seeing.
+The app will see only the account you actually select.
+
+
+**(10) Can applying a restriction let an app crash?**
+
+XPrivacyLua is designed to let apps not crash.
+However, sometimes an app will crash because of a restriction because there is a bug in the app.
+For example XPrivacyLua can return no data to an app while the app is not expecting this but should be prepared to handle this because the Android API documentation says this might happen.
+
+If you suspect that a restriction is causing a crash because there is a bug in the restriction, please provide a logcat and I will check the restriction.
+
+
+**(11) How can I filter on ...?**
+
+You can filter on restricted apps, on not restricted apps, on system apps and on user apps by typing special characters into the search field:
+
+* Filter on restricted apps: exclamation mark (!)
+* Filter on not restricted apps: question mark (?)
+* Filter on system apps: hash character (#)
+* Filter on user apps: at character (@)
+
+The special search characters should be the first characters in the search field (it is possible to combine special characters)
+and can be followed by additional characters to refine the search result.
+
+
+**(12) Can I get a discount / use an XPrivacy pro license to get the XPrivacyLua pro features?**
+
+XPrivacyLua was written from the ground up to support recent Android versions, which was really a lot of work.
+Since I believe everybody should be able to protect his/her privacy, XPrivacyLua is free to use.
+However, the XPrivacyLua pro features, mostly convencience and advanced features, not really needed to protect your privacy,
+need to be purchased and it is not possible to 'pay' with an XPrivacy pro license and there will be no discounts either.
+
+
+**(13) Will XPrivacyLua slow down my apps?**
+
+Depending on the number of applied restrictions, you might notice a slight delay when starting apps,
+but you will generally not notice delays when using apps.
+
+
+**(14) Will XPrivacyLua keep running after updating?**
+
+Yes, if XPrivacyLua was running before the update, it will keep running after the update,
+even though the user interface and new features will not be available until after a reboot.
+
+
+**(15) Can I get a refund?**
+
+If a purchased pro feature doesn't work properly
+and this isn't caused by a problem in the free features
+and I cannot fix the problem in a timely manner, you can get a refund.
+In all other cases there is no refund possible.
+In no circumstances there is a refund possible for any problem related to the free features, which include all restrictions,
+since there wasn't paid anything for them and because they can be evaluated without any limitation.
+I take my responsibility as seller to deliver what has been promised
+and I expect that you take responsibility for informing yourself of what you are buying.
+
+
+**(16) Can apps with root access be restricted?**
+
+Apps with root permissions can do whatever they like, so they can circumvent any restriction.
+So, be careful which apps you grant root permissions.
+There is no support on restricting apps with root access.
+
+
+**(17) Can I import my XPrivacy settings?**
+
+XPrivacy and XPrivacyLua work differently, so this is not possible.
+
+
+**(18) How do I selectively block/allow contacts?**
+
+This is a pro feature, which needs to be purchased.
+
+The pro companion app contacts settings are:
+
+* Block all: hide all contacts (the default)
+* Allow starred: make starred contacts visible
+* Allow not starred: make not starred contacts visible
+
+These settings can be applied to one app or globally to all apps.
+
+You'll need to apply the contacts restriction as well to apply these settings.
+
+You can star contacts (make contacts favorite) in the Android contacts app.
+Mostly the 'star' is in the upper right corner in the contact data.
+
+Due to limitations of the Android contacts provider it is not possible to block/allow contacts by contacts group in a reliable way.
+
+
+**(19) Why is import/export disabled (dimmed) ?**
+
+Assuming you purchased the pro features
+this will happen if the [Storage Access Framework](https://developer.android.com/guide/topics/providers/document-provider) is missing from your Android version.
+This is an important Android component to select files and folders.
+If you removed it yourself, you'll need to restore it, else you'll have to ask your ROM developer to add it.
+
+
+**(20) Why can some incoming SMSes not be restricted?**
+
+Likely because the app is using [this API](https://developers.google.com/identity/sms-retriever/request).
+The app will only see the content of verification SMSes intended for the app, so there is no restriction for this needed.
+
+Also, apps with permission to *receive* SMSes (in contrary to read SMSes) cannot be restricted.
+Normally, there is just one app that can receive SMSes, that should not be restricted, else no SMSes can be received anymore.
+If you really don't want an app to receive SMSes, you can revoke the SMS receive permission.
+
+
+
+If you have another question, you can use [this forum](https://forum.xda-developers.com/xposed/modules/xprivacylua6-0-android-privacy-manager-t3730663).
diff --git a/FUNDING.yml b/FUNDING.yml
new file mode 100644
index 00000000..6fea02da
--- /dev/null
+++ b/FUNDING.yml
@@ -0,0 +1 @@
+github: [M66B]
diff --git a/README.md b/README.md
index ee99aec3..82718b32 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,137 @@
XPrivacyLua
===========
-Android privacy manager
+
+Really simple to use privacy manager for Android 6.0 Marshmallow and later (successor of [XPrivacy](https://forum.xda-developers.com/xposed/modules/xprivacy-ultimate-android-privacy-app-t2320783"]XPrivacy[/URL])).
+
+Revoking Android permissions from apps often let apps crash or malfunction.
+XPrivacyLua solves this by feeding apps fake data instead of real data.
Features
--------
* Simple to use
+* Manage any user or system app
+* [Extensible](https://github.com/M66B/XPrivacyLua/blob/master/DEFINE.md)
+* Multi-user support
* Free and open source
+Restrictions
+------------
+
+* Determine activity (fake unknown activity, see [here](https://developers.google.com/location-context/activity-recognition/))
+* Get applications (hide installed apps and [widgets](https://developer.android.com/reference/android/appwidget/AppWidgetManager.html))
+* Get calendars (hide calendars)
+* Get call log (hide call log)
+* Get contacts (hide contacts with the pro option to allow (non) starred contacts, hide blocked numbers)
+* Get location (fake location, hide [NMEA](https://en.wikipedia.org/wiki/NMEA_0183) messages)
+* Get messages (hide MMS, SMS, SIM, voicemail)
+* Get sensors (hide all available sensors)
+* Read [account](https://developer.android.com/reference/android/accounts/Account.html) name (fake name, mostly e-mail address)
+* Read clipboard (fake paste)
+* Read identifiers (fake build serial number, Android ID, advertising ID, GSF ID)
+* Read notifications (fake [status bar notifications](https://developer.android.com/reference/android/service/notification/StatusBarNotification.html))
+* Read network data (hide cell info, Wi-Fi networks, fake Wi-Fi network name)
+* Read sync data (hide [sync data](https://developer.android.com/training/sync-adapters/creating-sync-adapter.html))
+* Read telephony data (fake IMEI, MEI, SIM serial number, voicemail number, etc)
+* Record audio (prevent recording)
+* Record video (prevent recording)
+* Send messages (prevent sending MMS, SMS, data)
+* Use analytics ([Fabric/Crashlytics](https://get.fabric.io/), [Facebook app events](https://developers.facebook.com/docs/reference/androidsdk/current/facebook/com/facebook/appevents/appeventslogger.html/), [Firebase Analytics](https://firebase.google.com/docs/analytics/), [Google Analytic](https://www.google.com/analytics/), [Mixpanel](https://mixpanel.com/), [Segment](https://segment.com/))
+* Use camera (fake camera not available and/or hide cameras)
+* Use tracking (fake user agent for [WebView](https://developer.android.com/reference/android/webkit/WebView.html) only, [Build properties](https://developer.android.com/reference/android/os/Build.html), network/SIM country/operator)
+
+The tracking restrictions will work only if the code of the target app was not [obfuscated](https://developer.android.com/studio/build/shrink-code.html).
+The other restrictions will work always.
+
+Hide or fake?
+
+* Hide: return empty list
+* Fake: return empty or fake value
+
+It is possible to add custom restriction definitions, see [this FAQ](https://github.com/M66B/XPrivacyLua/blob/master/FAQ.md#user-content-faq8) for details.
+
+You can see all technical details [here](https://github.com/M66B/XPrivacyLua/blob/master/app/src/main/assets/hooks.json).
+
+Notes
+-----
+
+* Some apps will start the camera app to take pictures. This cannot be restricted and there is no need for this, because only you can take pictures in this scenario, not the app.
+* Some apps will use [OpenSL ES for Android](https://developer.android.com/ndk/guides/audio/opensl-for-android.html) to record audio, an example is WhatsApp. Xposed cannot hook into native code, so this cannot be prevented.
+* The get applications restriction will not restrict getting information about individual apps for stability and performance reasons.
+* The telephony data restriction will result in apps seeing a fake IMEI. However, this doesn't change the IMEI address of your device.
+* Restricting activity recognition (location) results for recent Google Maps versions and possibly other apps in the error *... java.lang.ClassNotFoundException ...* for unknown reasons.
+
Compatibility
-------------
-XPrivacyLua is supported on Android 6 Marshmallow and later.
+XPrivacyLua is supported on Android 6.0 Marshmallow and later.
+For Android 4.0.3 KitKat to Android 5.1.1 Lollipop you can use [XPrivacy](https://github.com/M66B/XPrivacy/blob/master/README.md).
+
+XPrivacyLua was tested with the original Xposed framework only.
+
+Hooking *com.google.android.gms.location.ActivityRecognitionResult.extractResult* (restriction *Determine activity*)
+is known to fail with *script:25 vm error: java.lang.ClassNotFoundException: com.google.android.gms.location.DetectedActivity*
+and *script:28 attempt to call nil* for some apps, like Google Maps and NetFlix, for yet unknown reasons.
Installation
------------
-* Download and install the [Xposed framework](http://forum.xda-developers.com/xposed)
-* Download and install the [XPrivacyLua module](http://repo.xposed.info/module/eu.faircode.xlua)
+* Download, install and activate the [Xposed framework](http://forum.xda-developers.com/xposed)
+* Download, install and activate the [XPrivacyLua module](http://repo.xposed.info/module/eu.faircode.xlua)
+
+Certificate fingerprints:
+
+* MD5: 42:93:4F:A4:D5:AC:53:7B:04:97:3B:29:A6:6E:7B:B3
+* SHA1: 10:62:0A:E9:61:D7:88:54:F6:C9:CD:87:2C:43:88:23:28:49:C7:99
+* SHA256: 5E:69:9C:5D:AF:61:2C:AB:71:3A:35:BB:38:7C:F6:A8:86:8C:A0:DD:5D:CE:B4:CE:C1:53:8E:82:65:21:95:77
+
+Frequently Asked Questions
+--------------------------
+
+See [here](https://github.com/M66B/XPrivacyLua/blob/master/FAQ.md) for a list of often asked questions.
+
+Support
+-------
+
+Only the XPrivacyLua version released in the Xposed repository is supported.
+
+XPrivacyLua is supported with the original Xposed framework only.
+
+XPrivacyLua with [Island](http://forum.xda-developers.com/android/-t3366295) is not supported.
+
+* For support on Xposed, please go [here](http://forum.xda-developers.com/xposed)
+* For support on XPrivacyLua, please go [here](https://forum.xda-developers.com/xposed/modules/xprivacylua6-0-android-privacy-manager-t3730663)
Donations
---------
See [here](https://lua.xprivacy.eu/) about how you can donate.
+Contributing
+------------
+
+*Documentation*
+
+Contributions to this document and the frequently asked questions
+are prefered in the form of [pull requests](https://help.github.com/articles/creating-a-pull-request/).
+
+*Translations*
+
+* You can translate the in-app texts of XPrivacyLua [here](https://crowdin.com/project/xprivacylua/)
+* You can download the in-app texts of XPrivacyLua Pro for translation [here](https://lua.xprivacy.eu/strings_pro.xml)
+* If your language is not listed, please send a message through [this contact form](https://contact.faircode.eu/)
+
+*Source code*
+
+Building XPrivacyLua from source code is straightforward with [Android Studio](http://developer.android.com/sdk/).
+It is expected that you can solve build problems yourself, so there is no support on building.
+
+Source code contributions are prefered in the form of [pull requests](https://help.github.com/articles/creating-a-pull-request/).
+Please [contact me](https://contact.faircode.eu/) first to tell me what your plans are.
+
+Please note that you agree to the license below by contributing, including the copyright.
+
Attribution
-----------
diff --git a/XPRIVACY.md b/XPRIVACY.md
new file mode 100644
index 00000000..f0ba8e15
--- /dev/null
+++ b/XPRIVACY.md
@@ -0,0 +1,242 @@
+Comparison with XPrivacy
+========================
+
+The list below is [taken from](https://github.com/M66B/XPrivacy#restrictions) the XPrivacy documentation.
+
+* **Bold** means that XPrivacyLua supports the restriction
+* ~~Strike through~~ means that XPrivacyLua won't support the restriction
+
+Before asking questions, please read [this FAQ](https://github.com/M66B/XPrivacyLua/blob/master/FAQ.md#user-content-faq4).
+
+
+* Accounts
+ * ~~return an empty account list~~ see remark below
+ * ~~return an empty account type list~~ see remark below
+ * **return fake account info**
+ * ~~return empty authorization tokens~~ user choice
+ * **return an empty list of synchronizations**
+
+Since account info can be faked, it is not really necessary to hide the account list, which can also cause apps to crash.
+
+
+* Browser
+ * ~~return an empty bookmark list~~ see remark below
+ * ~~return an empty download list~~ see remark below
+ * ~~return empty search history~~ see remark below
+
+Different browsers (stock, Chrome, Firefox, etc) have different content providers, so this is app specific.
+Browser data is generally not accessible on recent Android versions anymore.
+
+
+* Calendar
+ * **return an empty calendar**
+
+
+* Calling
+ * ~~prevent calls from being placed~~ user choice
+ * ~~prevent SIP calls from being placed~~ user choice
+ * **prevent SMS messages from being sent**
+ * **prevent MMS messages from being sent**
+ * **prevent data messages from being sent**
+ * **return an empty call log**
+
+
+* Clipboard
+ * **prevent paste from clipboard (both manual and from an application)**
+
+
+* Contacts
+ * **return an empty contact list**
+ * **content://com.android.contacts**
+ * **content://com.android.contacts/contacts**
+ * **content://com.android.contacts/data**
+ * **content://com.android.contacts/phone_lookup**
+ * **content://com.android.contacts/profile**
+ * **SIM card**
+
+
+* Dictionary
+ * ~~return an empty user dictionary~~ not privacy related
+
+
+* E-mail
+ * ~~return an empty list of accounts, e-mails, etc (standard)~~ see remark below
+ * ~~return an empty list of accounts, e-mails, etc (Gmail)~~ see remark below
+
+Information about e-mail accounts and messages depends on the installed e-mail app and is therefore app specific.
+E-mail data is generally not accessible on recent Android versions anymore.
+
+
+* Identification
+ * **return a fake Android ID**
+ * **return a fake device serial number**
+ * ~~return a fake host name~~ not available on recent Android versions anymore
+ * **return a fake Google services framework ID**
+ * ~~return file not found for folder [/proc](http://linux.die.net/man/5/proc)~~ will result in crashes
+ * **return a fake Google advertising ID**
+ * ~~return a fake system property CID (Card Identification Register = SD-card serial number)~~ tracking related
+ * ~~return file not found for /sys/block/.../cid~~ will result in crashes
+ * ~~return file not found for /sys/class/.../cid~~ will result in crashes
+ * ~~return a fake input device descriptor~~ tracking related
+ * ~~return a fake USB ID/name/number~~ tracking related
+ * ~~return a fake Cast device ID / IP address~~ tracking related
+
+
+* Internet
+ * ~~revoke permission to internet access~~ will result in crashes
+ * ~~revoke permission to internet administration~~ will result in crashes
+ * ~~revoke permission to internet bandwidth statistics/administration~~ will result in crashes
+ * ~~revoke permission to [VPN](http://en.wikipedia.org/wiki/Vpn) services~~ will result in crashes
+ * ~~revoke permission to [Mesh networking](http://en.wikipedia.org/wiki/Mesh_networking) services~~ will result in crashes
+ * **return fake extra info**
+ * ~~return fake disconnected state~~ not privacy related
+ * ~~return fake supplicant disconnected state~~ not privacy related
+
+If you want to fake offline state, see [the example definitions](https://github.com/M66B/XPrivacyLua/tree/master/examples) about how this can be done with a custom restriction definition.
+
+
+* IPC
+ * ~~Binder~~ will result in crashes
+ * ~~Reflection~~ will result in crashes
+
+
+* Location
+ * **return a random or set location (also for Google Play services)**
+ * **return empty cell location**
+ * **return an empty list of (neighboring) cell info**
+ * **prevents geofences from being set (also for Google Play services)**
+ * **prevents proximity alerts from being set**
+ * **prevents sending NMEA data to an application**
+ * **prevent phone state from being sent to an application**
+ * **Cell info changed**
+ * **Cell location changed**
+ * ~~prevent sending extra commands (aGPS data)~~ not privacy related
+ * **return an empty list of Wi-Fi scan results**
+ * **prevent [activity recognition](http://developer.android.com/training/location/activity-recognition.html)**
+
+
+* Media
+ * **prevent recording audio**
+ * **prevent taking pictures**
+ * **prevent recording video**
+ * **you will be notified if an application tries to perform any of these actions**
+
+
+* Messages
+ * **return an empty SMS/MMS message list**
+ * **return an empty list of SMS messages stored on the SIM (ICC SMS)**
+ * **return an empty list of voicemail messages**
+
+
+* Network
+ * ~~return fake IP's~~ see [this FAQ](https://github.com/M66B/XPrivacyLua/blob/master/FAQ.md#user-content-faq4)
+ * ~~return fake MAC's (network, Wi-Fi, bluetooth)~~ see remark below
+ * **return fake BSSID/SSID**
+ * **return an empty list of Wi-Fi scan results**
+ * **return an empty list of configured Wi-Fi networks**
+ * ~~return an empty list of bluetooth adapters/devices~~ tracking related
+
+MAC addresses are [not available anymore](https://developer.android.com/training/articles/user-data-ids.html#version_specific_details_identifiers_in_m) on supported Android versions.
+
+
+* NFC
+ * ~~prevent receiving NFC adapter state changes~~ user choice
+ * ~~prevent receiving NDEF discovered~~ user choice
+ * ~~prevent receiving TAG discovered~~ user choice
+ * ~~prevent receiving TECH discovered~~ user choice
+
+
+* Notifications
+ * **prevent applications from receiving [statusbar notifications](https://developer.android.com/reference/android/service/notification/NotificationListenerService.html) (Android 4.3+)**
+ * ~~prevent [C2DM](https://developers.google.com/android/c2dm/) messages~~ use a firewall and block **xxx.mtalk.google.com:yyy** for Google Play services
+
+
+* Overlay
+ * ~~prevent draw over / on top~~ will result in [crashes](https://github.com/M66B/XPrivacy/issues/2374)
+
+
+* Phone
+ * **return a fake own/in/outgoing/voicemail number**
+ * **return a fake subscriber ID (IMSI for a GSM phone)**
+ * **return a fake phone device ID (IMEI): 000000000000000**
+ * ~~return a fake phone type: GSM (matching IMEI)~~ not privacy related
+ * ~~return a fake network type: unknown~~ not privacy related
+ * ~~return an empty ISIM domain~~ not available in user space
+ * **return an empty IMPI/IMPU**
+ * **return a fake MSISDN**
+ * return fake mobile network info
+ * Country: XX
+ * Operator: 00101 (test network)
+ * Operator name: fake
+ * return fake SIM info
+ * Country: XX
+ * Operator: 00101
+ * Operator name: faket
+ * **Serial number (ICCID): fake**
+ * ~~return empty [APN](http://en.wikipedia.org/wiki/Access_Point_Name) list~~ not privacy related
+ * ~~return no currently used APN~~ not privacy related
+ * ~~prevent phone state from being sent to an application~~ not privacy related
+ * ~~Call forwarding indication~~
+ * ~~Call state changed (ringing, off-hook)~~
+ * ~~Mobile data connection state change / being used~~
+ * ~~Message waiting indication~~
+ * ~~Service state changed (service/no service)~~
+ * ~~Signal level changed~~
+ * **return an empty group identifier level 1**
+
+
+* Sensors
+ * **return an empty default sensor**
+ * **return an empty list of sensors**
+ * restrict individual sensors: might be implemented as pro feature
+ * acceleration
+ * gravity
+ * heartrate
+ * humidity
+ * light
+ * magnetic
+ * motion
+ * orientation
+ * pressure
+ * proximity
+ * rotation
+ * step
+ * temperature
+
+
+* Shell
+ * ~~return I/O exception for Linux shell~~ will result in crashes
+ * ~~return I/O exception for Superuser shell~~ will result in crashes
+ * ~~return unsatisfied link error for load/loadLibrary~~ will result in crashes
+
+
+* Storage
+ * ~~revoke permission to the [media storage](http://www.doubleencore.com/2014/03/android-external-storage/)~~ will result in crashes
+ * ~~revoke permission to the external storage (SD-card)~~ will result in crashes
+ * ~~revoke permission to [MTP](http://en.wikipedia.org/wiki/Media_Transfer_Protocol)~~ will result in crashes
+ * ~~return fake unmounted state~~ not privacy related
+ * ~~prevent access to provided assets (media, etc.)~~ will result in crashes
+
+The supported Android versions provide the [Storage Access Framework](https://developer.android.com/guide/topics/providers/document-provider.html),
+which apps should use to open files instead of opening files directly.
+Moreover, the supported Android versions provide [runtime permissions](https://developer.android.com/training/permissions/requesting.html),
+so you can always choose to not grant storage permission to an app.
+
+If you want to confine apps to their own folder, see [the example definitions](https://github.com/M66B/XPrivacyLua/tree/master/examples) about how this can be done with a custom restriction definition.
+
+
+* System
+ * **return an empty list of installed applications**
+ * **return an empty list of recent tasks**
+ * **return an empty list of running processes**
+ * **return an empty list of running services**
+ * **return an empty list of running tasks**
+ * **return an empty list of widgets**
+ * ~~return an empty list of applications (provider)~~ not available on recent Android versions anymore
+ * **prevent package add, replace, restart, and remove notifications**
+
+
+* View
+ * ~~prevent links from opening in the browser~~ not privacy related
+ * return fake browser user agent string (WebView)
+ * *Mozilla/5.0 (Linux; U; Android; en-us) AppleWebKit/999+ (KHTML, like Gecko) Safari/999.9*
diff --git a/app/build.gradle b/app/build.gradle
index 694147c0..72072690 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,25 +1,40 @@
apply plugin: 'com.android.application'
+def keystorePropertiesFile = rootProject.file("keystore.properties")
+def keystoreProperties = new Properties()
+
+keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+
android {
- compileSdkVersion 27
- buildToolsVersion "27.0.3"
+ compileSdkVersion 29
defaultConfig {
applicationId "eu.faircode.xlua"
minSdkVersion 23
- targetSdkVersion 27
- versionCode 2
- versionName "0.2"
- archivesBaseName = "XPrivacyLua-v$versionName"
+ targetSdkVersion 29
+ versionCode 128
+ versionName "1.27"
+ archivesBaseName = "XPrivacyLua-v$versionName-$versionCode"
+ }
+ signingConfigs {
+ release {
+ storeFile file(keystoreProperties['storeFile'])
+ storePassword keystoreProperties['storePassword']
+ keyAlias keystoreProperties['keyAlias']
+ keyPassword keystoreProperties['keyPassword']
+ }
}
buildTypes {
release {
+ shrinkResources true
minifyEnabled true
useProguard = true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ signingConfig signingConfigs.release
}
debug {
+ shrinkResources false
minifyEnabled false
useProguard = false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
@@ -28,22 +43,41 @@ android {
}
dependencies {
+ def appcompat_version = "1.0.2"
+ def recyclerview_version = "1.0.0"
+ def constraintlayout_version = "1.1.3"
+ def material_version = "1.0.0"
+
implementation fileTree(dir: 'libs', include: ['*.jar'])
- // https://developer.android.com/topic/libraries/support-library/revisions.html
- implementation 'com.android.support:appcompat-v7:27.+'
- implementation 'com.android.support.constraint:constraint-layout:1.1.+'
- implementation 'com.android.support:recyclerview-v7:27.+'
- implementation 'com.android.support:design:27.+'
+ // https://mvnrepository.com/artifact/androidx.appcompat/appcompat
+ implementation "androidx.appcompat:appcompat:$appcompat_version"
+
+ // https://mvnrepository.com/artifact/androidx.recyclerview/recyclerview
+ implementation "androidx.recyclerview:recyclerview:$recyclerview_version"
+ //implementation "androidx.recyclerview:recyclerview-selection:$recyclerview_version"
+
+ // https://mvnrepository.com/artifact/androidx.constraintlayout/constraintlayout
+ implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
+
+ // https://mvnrepository.com/artifact/com.google.android.material/material
+ implementation "com.google.android.material:material:$material_version"
// https://bumptech.github.io/glide/
- implementation 'com.github.bumptech.glide:glide:4.4.0'
- annotationProcessor 'com.github.bumptech.glide:compiler:4.4.0'
+ // https://mvnrepository.com/artifact/com.github.bumptech.glide/glide
+ // { exclude group: "com.android.support" }
+ implementation('com.github.bumptech.glide:glide:4.9.0') {
+ exclude group: "com.android.support"
+ }
+ annotationProcessor 'androidx.annotation:annotation:1.1.0'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
// https://github.com/rovo89/XposedBridge/wiki/Using-the-Xposed-Framework-API
// https://bintray.com/rovo89/de.robv.android.xposed/api
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
- implementation 'org.luaj:luaj-jse:3.0.1'
+ // http://www.luaj.org/luaj/3.0/README.html
+ // https://mvnrepository.com/artifact/org.luaj/luaj-jse
+ // implementation 'org.luaj:luaj-jse:3.0.1'
}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 341d71b8..5bdabdd3 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -21,15 +21,19 @@
-renamesourcefileattribute SourceFile
#XPrivacyLua
--keep class eu.faircode.xlua.Xposed {*; }
+-keep class eu.faircode.xlua.XLua {*; }
+-keep class eu.faircode.xlua.XHook {*; }
-keep class eu.faircode.xlua.XParam {*; }
+-keepnames class eu.faircode.xlua.** {*; }
#LuaJ
-dontwarn org.luaj.vm2.**
+-keepnames class org.luaj.vm2.** {*; }
#Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
+-keep class com.bumptech.glide.GeneratedAppGlideModuleImpl
-keep enum com.bumptech.glide.** {*; }
#Support library
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 07dc21de..adff31ba 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,12 +3,15 @@
package="eu.faircode.xlua">
+ android:theme="@style/AppThemeLight"
+ android:largeHeap="true">
+
+
diff --git a/app/src/main/XPrivacyLua_logo.png b/app/src/main/XPrivacyLua_logo.png
new file mode 100644
index 00000000..774cdf0a
Binary files /dev/null and b/app/src/main/XPrivacyLua_logo.png differ
diff --git a/app/src/main/aidl/eu/faircode/xlua/IEventListener.aidl b/app/src/main/aidl/eu/faircode/xlua/IEventListener.aidl
deleted file mode 100644
index 8375d482..00000000
--- a/app/src/main/aidl/eu/faircode/xlua/IEventListener.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- This file is part of XPrivacy/Lua.
-
- XPrivacy/Lua is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- XPrivacy/Lua is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
-
- Copyright 2017-2018 Marcel Bokhorst (M66B)
- */
-
-package eu.faircode.xlua;
-
-interface IEventListener {
- oneway void usageDataChanged();
-}
diff --git a/app/src/main/aidl/eu/faircode/xlua/IService.aidl b/app/src/main/aidl/eu/faircode/xlua/IService.aidl
deleted file mode 100644
index 8a5194dc..00000000
--- a/app/src/main/aidl/eu/faircode/xlua/IService.aidl
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- This file is part of XPrivacy/Lua.
-
- XPrivacy/Lua is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- XPrivacy/Lua is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
-
- Copyright 2017-2018 Marcel Bokhorst (M66B)
- */
-
-package eu.faircode.xlua;
-
-import eu.faircode.xlua.XHook;
-import eu.faircode.xlua.XApp;
-import eu.faircode.xlua.IEventListener;
-
-interface IService {
- int getVersion();
-
- List getHooks();
- List getApps();
- oneway void assignHooks(in List hookid, String packageName, int uid, boolean delete, boolean kill);
- List getAssignedHooks(String packageName, int uid);
-
- oneway void registerEventListener(IEventListener listener);
- oneway void unregisterEventListener(IEventListener listener);
-
- oneway void report(String hookid, String packageName, int uid, String event, in Bundle data);
-
- String getSetting(int userid, String category, String name);
- oneway void putSetting(int userid, String category, String name, String value);
-
- oneway void clearData();
-}
diff --git a/app/src/main/aidl/eu/faircode/xlua/XApp.aidl b/app/src/main/aidl/eu/faircode/xlua/XApp.aidl
deleted file mode 100644
index 650e8d20..00000000
--- a/app/src/main/aidl/eu/faircode/xlua/XApp.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- This file is part of XPrivacy/Lua.
-
- XPrivacy/Lua is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- XPrivacy/Lua is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
-
- Copyright 2017-2018 Marcel Bokhorst (M66B)
- */
-
-package eu.faircode.xlua;
-
-parcelable XApp;
diff --git a/app/src/main/aidl/eu/faircode/xlua/XHook.aidl b/app/src/main/aidl/eu/faircode/xlua/XHook.aidl
deleted file mode 100644
index bf89b8b4..00000000
--- a/app/src/main/aidl/eu/faircode/xlua/XHook.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- This file is part of XPrivacy/Lua.
-
- XPrivacy/Lua is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- XPrivacy/Lua is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
-
- Copyright 2017-2018 Marcel Bokhorst (M66B)
- */
-
-package eu.faircode.xlua;
-
-parcelable XHook;
diff --git a/app/src/main/assets/account_createfromparcel.lua b/app/src/main/assets/account_createfromparcel.lua
new file mode 100644
index 00000000..e04215f0
--- /dev/null
+++ b/app/src/main/assets/account_createfromparcel.lua
@@ -0,0 +1,51 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local clsAm = luajava.bindClass('android.accounts.AccountManager')
+ local am = clsAm:get(param:getApplicationContext())
+ local auths = am:getAuthenticatorTypes()
+
+ local restricted = true
+ local packageName = param:getPackageName()
+ for index = 1, auths['length'] do
+ local auth = auths[index]
+ if result.type == auth.type and auth.packageName == packageName then
+ restricted = false
+ break
+ end
+ end
+
+ --log((restricted and 'Restricted' or 'Allowed') .. ' account ' .. result.type .. '/' .. result.name)
+ if restricted then
+ local old = result.name
+ local fake = param:getSetting('value.email')
+ if fake == nil then
+ result.name = 'private@lua.xprivacy.eu'
+ else
+ result.name = fake
+ end
+ return true, old, fake
+ else
+ return false
+ end
+end
diff --git a/app/src/main/assets/activityrecognitionresult_extractresult.lua b/app/src/main/assets/activityrecognitionresult_extractresult.lua
new file mode 100644
index 00000000..65cc45b5
--- /dev/null
+++ b/app/src/main/assets/activityrecognitionresult_extractresult.lua
@@ -0,0 +1,33 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local classActivity = luajava.bindClass('com.google.android.gms.location.DetectedActivity')
+ local detected = luajava.new(classActivity, 4, 100) -- unknown, 100%
+ local classResult = luajava.bindClass('com.google.android.gms.location.ActivityRecognitionResult')
+ local time = result:getTime()
+ local elapsed = result:getElapsedRealtimeMillis()
+ local fake = luajava.new(classResult, detected, time, elapsed)
+ param:setResult(fake)
+ return true, result:toString(), fake:toString()
+end
diff --git a/app/src/main/assets/advertisingidclient$info_getid.lua b/app/src/main/assets/advertisingidclient$info_getid.lua
new file mode 100644
index 00000000..9f2ec35d
--- /dev/null
+++ b/app/src/main/assets/advertisingidclient$info_getid.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake = '00000000-0000-0000-0000-000000000000'
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/audiomanager_getdevices.lua b/app/src/main/assets/audiomanager_getdevices.lua
new file mode 100644
index 00000000..93a4423b
--- /dev/null
+++ b/app/src/main/assets/audiomanager_getdevices.lua
@@ -0,0 +1,29 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local array = param:getResult()
+ if array == nil or array.length == 0 then
+ return false
+ end
+
+ local stringClass = luajava.bindClass('android.media.AudioDeviceInfo')
+ local arrayClass = luajava.bindClass('java.lang.reflect.Array')
+ local fake = arrayClass:newInstance(stringClass, 0)
+ param:setResult(fake)
+ return true
+end
diff --git a/app/src/main/assets/bundle_get_location.lua b/app/src/main/assets/bundle_get_location.lua
index af9b524f..d1d52860 100644
--- a/app/src/main/assets/bundle_get_location.lua
+++ b/app/src/main/assets/bundle_get_location.lua
@@ -1,29 +1,34 @@
--- This file is part of XPrivacy/Lua.
+-- This file is part of XPrivacyLua.
--- XPrivacy/Lua is free software: you can redistribute it and/or modify
+-- XPrivacyLua is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--- XPrivacy/Lua is distributed in the hope that it will be useful,
+-- XPrivacyLua is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- You should have received a copy of the GNU General Public License
--- along with XPrivacy/Lua. If not, see .
+-- along with XPrivacyLua. If not, see .
--- Copyright 2017-2018 Marcel Bokhorst (M66B)
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
function after(hook, param)
- key = param:getArg(0)
- if key == 'location' then
- loc = luajava.newInstance('android.location.Location', 'privacy')
- loc:setLatitude(0)
- loc:setLongitude(0)
- param:setResult(loc)
- return true
- else
+ local result = param:getResult()
+ if param:getException() ~= nil or result == nil then
return false
end
+
+ local key = param:getArgument(0)
+ if key ~= 'location' then
+ return false
+ end
+
+ local fake = luajava.newInstance('android.location.Location', 'privacy')
+ fake:setLatitude(0)
+ fake:setLongitude(0)
+ param:setResult(fake)
+ return true, result:toString(), fake:toString()
end
diff --git a/app/src/main/assets/calllog_query.lua b/app/src/main/assets/calllog_query.lua
new file mode 100644
index 00000000..e8a43c8e
--- /dev/null
+++ b/app/src/main/assets/calllog_query.lua
@@ -0,0 +1,35 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ local uri = param:getArgument(0)
+ if uri == nil then
+ return false
+ end
+
+ local authority = uri:getAuthority()
+
+ if 'call_log' or authority == 'call_log_shadow' then
+
+ local prefix = string.gmatch(path, '[^/]+')()
+ if prefix == 'provider_status' then
+ return false
+ end
+
+ return false
+ end
+end
\ No newline at end of file
diff --git a/app/src/main/assets/camera2_open.lua b/app/src/main/assets/camera2_open.lua
new file mode 100644
index 00000000..2bb801e0
--- /dev/null
+++ b/app/src/main/assets/camera2_open.lua
@@ -0,0 +1,23 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ local exception = luajava.bindClass('android.hardware.camera2.CameraAccessException')
+ local fake = luajava.new(exception, 1, 'privacy') -- 1=disabled
+ param:setResult(fake)
+ return true
+end
diff --git a/app/src/main/assets/mediarecorder_setaudiosource.lua b/app/src/main/assets/camera_open.lua
similarity index 53%
rename from app/src/main/assets/mediarecorder_setaudiosource.lua
rename to app/src/main/assets/camera_open.lua
index eee30bef..03d73878 100644
--- a/app/src/main/assets/mediarecorder_setaudiosource.lua
+++ b/app/src/main/assets/camera_open.lua
@@ -1,22 +1,22 @@
--- This file is part of XPrivacy/Lua.
+-- This file is part of XPrivacyLua.
--- XPrivacy/Lua is free software: you can redistribute it and/or modify
+-- XPrivacyLua is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--- XPrivacy/Lua is distributed in the hope that it will be useful,
+-- XPrivacyLua is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- You should have received a copy of the GNU General Public License
--- along with XPrivacy/Lua. If not, see .
+-- along with XPrivacyLua. If not, see .
--- Copyright 2017-2018 Marcel Bokhorst (M66B)
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
function before(hook, param)
- source = param:getArg(0)
- param:putValue('source', source)
- return false
+ local fake = luajava.newInstance('java.lang.RuntimeException', 'privacy')
+ param:setResult(fake)
+ return true
end
diff --git a/app/src/main/assets/clipdata_createfromparcel.lua b/app/src/main/assets/clipdata_createfromparcel.lua
new file mode 100644
index 00000000..9176ce3f
--- /dev/null
+++ b/app/src/main/assets/clipdata_createfromparcel.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == null or result:getItemCount() == 0 then
+ return false
+ end
+
+ local fake = result:newPlainText('XPrivacyLua', 'Private')
+ param:setResult(fake)
+ return true
+end
diff --git a/app/src/main/assets/configuration_createfromparcel.lua b/app/src/main/assets/configuration_createfromparcel.lua
new file mode 100644
index 00000000..a2afd892
--- /dev/null
+++ b/app/src/main/assets/configuration_createfromparcel.lua
@@ -0,0 +1,23 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local configuration = param:getResult()
+ configuration.mcc = 0
+ configuration.mnc = 0
+ return true
+end
\ No newline at end of file
diff --git a/app/src/main/assets/contentresolver_query.lua b/app/src/main/assets/contentresolver_query.lua
new file mode 100644
index 00000000..599d2783
--- /dev/null
+++ b/app/src/main/assets/contentresolver_query.lua
@@ -0,0 +1,181 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ local uri = param:getArgument(0)
+ if uri == nil then
+ return false
+ end
+
+ local path = uri:getPath()
+ if path == nil then
+ return false
+ end
+
+ local h = hook:getName()
+ local match = string.gmatch(h, '[^/]+')
+ local func = match()
+ local name = match()
+ local authority = uri:getAuthority()
+ if name == 'contacts' and authority == 'com.android.contacts' then
+ local prefix = string.gmatch(path, '[^/]+')()
+ if prefix == 'provider_status' then
+ return false
+ end
+
+ local starred = param:getSetting('contacts.starred')
+ if starred ~= nil and
+ (prefix == 'contacts' or prefix == 'raw_contacts' or prefix == 'data') then
+ local where
+ if func == 'ContentResolver.query26' then
+ local bundle = param:getArgument(2)
+ if bundle == nil then
+ where = nil
+ else
+ where = bundle:getString('android:query-arg-sql-selection')
+ end
+ else
+ where = param:getArgument(2)
+ end
+
+ if where == nil or where == '' then
+ where = '(starred = ' .. starred .. ')'
+ else
+ where = '(starred = ' .. starred .. ') AND (' .. where .. ')'
+ end
+
+ if func == 'ContentResolver.query26' then
+ param:getArgument(2):putString('android:query-arg-sql-selection', where)
+ else
+ param:setArgument(2, where)
+ end
+
+ if false then
+ local args
+ if func == 'ContentResolver.query26' then
+ local bundle = param:getArgument(2)
+ if bundle == nil then
+ args = nil
+ else
+ args = bundle:getStringArray('android:query-arg-sql-selection-args')
+ end
+ else
+ args = param:getArgument(3)
+ end
+
+ local line = path .. ' where ' .. where .. ' ('
+ if args ~= nil then
+ local index
+ for index = 1, args['length'] do
+ line = line .. ' ' .. args[index]
+ end
+ end
+ line = line .. ')'
+ log(line)
+ end
+ end
+ end
+
+ return false
+end
+
+function after(hook, param)
+ local uri = param:getArgument(0)
+ local cursor = param:getResult()
+ if uri == nil or cursor == nil then
+ return false
+ end
+
+ local h = hook:getName()
+ local match = string.gmatch(h, '[^/]+')
+ local func = match()
+ local name = match()
+ local authority = uri:getAuthority()
+
+ if (name == 'blockednumber' and authority == 'com.android.blockednumber') or
+ (name == 'calendars' and authority == 'com.android.calendar') or
+ (name == 'call_log' and authority == 'call_log') or
+ (name == 'call_log' and authority == 'call_log_shadow') or
+ (name == 'contacts' and authority == 'icc') or
+ (name == 'contacts' and authority == 'com.android.contacts') or
+ (name == 'gsf_id' and authority == 'com.google.android.gsf.gservices') or
+ (name == 'mmssms' and authority == 'mms') or
+ (name == 'mmssms' and authority == 'sms') or
+ (name == 'mmssms' and authority == 'mms-sms') or
+ (name == 'mmssms' and authority == 'com.google.android.apps.messaging.shared.datamodel.BugleContentProvider') or
+ (name == 'voicemail' and authority == 'com.android.voicemail') then
+
+ if name == 'contacts' and authority == 'com.android.contacts' then
+ local path = uri:getPath()
+ if path == nil then
+ return false
+ end
+
+ local prefix = string.gmatch(path, '[^/]+')()
+ if prefix == 'provider_status' then
+ return false
+ end
+
+ local starred = param:getSetting('contacts.starred')
+ if starred ~= nil and
+ (prefix == 'contacts' or prefix == 'raw_contacts' or prefix == 'data') then
+ return true
+ end
+ end
+
+ if name == 'gsf_id' then
+ local args
+ if func == 'ContentResolver.query26' then
+ local bundle = param:getArgument(2)
+ if bundle == nil then
+ return false
+ end
+ args = bundle:getStringArray('android:query-arg-sql-selection-args')
+ else
+ args = param:getArgument(3)
+ end
+ if args == nil then
+ return false
+ end
+
+ local found = false
+ local index
+ for index = 1, args['length'] do
+ if args[index] == 'android_id' then
+ found = true
+ break
+ end
+ end
+
+ if not found then
+ return false
+ end
+ end
+
+ local fake = luajava.newInstance('android.database.MatrixCursor', cursor:getColumnNames())
+ --fake:setExtras(cursor:getExtras())
+ --notify = cursor:getNotificationUri()
+ --if notify ~= nil then
+ -- fake:setNotificationUri(param:getThis(), notify)
+ --end
+
+ param:setResult(fake);
+ return true
+ else
+ return false
+ end
+end
\ No newline at end of file
diff --git a/app/src/main/assets/fabric_with_kits.lua b/app/src/main/assets/fabric_with_kits.lua
new file mode 100644
index 00000000..38c3981e
--- /dev/null
+++ b/app/src/main/assets/fabric_with_kits.lua
@@ -0,0 +1,45 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local kits = param:getArgument(1)
+ if kits == nil then
+ return false
+ end
+
+ for index = 1, kits['length'] do
+ local kit = kits[index]
+ if kit ~= nil and kit.getIdentifier ~= nil and type(kit.getIdentifier) == 'function' then
+ local identifier = kit:getIdentifier()
+ log(identifier)
+ if identifier == 'com.crashlytics.sdk.android:crashlytics' then
+ log(kit)
+ if kit.core ~= nil and kit.core.disabled ~= nil then
+ kit.core.disabled = true
+ return true
+ elseif kit.disabled ~= nil then
+ kit.disabled = true
+ return true
+ else
+ log('Crashlytics not disabled')
+ end
+ end
+ end
+ end
+
+ return false
+end
diff --git a/app/src/main/assets/firebase_getinstance.lua b/app/src/main/assets/firebase_getinstance.lua
new file mode 100644
index 00000000..903daad2
--- /dev/null
+++ b/app/src/main/assets/firebase_getinstance.lua
@@ -0,0 +1,28 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil or
+ result.setAnalyticsCollectionEnabled == nil or
+ type(result.setAnalyticsCollectionEnabled) ~= 'function' then
+ return false
+ end
+
+ result:setAnalyticsCollectionEnabled(false)
+ return true
+end
diff --git a/app/src/main/assets/firebase_setenabled.lua b/app/src/main/assets/firebase_setenabled.lua
new file mode 100644
index 00000000..c0fc86af
--- /dev/null
+++ b/app/src/main/assets/firebase_setenabled.lua
@@ -0,0 +1,24 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ local enabled = param:getArgument(0)
+ if enabled then
+ param:setArgument(0, false)
+ end
+ return enabled
+end
diff --git a/app/src/main/assets/ga_getinstance.lua b/app/src/main/assets/ga_getinstance.lua
new file mode 100644
index 00000000..5080d739
--- /dev/null
+++ b/app/src/main/assets/ga_getinstance.lua
@@ -0,0 +1,32 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil or
+ result.isDryRunEnabled == nil or type(result.isDryRunEnabled) ~= 'function' or
+ result.setDryRun == nil or type(result.setDryRun) ~= 'function' then
+ return false
+ end
+
+ if result:isDryRunEnabled() then
+ return false
+ else
+ result:setDryRun(true)
+ return true
+ end
+end
diff --git a/app/src/main/assets/ga_setdryrun.lua b/app/src/main/assets/ga_setdryrun.lua
new file mode 100644
index 00000000..34dfb67d
--- /dev/null
+++ b/app/src/main/assets/ga_setdryrun.lua
@@ -0,0 +1,24 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ local enable = param:getArgument(0)
+ if not enable then
+ param:setArgument(0, true)
+ end
+ return not enable
+end
diff --git a/app/src/main/assets/generic_no_result.lua b/app/src/main/assets/generic_block_method.lua
similarity index 63%
rename from app/src/main/assets/generic_no_result.lua
rename to app/src/main/assets/generic_block_method.lua
index 9ab57b38..42856dd2 100644
--- a/app/src/main/assets/generic_no_result.lua
+++ b/app/src/main/assets/generic_block_method.lua
@@ -1,19 +1,19 @@
--- This file is part of XPrivacy/Lua.
+-- This file is part of XPrivacyLua.
--- XPrivacy/Lua is free software: you can redistribute it and/or modify
+-- XPrivacyLua is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--- XPrivacy/Lua is distributed in the hope that it will be useful,
+-- XPrivacyLua is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- You should have received a copy of the GNU General Public License
--- along with XPrivacy/Lua. If not, see .
+-- along with XPrivacyLua. If not, see .
--- Copyright 2017-2018 Marcel Bokhorst (M66B)
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
function before(hook, param)
param:setResult(nil)
diff --git a/app/src/main/assets/generic_country_value.lua b/app/src/main/assets/generic_country_value.lua
new file mode 100644
index 00000000..f1ca2094
--- /dev/null
+++ b/app/src/main/assets/generic_country_value.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake = 'xx'
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/generic_empty_list.lua b/app/src/main/assets/generic_empty_list.lua
new file mode 100644
index 00000000..465b42c5
--- /dev/null
+++ b/app/src/main/assets/generic_empty_list.lua
@@ -0,0 +1,31 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local list = param:getResult()
+ if list == nil then
+ return false
+ end
+ local array = luajava.bindClass('java.lang.reflect.Array')
+ if array:getLength(list:toArray()) == 0 then
+ return false
+ end
+
+ local fake = luajava.newInstance('java.util.ArrayList')
+ param:setResult(fake)
+ return true
+end
diff --git a/app/src/main/assets/generic_empty_string_array.lua b/app/src/main/assets/generic_empty_string_array.lua
new file mode 100644
index 00000000..4cca1820
--- /dev/null
+++ b/app/src/main/assets/generic_empty_string_array.lua
@@ -0,0 +1,29 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil or result.length == 0 then
+ return false
+ end
+
+ local stringClass = luajava.bindClass('java.lang.String')
+ local arrayClass = luajava.bindClass('java.lang.reflect.Array')
+ local fake = arrayClass:newInstance(stringClass, 0)
+ param:setResult(fake)
+ return true
+end
diff --git a/app/src/main/assets/generic_false_value.lua b/app/src/main/assets/generic_false_value.lua
new file mode 100644
index 00000000..e0461abe
--- /dev/null
+++ b/app/src/main/assets/generic_false_value.lua
@@ -0,0 +1,26 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if not result then
+ return false
+ end
+
+ param:setResult(false)
+ return true, 'true', 'false'
+end
diff --git a/app/src/main/assets/generic_filter_by_uid.lua b/app/src/main/assets/generic_filter_by_uid.lua
new file mode 100644
index 00000000..4f4114ad
--- /dev/null
+++ b/app/src/main/assets/generic_filter_by_uid.lua
@@ -0,0 +1,58 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local list = param:getResult()
+ if list == nil then
+ return false
+ end
+
+ local filtered = false
+ local name = hook:getName()
+ local cuid = param:getUid()
+
+ local index
+ local info = list:toArray()
+ for index = info['length'], 1, -1 do
+ local item = info[index]
+
+ local uid
+ if item == nil then
+ uid = -1
+ elseif name == 'PackageManager.getInstalledPackages' or
+ name == 'PackageManager.getPackagesHoldingPermissions' or
+ name == 'PackageManager.getPreferredPackages' then
+ uid = item.applicationInfo.uid -- PackageInfo
+ elseif name == 'PackageManager.queryIntentActivities' or
+ name == 'PackageManager.queryIntentActivityOptions' then
+ uid = item.activityInfo.applicationInfo.uid -- ResolveInfo
+ elseif name == 'PackageManager.queryIntentContentProviders' then
+ uid = item.providerInfo.applicationInfo.uid -- ResolveInfo
+ elseif name == 'PackageManager.queryIntentServices' then
+ uid = item.serviceInfo.applicationInfo.uid -- ResolveInfo
+ else
+ uid = item.uid
+ end
+
+ if uid ~= cuid then
+ filtered = true
+ list:remove(index - 1)
+ end
+ end
+
+ return filtered
+end
diff --git a/app/src/main/assets/generic_mcc_value.lua b/app/src/main/assets/generic_mcc_value.lua
new file mode 100644
index 00000000..64bfeb07
--- /dev/null
+++ b/app/src/main/assets/generic_mcc_value.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake = 1 -- test network
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/generic_mnc_value.lua b/app/src/main/assets/generic_mnc_value.lua
new file mode 100644
index 00000000..64bfeb07
--- /dev/null
+++ b/app/src/main/assets/generic_mnc_value.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake = 1 -- test network
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/generic_null_value.lua b/app/src/main/assets/generic_null_value.lua
new file mode 100644
index 00000000..0bf44d17
--- /dev/null
+++ b/app/src/main/assets/generic_null_value.lua
@@ -0,0 +1,30 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake
+ param:setResult(fake)
+ if result ~= nil and type(result) == 'userdata' then
+ result = result:toString()
+ end
+ return true, result, fake
+end
diff --git a/app/src/main/assets/generic_operator_value.lua b/app/src/main/assets/generic_operator_value.lua
new file mode 100644
index 00000000..0e20cc12
--- /dev/null
+++ b/app/src/main/assets/generic_operator_value.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake = '00101' -- test network
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/generic_unknown_value.lua b/app/src/main/assets/generic_unknown_value.lua
new file mode 100644
index 00000000..760c0d26
--- /dev/null
+++ b/app/src/main/assets/generic_unknown_value.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake = 'unknown'
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/generic_zero_value.lua b/app/src/main/assets/generic_zero_value.lua
new file mode 100644
index 00000000..bc6327b3
--- /dev/null
+++ b/app/src/main/assets/generic_zero_value.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == 0 then
+ return false
+ end
+
+ local fake = 0
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/geofence$builder_setcircularregion.lua b/app/src/main/assets/geofence$builder_setcircularregion.lua
new file mode 100644
index 00000000..44e855e5
--- /dev/null
+++ b/app/src/main/assets/geofence$builder_setcircularregion.lua
@@ -0,0 +1,23 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ param:setArgument(0, 0) -- latitude
+ param:setArgument(1, 0) -- longitude
+ param:setArgument(2, 0.1) -- radius
+ return true
+end
diff --git a/app/src/main/assets/hooks.json b/app/src/main/assets/hooks.json
index 9c47b09f..bd5e5faf 100644
--- a/app/src/main/assets/hooks.json
+++ b/app/src/main/assets/hooks.json
@@ -1,27 +1,511 @@
/*
- This file is part of XPrivacy/Lua.
+ This file is part of XPrivacyLua.
- XPrivacy/Lua is free software: you can redistribute it and/or modify
+ XPrivacyLua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- XPrivacy/Lua is distributed in the hope that it will be useful,
+ XPrivacyLua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
+ along with XPrivacyLua. If not, see .
- Copyright 2017-2018 Marcel Bokhorst (M66B)
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
*/
[
- // Location
- // https://developer.android.com/reference/android/location/LocationManager.html
- // https://developer.android.com/reference/android/location/Location.html
+ // Determine activity
+ // https://developers.google.com/android/reference/com/google/android/gms/location/ActivityRecognitionResult
+ // https://developers.google.com/android/reference/com/google/android/gms/location/DetectedActivity
+ {
+ "collection": "Privacy",
+ "group": "Determine.Activity",
+ "name": "ActivityRecognitionResult.extractResult",
+ "author": "M66B",
+ "className": "com.google.android.gms.location.ActivityRecognitionResult",
+ "methodName": "extractResult",
+ "parameterTypes": [
+ "android.content.Intent"
+ ],
+ "returnType": "com.google.android.gms.location.ActivityRecognitionResult",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@activityrecognitionresult_extractresult"
+ },
+ // Get applications
+ // https://developer.android.com/reference/android/app/ActivityManager.html
+ // https://developer.android.com/reference/android/appwidget/AppWidgetManager.html
+ // https://developer.android.com/reference/android/content/pm/PackageManager.html
+ // https://developer.android.com/reference/android/content/Intent.html
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "ActivityManager.getRecentTasks",
+ "author": "M66B",
+ "className": "android.app.ActivityManager",
+ "methodName": "getRecentTasks",
+ "parameterTypes": [
+ "int",
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 1,
+ "maxSdk": 20,
+ // Android L returns filtered data
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "ActivityManager.getRunningAppProcesses",
+ "author": "M66B",
+ "className": "android.app.ActivityManager",
+ "methodName": "getRunningAppProcesses",
+ "parameterTypes": [
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 3,
+ "luaScript": "@generic_filter_by_uid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "ActivityManager.getRunningServices",
+ "author": "M66B",
+ "className": "android.app.ActivityManager",
+ "methodName": "getRunningServices",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 1,
+ "maxSdk": 25,
+ // Android O returns filtered data
+ "luaScript": "@generic_filter_by_uid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "ActivityManager.getRunningTasks",
+ "author": "M66B",
+ "className": "android.app.ActivityManager",
+ "methodName": "getRunningTasks",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 1,
+ "maxSdk": 20,
+ // Android L returns filtered data
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "AppWidgetManager.getInstalledProviders",
+ "author": "M66B",
+ "className": "android.appwidget.AppWidgetManager",
+ "methodName": "getInstalledProviders",
+ "parameterTypes": [
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 3,
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "AppWidgetManager.getInstalledProvidersForPackage",
+ "author": "M66B",
+ "className": "android.appwidget.AppWidgetManager",
+ "methodName": "getInstalledProvidersForPackage",
+ "parameterTypes": [
+ "java.lang.String",
+ "android.os.UserHandle"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 26,
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "AppWidgetManager.getInstalledProvidersForProfile",
+ "author": "M66B",
+ "className": "android.appwidget.AppWidgetManager",
+ "methodName": "getInstalledProvidersForProfile",
+ "parameterTypes": [
+ "android.os.UserHandle"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 21,
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "Intent.createFromParcel/package",
+ "author": "M66B",
+ "className": "android.content.Intent",
+ "methodName": "CREATOR:createFromParcel",
+ "parameterTypes": [
+ "android.os.Parcel"
+ ],
+ "returnType": "android.content.Intent",
+ "minSdk": 1,
+ "luaScript": "@intent_createfromparcel"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "PackageManager.getInstalledApplications",
+ "author": "M66B",
+ "className": "android.content.pm.PackageManager",
+ "methodName": "getInstalledApplications",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 1,
+ "luaScript": "@generic_filter_by_uid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "PackageManager.getInstalledPackages",
+ "author": "M66B",
+ "className": "android.content.pm.PackageManager",
+ "methodName": "getInstalledPackages",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 1,
+ "luaScript": "@generic_filter_by_uid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "PackageManager.getPackagesHoldingPermissions",
+ "author": "M66B",
+ "className": "android.content.pm.PackageManager",
+ "methodName": "getPackagesHoldingPermissions",
+ "parameterTypes": [
+ "[Ljava.lang.String;",
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 18,
+ "luaScript": "@generic_filter_by_uid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "PackageManager.queryIntentActivities",
+ "author": "M66B",
+ "className": "android.content.pm.PackageManager",
+ "methodName": "queryIntentActivities",
+ "parameterTypes": [
+ "android.content.Intent",
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 1,
+ "luaScript": "@generic_filter_by_uid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "PackageManager.getPreferredPackages",
+ "author": "M66B",
+ "className": "android.content.pm.PackageManager",
+ "methodName": "getPreferredPackages",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 1,
+ "luaScript": "@generic_filter_by_uid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "PackageManager.queryIntentActivityOptions",
+ "author": "M66B",
+ "className": "android.content.pm.PackageManager",
+ "methodName": "queryIntentActivityOptions",
+ "parameterTypes": [
+ "android.content.ComponentName",
+ "[Landroid.content.Intent;",
+ "android.content.Intent",
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 1,
+ "luaScript": "@generic_filter_by_uid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "PackageManager.queryIntentContentProviders",
+ "author": "M66B",
+ "className": "android.content.pm.PackageManager",
+ "methodName": "queryIntentContentProviders",
+ "parameterTypes": [
+ "android.content.Intent",
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 19,
+ "luaScript": "@generic_filter_by_uid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Applications",
+ "name": "PackageManager.queryIntentServices",
+ "author": "M66B",
+ "className": "android.content.pm.PackageManager",
+ "methodName": "queryIntentServices",
+ "parameterTypes": [
+ "android.content.Intent",
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 19,
+ "luaScript": "@generic_filter_by_uid"
+ },
+ // Get calendars
+ // https://developer.android.com/guide/topics/providers/calendar-provider.html API 14
+ {
+ "collection": "Privacy",
+ "group": "Get.Calendars",
+ "name": "ContentResolver.query1/calendars",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 1,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Calendars",
+ "name": "ContentResolver.query16/calendars",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 16,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Calendars",
+ "name": "ContentResolver.query26/calendars",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "android.os.Bundle",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 26,
+ "luaScript": "@contentresolver_query"
+ },
+ // Get call log
+ // https://developer.android.com/reference/android/provider/CallLog.html API 1
+ {
+ "collection": "Privacy",
+ "group": "Get.Call.log",
+ "name": "ContentResolver.query1/call_log",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 1,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Call.log",
+ "name": "ContentResolver.query16/call_log",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 16,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Call.log",
+ "name": "ContentResolver.query26/call_log",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "android.os.Bundle",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 26,
+ "luaScript": "@contentresolver_query"
+ },
+ // Get contacts
+ // https://developer.android.com/guide/topics/providers/contacts-provider.html API 5
+ // https://developer.android.com/reference/android/provider/BlockedNumberContract.html API 24
+ // https://android.googlesource.com/platform/frameworks/opt/telephony/+/master/src/java/com/android/internal/telephony/IccProvider.java
+ {
+ "collection": "Privacy",
+ "group": "Get.Contacts",
+ "name": "ContentResolver.query1/contacts",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 1,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Contacts",
+ "name": "ContentResolver.query16/contacts",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 16,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Contacts",
+ "name": "ContentResolver.query26/contacts",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "android.os.Bundle",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 26,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Contacts",
+ "name": "ContentResolver.query1/blockednumber",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 1,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Contacts",
+ "name": "ContentResolver.query16/blockednumber",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 16,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Contacts",
+ "name": "ContentResolver.query26/blockednumber",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "android.os.Bundle",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 26,
+ "luaScript": "@contentresolver_query"
+ },
+ // Get location
// https://developer.android.com/reference/android/os/Bundle.html
+ // https://developer.android.com/reference/android/location/Location.html
+ // https://developer.android.com/reference/android/location/LocationManager.html
{
"collection": "Privacy",
"group": "Get.Location",
@@ -34,7 +518,6 @@
],
"returnType": "java.lang.Object",
"minSdk": 1,
- "maxSdk": 999,
"enabled": false,
"luaScript": "@bundle_get_location"
},
@@ -50,104 +533,2221 @@
],
"returnType": "android.location.Location",
"minSdk": 1,
- "maxSdk": 999,
- "enabled": true,
"luaScript": "@location_createfromparcel"
},
- // Record audio
- // https://developer.android.com/reference/android/media/AudioRecord.html
{
"collection": "Privacy",
- "group": "Record.Audio",
- "name": "AudioRecord.startRecording",
+ "group": "Get.Location",
+ "name": "LocationManager.addNmeaListener5",
"author": "M66B",
- "className": "android.media.AudioRecord",
- "methodName": "startRecording",
+ "className": "android.location.LocationManager",
+ "methodName": "addNmeaListener",
"parameterTypes": [
+ "android.location.GpsStatus$NmeaListener"
],
- "returnType": "void",
- "minSdk": 3,
- "maxSdk": 999,
- "enabled": true,
- "luaScript": "@generic_no_result"
+ "returnType": "boolean",
+ "minSdk": 5,
+ "luaScript": "@generic_false_value"
},
{
"collection": "Privacy",
- "group": "Record.Audio",
- "name": "AudioRecord.startRecording(MediaSyncEvent)",
+ "group": "Get.Location",
+ "name": "LocationManager.addNmeaListener24a",
"author": "M66B",
- "className": "android.media.AudioRecord",
- "methodName": "startRecording",
+ "className": "android.location.LocationManager",
+ "methodName": "addNmeaListener",
"parameterTypes": [
- "android.media.MediaSyncEvent"
+ "android.location.OnNmeaMessageListener"
],
- "returnType": "void",
- "minSdk": 16,
- "maxSdk": 999,
- "enabled": true,
- "luaScript": "@generic_no_result"
+ "returnType": "boolean",
+ "minSdk": 24,
+ "luaScript": "@generic_false_value"
},
{
"collection": "Privacy",
- "group": "Record.Audio",
- "name": "AudioRecord.stop",
+ "group": "Get.Location",
+ "name": "LocationManager.addNmeaListener24b",
"author": "M66B",
- "className": "android.media.AudioRecord",
- "methodName": "stop",
+ "className": "android.location.LocationManager",
+ "methodName": "addNmeaListener",
"parameterTypes": [
+ "android.location.OnNmeaMessageListener",
+ "android.os.Handler"
],
- "returnType": "void",
- "minSdk": 3,
- "maxSdk": 999,
- "enabled": true,
- "luaScript": "@generic_no_result"
+ "returnType": "boolean",
+ "minSdk": 24,
+ "luaScript": "@generic_false_value"
},
- // Record audio
- // https://developer.android.com/reference/android/media/MediaRecorder.html
{
"collection": "Privacy",
- "group": "Record.Audio",
- "name": "MediaRecorder.setAudioSource",
+ "group": "Get.Location",
+ "name": "LocationManager.addProximityAlert",
"author": "M66B",
- "className": "android.media.MediaRecorder",
- "methodName": "setAudioSource",
+ "className": "android.location.LocationManager",
+ "methodName": "addProximityAlert",
"parameterTypes": [
- "int"
+ "double",
+ "double",
+ "float",
+ "long",
+ "android.app.PendingIntent"
],
"returnType": "void",
"minSdk": 1,
- "maxSdk": 999,
- "enabled": true,
- "luaScript": "@mediarecorder_setaudiosource"
+ "luaScript": "@generic_block_method"
},
{
"collection": "Privacy",
- "group": "Record.Audio",
- "name": "MediaRecorder.start",
+ "group": "Get.Location",
+ "name": "Geofence$Builder.setCircularRegion",
"author": "M66B",
- "className": "android.media.MediaRecorder",
- "methodName": "start",
+ "className": "com.google.android.gms.location.Geofence$Builder",
+ "methodName": "setCircularRegion",
"parameterTypes": [
+ "double",
+ "double",
+ "float"
],
- "returnType": "void",
+ "returnType": "com.google.android.gms.location.Geofence$Builder",
"minSdk": 1,
- "maxSdk": 999,
- "enabled": true,
- "luaScript": "@mediarecorder_start"
+ "optional": true,
+ "luaScript": "@geofence$builder_setcircularregion"
},
{
"collection": "Privacy",
- "group": "Record.Audio",
- "name": "MediaRecorder.stop",
+ "group": "Get.Location",
+ "name": "PlaceLikelihoodBuffer.getCount",
"author": "M66B",
- "className": "android.media.MediaRecorder",
- "methodName": "stop",
+ "className": "com.google.android.gms.location.places.PlaceLikelihoodBuffer",
+ "methodName": "getCount",
+ "parameterTypes": [
+ ],
+ "returnType": "int",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_zero_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Location",
+ "name": "PlaceLikelihoodBuffer.get",
+ "author": "M66B",
+ "className": "com.google.android.gms.location.places.PlaceLikelihoodBuffer",
+ "methodName": "get",
+ "parameterTypes": [
+ "int"
+ ],
+ // any return type
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_null_value"
+ },
+ // Get messages
+ // https://developer.android.com/reference/android/provider/Telephony.Sms.Intents#SMS_RECEIVED_ACTION
+ // https://developer.android.com/reference/android/provider/Telephony.Mms.html API 19
+ // https://developer.android.com/reference/android/provider/Telephony.MmsSms.html API 19
+ // https://developer.android.com/reference/android/provider/VoicemailContract.html API 14
+ // https://developer.android.com/reference/android/telephony/SmsManager.html API 4
+ // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/provider/VoicemailContract.java
+ {
+ "collection": "Privacy",
+ "group": "Get.Messages",
+ "name": "Intent.createFromParcel/message",
+ "author": "M66B",
+ "className": "android.content.Intent",
+ "methodName": "CREATOR:createFromParcel",
+ "parameterTypes": [
+ "android.os.Parcel"
+ ],
+ "returnType": "android.content.Intent",
+ "minSdk": 1,
+ "luaScript": "@intent_createfromparcel"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Messages",
+ "name": "ContentResolver.query1/mmssms",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 1,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Messages",
+ "name": "ContentResolver.query16/mmssms",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 16,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Messages",
+ "name": "ContentResolver.query26/mmssms",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "android.os.Bundle",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 26,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Messages",
+ "name": "ContentResolver.query1/voicemail",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 1,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Messages",
+ "name": "ContentResolver.query16/voicemail",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 16,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Messages",
+ "name": "ContentResolver.query26/voicemail",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "android.os.Bundle",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 26,
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Messages",
+ "name": "SmsManager.getAllMessagesFromIcc",
+ "author": "M66B",
+ "className": "android.telephony.SmsManager",
+ "methodName": "getAllMessagesFromIcc",
+ "parameterTypes": [
+ ],
+ "returnType": "java.util.ArrayList",
+ "minSdk": 4,
+ "luaScript": "@generic_empty_list"
+ },
+ // Get sensors
+ // https://developer.android.com/reference/android/hardware/SensorManager.html
+ {
+ "collection": "Privacy",
+ "group": "Get.Sensors",
+ "name": "SensorManager.getDefaultSensor",
+ "author": "M66B",
+ "className": "android.hardware.SensorManager",
+ "methodName": "getDefaultSensor",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "android.hardware.Sensor",
+ "minSdk": 3,
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Sensors",
+ "name": "SensorManager.getDefaultSensor/wakeup",
+ "author": "M66B",
+ "className": "android.hardware.SensorManager",
+ "methodName": "getDefaultSensor",
+ "parameterTypes": [
+ "int",
+ "boolean"
+ ],
+ "returnType": "android.hardware.Sensor",
+ "minSdk": 21,
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Sensors",
+ "name": "SensorManager.getDynamicSensorList",
+ "author": "M66B",
+ "className": "android.hardware.SensorManager",
+ "methodName": "getDynamicSensorList",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 24,
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Sensors",
+ "name": "SensorManager.getSensorList",
+ "author": "M66B",
+ "className": "android.hardware.SensorManager",
+ "methodName": "getSensorList",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 3,
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Get.Sensors",
+ "name": "SensorManager.getSensors",
+ "author": "M66B",
+ "className": "android.hardware.SensorManager",
+ "methodName": "getSensors",
+ "parameterTypes": [
+ ],
+ "returnType": "int",
+ "minSdk": 1,
+ "luaScript": "@generic_zero_value"
+ },
+ // Read account
+ // https://developer.android.com/reference/android/accounts/Account.html
+ {
+ "collection": "Privacy",
+ "group": "Read.Account",
+ "name": "Account.createFromParcel",
+ "author": "M66B",
+ "className": "android.accounts.Account",
+ "methodName": "CREATOR:createFromParcel",
+ "parameterTypes": [
+ "android.os.Parcel"
+ ],
+ "returnType": "android.accounts.Account",
+ "minSdk": 5,
+ "luaScript": "@account_createfromparcel"
+ },
+ // Read clipboard
+ // https://developer.android.com/reference/android/content/ClipData.html
+ {
+ "collection": "Privacy",
+ "group": "Read.Clipboard",
+ "name": "ClipData.createFromParcel",
+ "author": "M66B",
+ "className": "android.content.ClipData",
+ "methodName": "CREATOR:createFromParcel",
+ "parameterTypes": [
+ "android.os.Parcel"
+ ],
+ "returnType": "android.content.ClipData",
+ "minSdk": 11,
+ "luaScript": "@clipdata_createfromparcel"
+ },
+ // Read identifiers
+ // https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
+ // https://developer.android.com/reference/android/os/Build.html
+ // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/SystemProperties.java
+ {
+ "collection": "Privacy",
+ "group": "Read.Identifiers",
+ "name": "AdvertisingIdClient$Info.getId",
+ "author": "M66B",
+ "className": "com.google.android.gms.ads.identifier.AdvertisingIdClient$Info",
+ "methodName": "getId",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@advertisingidclient$info_getid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Identifiers",
+ "name": "Build.getSerial",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "getSerial",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 26,
+ "luaScript": "@value_serial"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Identifiers",
+ "name": "Build.SERIAL",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#SERIAL",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 9,
+ "usage": false,
+ "luaScript": "@value_serial"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Identifiers",
+ "name": "ContentResolver.query1/gsf_id",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 1,
+ "excludePackages": "com\\.android\\..*,com\\.google\\.android\\..*",
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Identifiers",
+ "name": "ContentResolver.query16/gsf_id",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "[Ljava.lang.String;",
+ "java.lang.String",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 16,
+ "excludePackages": "com\\.android\\..*,com\\.google\\.android\\..*",
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Identifiers",
+ "name": "ContentResolver.query26/gsf_id",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "query",
+ "parameterTypes": [
+ "android.net.Uri",
+ "[Ljava.lang.String;",
+ "android.os.Bundle",
+ "android.os.CancellationSignal"
+ ],
+ "returnType": "android.database.Cursor",
+ "minSdk": 26,
+ "excludePackages": "com\\.android\\..*,com\\.google\\.android\\..*",
+ "luaScript": "@contentresolver_query"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Identifiers",
+ "name": "Settings.Secure.getString/android_id",
+ "author": "M66B",
+ "className": "android.provider.Settings$Secure",
+ "methodName": "getString",
+ "parameterTypes": [
+ "android.content.ContentResolver",
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 3,
+ "luaScript": "@settingssecure_getstring"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Identifiers",
+ "name": "SystemProperties.get/serial",
+ "author": "M66B",
+ "className": "android.os.SystemProperties",
+ "methodName": "get",
+ "parameterTypes": [
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@systemproperties_get"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Identifiers",
+ "name": "SystemProperties.get.default/serial",
+ "author": "M66B",
+ "className": "android.os.SystemProperties",
+ "methodName": "get",
+ "parameterTypes": [
+ "java.lang.String",
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@systemproperties_get"
+ },
+ // Read network data
+ // https://developer.android.com/reference/android/telephony/TelephonyManager.html
+ // https://developer.android.com/reference/android/telephony/PhoneStateListener.html
+ // https://developer.android.com/reference/android/net/wifi/WifiManager.html
+ {
+ "collection": "Privacy",
+ "group": "Read.Network",
+ "name": "NetworkInfo.getExtraInfo",
+ "author": "M66B",
+ "className": "android.net.NetworkInfo",
+ "methodName": "getExtraInfo",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Network",
+ "name": "TelephonyManager.getAllCellInfo",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getAllCellInfo",
+ "parameterTypes": [
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 17,
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Network",
+ "name": "TelephonyManager.getCellLocation",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getCellLocation",
+ "parameterTypes": [
+ ],
+ "returnType": "android.telephony.CellLocation",
+ "minSdk": 1,
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Network",
+ "name": "TelephonyManager.getNeighboringCellInfo",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getNeighboringCellInfo",
+ "parameterTypes": [
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 3,
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Network",
+ "name": "TelephonyManager.listen",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "listen",
"parameterTypes": [
+ "android.telephony.PhoneStateListener",
+ "int"
],
"returnType": "void",
"minSdk": 1,
- "maxSdk": 999,
- "enabled": true,
- "luaScript": "@mediarecorder_start"
+ "luaScript": "@telephonymanager_listen"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Network",
+ "name": "WifiManager.getConfiguredNetworks",
+ "author": "M66B",
+ "className": "android.net.wifi.WifiManager",
+ "methodName": "getConfiguredNetworks",
+ "parameterTypes": [
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 1,
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Network",
+ "name": "WifiManager.getScanResults",
+ "author": "M66B",
+ "className": "android.net.wifi.WifiManager",
+ "methodName": "getScanResults",
+ "parameterTypes": [
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 1,
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Network",
+ "name": "WifiInfo.getBSSID",
+ "author": "M66B",
+ "className": "android.net.wifi.WifiInfo",
+ "methodName": "getBSSID",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@wifiinfo_getbssid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Network",
+ "name": "WifiInfo.getSSID",
+ "author": "M66B",
+ "className": "android.net.wifi.WifiInfo",
+ "methodName": "getSSID",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@wifiinfo_getssid"
+ },
+ // Read notifications
+ // https://developer.android.com/reference/android/service/notification/NotificationListenerService.html
+ {
+ "collection": "Privacy",
+ "group": "Read.Notifications",
+ "name": "StatusBarNotification.getNotification",
+ "author": "M66B",
+ "className": "android.service.notification.StatusBarNotification",
+ "methodName": "getNotification",
+ "parameterTypes": [
+ ],
+ "returnType": "android.app.Notification",
+ "minSdk": 18,
+ "luaScript": "@statusbarnotification_getnotification"
+ },
+ // Read sync data
+ // https://developer.android.com/reference/android/content/ContentResolver.html#getCurrentSyncs()
+ {
+ "collection": "Privacy",
+ "group": "Read.Sync",
+ "name": "ContentResolver.getCurrentSync",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "getCurrentSync",
+ "parameterTypes": [
+ ],
+ "returnType": "android.content.SyncInfo",
+ "minSdk": 8,
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Sync",
+ "name": "ContentResolver.getCurrentSyncs",
+ "author": "M66B",
+ "className": "android.content.ContentResolver",
+ "methodName": "getCurrentSyncs",
+ "parameterTypes": [
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 11,
+ "luaScript": "@generic_empty_list"
+ },
+ // Read telephony data
+ // https://developer.android.com/reference/android/telephony/SubscriptionInfo.html
+ // https://developer.android.com/reference/android/telephony/TelephonyManager.html
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "SubscriptionInfo.getIccId",
+ "author": "M66B",
+ "className": "android.telephony.SubscriptionInfo",
+ "methodName": "getIccId",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 22,
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "SubscriptionInfo.getMcc",
+ "author": "M66B",
+ "className": "android.telephony.SubscriptionInfo",
+ "methodName": "getMcc",
+ "parameterTypes": [
+ ],
+ "returnType": "int",
+ "minSdk": 22,
+ "luaScript": "@generic_mcc_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "SubscriptionInfo.getMnc",
+ "author": "M66B",
+ "className": "android.telephony.SubscriptionInfo",
+ "methodName": "getMnc",
+ "parameterTypes": [
+ ],
+ "returnType": "int",
+ "minSdk": 22,
+ "luaScript": "@generic_mnc_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "SubscriptionInfo.getNumber",
+ "author": "M66B",
+ "className": "android.telephony.SubscriptionInfo",
+ "methodName": "getNumber",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 22,
+ "luaScript": "@value_phone_number"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "SubscriptionInfo.getSubscriptionId",
+ "author": "M66B",
+ "className": "android.telephony.SubscriptionInfo",
+ "methodName": "getSubscriptionId",
+ "parameterTypes": [
+ ],
+ "returnType": "int",
+ "minSdk": 22,
+ "luaScript": "@generic_zero_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getDeviceId",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getDeviceId",
+ // IMEI, MEID, ESN
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@value_device_id"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getDeviceId/slot",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getDeviceId",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ // officially SDK 23
+ "optional": true,
+ "luaScript": "@value_device_id"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getGroupIdLevel1",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getGroupIdLevel1",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 18,
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getImei",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getImei",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 26,
+ "luaScript": "@value_imei"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getImei/slot",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getImei",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 26,
+ "luaScript": "@value_imei"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getLine1Number",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getLine1Number",
+ // MSISDN
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@value_phone_number"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getMeid",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getMeid",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 26,
+ "luaScript": "@value_meid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getMeid/slot",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getMeid",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 26,
+ "luaScript": "@value_meid"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getNetworkSpecifier",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getNetworkSpecifier",
+ // subscription ID pinned to the TelephonyManager
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 26,
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getSimSerialNumber",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getSimSerialNumber",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getSubscriberId",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getSubscriberId",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getSubscriberId/slot",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getSubscriberId",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "optional": true,
+ // Not an official API
+ "luaScript": "@generic_null_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getVoiceMailAlphaTag",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getVoiceMailAlphaTag",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@value_phone_number"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Telephony",
+ "name": "TelephonyManager.getVoiceMailNumber",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getVoiceMailNumber",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@value_phone_number"
+ },
+ // Record audio
+ // https://developer.android.com/reference/android/media/AudioRecord.html
+ {
+ "collection": "Privacy",
+ "group": "Record.Audio",
+ "name": "AudioRecord.startRecording",
+ "author": "M66B",
+ "className": "android.media.AudioRecord",
+ "methodName": "startRecording",
+ "parameterTypes": [
+ ],
+ "returnType": "void",
+ "minSdk": 3,
+ "notify": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Record.Audio",
+ "name": "AudioRecord.startRecording(MediaSyncEvent)",
+ "author": "M66B",
+ "className": "android.media.AudioRecord",
+ "methodName": "startRecording",
+ "parameterTypes": [
+ "android.media.MediaSyncEvent"
+ ],
+ "returnType": "void",
+ "minSdk": 16,
+ "notify": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Record.Audio",
+ "name": "AudioRecord.stop",
+ "author": "M66B",
+ "className": "android.media.AudioRecord",
+ "methodName": "stop",
+ "parameterTypes": [
+ ],
+ "returnType": "void",
+ "minSdk": 3,
+ "luaScript": "@generic_block_method"
+ },
+ // Record audio
+ // https://developer.android.com/reference/android/media/AudioManager.html
+ // https://developer.android.com/reference/android/media/MediaRecorder.html
+ {
+ "collection": "Privacy",
+ "group": "Record.Audio",
+ "name": "AudioManager.getActiveRecordingConfigurations",
+ "author": "M66B",
+ "className": "android.media.AudioManager",
+ "methodName": "getActiveRecordingConfigurations",
+ "parameterTypes": [
+ ],
+ "returnType": "java.util.List",
+ "minSdk": 24,
+ "luaScript": "@generic_empty_list"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Record.Audio",
+ "name": "AudioManager.getDevices",
+ "author": "M66B",
+ "className": "android.media.AudioManager",
+ "methodName": "getDevices",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "[Landroid.media.AudioDeviceInfo;",
+ "minSdk": 23,
+ "luaScript": "@audiomanager_getdevices"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Record.Audio",
+ "name": "AudioManager.registerAudioDeviceCallback",
+ "author": "M66B",
+ "className": "android.media.AudioManager",
+ "methodName": "registerAudioDeviceCallback",
+ "parameterTypes": [
+ "android.media.AudioDeviceCallback",
+ "android.os.Handler"
+ ],
+ "returnType": "void",
+ "minSdk": 23,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Record.Audio",
+ "name": "AudioManager.registerAudioRecordingCallback",
+ "author": "M66B",
+ "className": "android.media.AudioManager",
+ "methodName": "registerAudioRecordingCallback",
+ "parameterTypes": [
+ "android.media.AudioManager$AudioRecordingCallback",
+ "android.os.Handler"
+ ],
+ "returnType": "void",
+ "minSdk": 24,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Record.Audio",
+ "name": "MediaRecorder.setAudioSource",
+ "author": "M66B",
+ "className": "android.media.MediaRecorder",
+ "methodName": "setAudioSource",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "luaScript": "@mediarecorder_setsource"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Record.Audio",
+ "name": "MediaRecorder.start.Audio",
+ "author": "M66B",
+ "className": "android.media.MediaRecorder",
+ "methodName": "start",
+ "parameterTypes": [
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "notify": true,
+ "luaScript": "@mediarecorder_start"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Record.Audio",
+ "name": "MediaRecorder.stop.Audio",
+ "author": "M66B",
+ "className": "android.media.MediaRecorder",
+ "methodName": "stop",
+ "parameterTypes": [
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "luaScript": "@mediarecorder_stop"
+ },
+ // Record video
+ // https://developer.android.com/reference/android/media/MediaRecorder.html
+ {
+ "collection": "Privacy",
+ "group": "Record.Video",
+ "name": "MediaRecorder.setVideoSource",
+ "author": "M66B",
+ "className": "android.media.MediaRecorder",
+ "methodName": "setVideoSource",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "void",
+ "minSdk": 3,
+ "luaScript": "@mediarecorder_setsource"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Record.Video",
+ "name": "MediaRecorder.start.Video",
+ "author": "M66B",
+ "className": "android.media.MediaRecorder",
+ "methodName": "start",
+ "parameterTypes": [
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "notify": true,
+ "luaScript": "@mediarecorder_start"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Record.Video",
+ "name": "MediaRecorder.stop.Video",
+ "author": "M66B",
+ "className": "android.media.MediaRecorder",
+ "methodName": "stop",
+ "parameterTypes": [
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "luaScript": "@mediarecorder_stop"
+ },
+ // Send messages
+ // https://developer.android.com/reference/android/telephony/SmsManager.html
+ {
+ "collection": "Privacy",
+ "group": "Send.Messages",
+ "name": "SmsManager.sendDataMessage",
+ "author": "M66B",
+ "className": "android.telephony.SmsManager",
+ "methodName": "sendDataMessage",
+ "parameterTypes": [
+ "java.lang.String",
+ "java.lang.String",
+ "short",
+ "byte[]",
+ "android.app.PendingIntent",
+ "android.app.PendingIntent"
+ ],
+ "returnType": "void",
+ "minSdk": 4,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Send.Messages",
+ "name": "SmsManager.sendMultimediaMessage",
+ "author": "M66B",
+ "className": "android.telephony.SmsManager",
+ "methodName": "sendMultimediaMessage",
+ "parameterTypes": [
+ "android.content.Context",
+ "android.net.Uri",
+ "java.lang.String",
+ "android.os.Bundle",
+ "android.app.PendingIntent"
+ ],
+ "returnType": "void",
+ "minSdk": 21,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Send.Messages",
+ "name": "SmsManager.sendMultipartTextMessage",
+ "author": "M66B",
+ "className": "android.telephony.SmsManager",
+ "methodName": "sendMultipartTextMessage",
+ "parameterTypes": [
+ "java.lang.String",
+ "java.lang.String",
+ "java.util.ArrayList",
+ "java.util.ArrayList",
+ "java.util.ArrayList"
+ ],
+ "returnType": "void",
+ "minSdk": 4,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Send.Messages",
+ "name": "SmsManager.sendTextMessage",
+ "author": "M66B",
+ "className": "android.telephony.SmsManager",
+ "methodName": "sendTextMessage",
+ "parameterTypes": [
+ "java.lang.String",
+ "java.lang.String",
+ "java.lang.String",
+ "android.app.PendingIntent",
+ "android.app.PendingIntent"
+ ],
+ "returnType": "void",
+ "minSdk": 4,
+ "luaScript": "@generic_block_method"
+ },
+ // Use analytics
+ // https://docs.fabric.io/javadocs/fabric/1.3.17/index.html
+ // https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics
+ // https://developers.facebook.com/docs/reference/androidsdk/current/facebook/com/facebook/appevents/appeventslogger.html/
+ // https://developers.google.com/android/reference/com/google/android/gms/analytics/GoogleAnalytics
+ // https://segment.com/docs/sources/mobile/android/
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "Fabric.with/kits",
+ "author": "M66B",
+ "className": "io.fabric.sdk.android.Fabric",
+ "methodName": "with",
+ "parameterTypes": [
+ "android.content.Context",
+ "[Lio.fabric.sdk.android.Kit;"
+ ],
+ "returnType": "io.fabric.sdk.android.Fabric",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@fabric_with_kits"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "FirebaseAnalytics.getInstance",
+ "author": "M66B",
+ "className": "com.google.firebase.analytics.FirebaseAnalytics",
+ "methodName": "getInstance",
+ "parameterTypes": [
+ "android.content.Context"
+ ],
+ "returnType": "com.google.firebase.analytics.FirebaseAnalytics",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@firebase_getinstance"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "FirebaseAnalytics.setAnalyticsCollectionEnabled",
+ "author": "M66B",
+ "className": "com.google.firebase.analytics.FirebaseAnalytics",
+ "methodName": "setAnalyticsCollectionEnabled",
+ "parameterTypes": [
+ "boolean"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@firebase_setenabled"
+ },
+ // logPayment is not hooked to prevent problems with payments
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.activateApp/app",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "activateApp",
+ "parameterTypes": [
+ "android.app.Application"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.activateApp/app/id",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "activateApp",
+ "parameterTypes": [
+ "android.app.Application",
+ "java.lang.String"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.activateApp/context",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "activateApp",
+ "parameterTypes": [
+ "android.content.Context"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.activateApp/context/id",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "activateApp",
+ "parameterTypes": [
+ "android.content.Context",
+ "java.lang.String"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.deactivateApp/context",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "deactivateApp",
+ "parameterTypes": [
+ "android.content.Context"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.deactivateApp/context/id",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "deactivateApp",
+ "parameterTypes": [
+ "android.content.Context",
+ "java.lang.String"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.logEvent/name",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "logEvent",
+ "parameterTypes": [
+ "java.lang.String"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.logEvent/name/bundle",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "logEvent",
+ "parameterTypes": [
+ "java.lang.String",
+ "android.os.Bundle"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.logEvent/name/double",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "logEvent",
+ "parameterTypes": [
+ "java.lang.String",
+ "double"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.logEvent/name/double/bundle",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "logEvent",
+ "parameterTypes": [
+ "java.lang.String",
+ "double",
+ "android.os.Bundle"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ // logPurchase is not hooked to prevent trouble with purchases
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.logPushNotificationOpen",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "logPushNotificationOpen",
+ "parameterTypes": [
+ "android.os.Bundle"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "AppEventsLogger.logPushNotificationOpen/action",
+ "author": "M66B",
+ "className": "com.facebook.appevents.AppEventsLogger",
+ "methodName": "logPushNotificationOpen",
+ "parameterTypes": [
+ "android.os.Bundle",
+ "java.lang.String"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "MixpanelAPI.track",
+ "author": "M66B",
+ "className": "com.mixpanel.android.mpmetrics.MixpanelAPI",
+ "methodName": "track",
+ "parameterTypes": [
+ "java.lang.String"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "MixpanelAPI.track/json",
+ "author": "M66B",
+ "className": "com.mixpanel.android.mpmetrics.MixpanelAPI",
+ "methodName": "track",
+ "parameterTypes": [
+ "java.lang.String",
+ "org.json.JSONObject"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "MixpanelAPI.track/json/auto",
+ "author": "M66B",
+ "className": "com.mixpanel.android.mpmetrics.MixpanelAPI",
+ "methodName": "track",
+ "parameterTypes": [
+ "java.lang.String",
+ "org.json.JSONObject",
+ "boolean"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "MixpanelAPI.trackMap",
+ "author": "M66B",
+ "className": "com.mixpanel.android.mpmetrics.MixpanelAPI",
+ "methodName": "trackMap",
+ "parameterTypes": [
+ "java.lang.String",
+ "java.util.Map"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@generic_block_method"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "GoogleAnalytics.getInstance",
+ "author": "M66B",
+ "className": "com.google.android.gms.analytics.GoogleAnalytics",
+ "methodName": "getInstance",
+ "parameterTypes": [
+ "android.content.Context"
+ ],
+ "returnType": "com.google.android.gms.analytics.GoogleAnalytics",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@ga_getinstance"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "GoogleAnalytics.setDryRun",
+ "author": "M66B",
+ "className": "com.google.android.gms.analytics.GoogleAnalytics",
+ "methodName": "setDryRun",
+ "parameterTypes": [
+ "boolean"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@ga_setdryrun"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "SegmentAnalytics.setSingletonInstance",
+ "author": "M66B",
+ "className": "com.segment.analytics.Analytics",
+ "methodName": "setSingletonInstance",
+ "parameterTypes": [
+ "com.segment.analytics.Analytics"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@segment_getinstance"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Analytics",
+ "name": "SegmentAnalytics.optOut",
+ "author": "M66B",
+ "className": "com.segment.analytics.Analytics",
+ "methodName": "optOut",
+ "parameterTypes": [
+ "boolean"
+ ],
+ "returnType": "void",
+ "minSdk": 1,
+ "optional": true,
+ "luaScript": "@segment_optout"
+ },
+ // Use camera
+ // https://developer.android.com/reference/android/hardware/Camera.html
+ // https://developer.android.com/reference/android/hardware/camera2/CameraManager.html
+ // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/hardware/Camera.java
+ {
+ "collection": "Privacy",
+ "group": "Use.Camera",
+ "name": "Camera.getCameraInfo",
+ "author": "M66B",
+ "className": "android.hardware.Camera",
+ "methodName": "getCameraInfo",
+ "parameterTypes": [
+ "int",
+ "android.hardware.Camera$CameraInfo"
+ ],
+ "returnType": "void",
+ "minSdk": 9,
+ "notify": true,
+ "luaScript": "@camera_open"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Camera",
+ "name": "Camera.getNumberOfCameras",
+ "author": "M66B",
+ "className": "android.hardware.Camera",
+ "methodName": "getNumberOfCameras",
+ "parameterTypes": [
+ ],
+ "returnType": "int",
+ "minSdk": 9,
+ "luaScript": "@generic_zero_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Camera",
+ "name": "Camera.open",
+ "author": "M66B",
+ "className": "android.hardware.Camera",
+ "methodName": "open",
+ "parameterTypes": [
+ ],
+ "returnType": "android.hardware.Camera",
+ "minSdk": 1,
+ "notify": true,
+ "luaScript": "@camera_open"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Camera",
+ "name": "Camera.open/camera",
+ "author": "M66B",
+ "className": "android.hardware.Camera",
+ "methodName": "open",
+ "parameterTypes": [
+ "int"
+ ],
+ "returnType": "android.hardware.Camera",
+ "minSdk": 9,
+ "notify": true,
+ "luaScript": "@camera_open"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Camera",
+ "name": "Camera.openLegacy/camera",
+ "author": "M66B",
+ "className": "android.hardware.Camera",
+ "methodName": "openLegacy",
+ "parameterTypes": [
+ "int",
+ "int"
+ ],
+ "returnType": "android.hardware.Camera",
+ "minSdk": 21,
+ "notify": true,
+ "luaScript": "@camera_open"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Camera",
+ "name": "CameraManager2.openCamera",
+ "author": "M66B",
+ "className": "android.hardware.camera2.CameraManager",
+ "methodName": "openCamera",
+ "parameterTypes": [
+ "java.lang.String",
+ "android.hardware.camera2.CameraDevice$StateCallback",
+ "android.os.Handler"
+ ],
+ "returnType": "void",
+ "minSdk": 21,
+ "notify": true,
+ "luaScript": "@camera2_open"
+ },
+ // Use tracking
+ // https://developer.android.com/reference/android/os/Build.html
+ // https://developer.android.com/reference/android/content/res/Configuration.html
+ // https://developer.android.com/reference/android/telephony/SubscriptionInfo.html
+ // https://developer.android.com/reference/android/telephony/TelephonyManager.html
+ // https://developer.android.com/reference/android/webkit/WebView.html
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.BOARD",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#BOARD",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.BOOTLOADER",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#BOOTLOADER",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 8,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.BRAND",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#BRAND",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.DEVICE",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#DEVICE",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.DISPLAY",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#DISPLAY",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 3,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.FINGERPRINT",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#FINGERPRINT",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "enabled": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.HARDWARE",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#HARDWARE",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 8,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.HOST",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#HOST",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.ID",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#ID",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.MANUFACTURER",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#MANUFACTURER",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 4,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.MODEL",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#MODEL",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.PRODUCT",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#PRODUCT",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.RADIO",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#RADIO",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 8,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.TAGS",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#TAGS",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.TIME",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#TIME",
+ "parameterTypes": [
+ ],
+ "returnType": "long",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_zero_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.TYPE",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#TYPE",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Build.USER",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "#USER",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "usage": false,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Read.Identifiers",
+ "name": "Build.getRadioVersion",
+ "author": "M66B",
+ "className": "android.os.Build",
+ "methodName": "getRadioVersion",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 14,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Configuration.createFromParcel",
+ "author": "M66B",
+ "className": "android.content.res.Configuration",
+ "methodName": "CREATOR:createFromParcel",
+ "parameterTypes": [
+ "android.os.Parcel"
+ ],
+ "returnType": "android.content.res.Configuration",
+ "minSdk": 1,
+ "usage": false,
+ "enabled": false,
+ "luaScript": "@configuration_createfromparcel"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "Settings.Secure.getString/bluetooth_name",
+ "author": "M66B",
+ "className": "android.provider.Settings$Secure",
+ "methodName": "getString",
+ "parameterTypes": [
+ "android.content.ContentResolver",
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 3,
+ "luaScript": "@settingssecure_getstring"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "SystemProperties.get/build",
+ "author": "M66B",
+ "className": "android.os.SystemProperties",
+ "methodName": "get",
+ "parameterTypes": [
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "enabled": false,
+ "luaScript": "@systemproperties_get"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "SystemProperties.get.default/build",
+ "author": "M66B",
+ "className": "android.os.SystemProperties",
+ "methodName": "get",
+ "parameterTypes": [
+ "java.lang.String",
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "enabled": false,
+ "luaScript": "@systemproperties_get"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "SystemProperties.get/operator",
+ "author": "M66B",
+ "className": "android.os.SystemProperties",
+ "methodName": "get",
+ "parameterTypes": [
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "enabled": false,
+ "luaScript": "@systemproperties_get"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "SystemProperties.get.default/operator",
+ "author": "M66B",
+ "className": "android.os.SystemProperties",
+ "methodName": "get",
+ "parameterTypes": [
+ "java.lang.String",
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "enabled": false,
+ "luaScript": "@systemproperties_get"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "SystemProperties.get/vendor",
+ "author": "M66B",
+ "className": "android.os.SystemProperties",
+ "methodName": "get",
+ "parameterTypes": [
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "enabled": false,
+ "luaScript": "@systemproperties_get"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "SystemProperties.get.default/vendor",
+ "author": "M66B",
+ "className": "android.os.SystemProperties",
+ "methodName": "get",
+ "parameterTypes": [
+ "java.lang.String",
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "enabled": false,
+ "luaScript": "@systemproperties_get"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "SubscriptionInfo.getCarrierName",
+ "author": "M66B",
+ "className": "android.telephony.SubscriptionInfo",
+ "methodName": "getCarrierName",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.CharSequence",
+ "minSdk": 22,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "SubscriptionInfo.getCountryIso",
+ "author": "M66B",
+ "className": "android.telephony.SubscriptionInfo",
+ "methodName": "getCountryIso",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 22,
+ "luaScript": "@generic_country_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "TelephonyManager/getNetworkCountryIso",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getNetworkCountryIso",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@generic_country_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "TelephonyManager/getNetworkOperator",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getNetworkOperator",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@generic_operator_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "TelephonyManager/getNetworkOperatorName",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getNetworkOperatorName",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "TelephonyManager/getSimCountryIso",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getSimCountryIso",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@generic_country_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "TelephonyManager/getSimOperator",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getSimOperator",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@generic_operator_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "TelephonyManager/getSimOperatorName",
+ "author": "M66B",
+ "className": "android.telephony.TelephonyManager",
+ "methodName": "getSimOperatorName",
+ "parameterTypes": [
+ ],
+ "returnType": "java.lang.String",
+ "minSdk": 1,
+ "luaScript": "@generic_unknown_value"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "WebView.constructor.1a",
+ "author": "M66B",
+ "className": "android.webkit.WebView",
+ "parameterTypes": [
+ "android.content.Context"
+ ],
+ "minSdk": 1,
+ "enabled": false,
+ "usage": false,
+ "luaScript": "@webview_constructor"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "WebView.constructor.1b",
+ "author": "M66B",
+ "className": "android.webkit.WebView",
+ "parameterTypes": [
+ "android.content.Context",
+ "android.util.AttributeSet"
+ ],
+ "minSdk": 1,
+ "enabled": false,
+ "usage": false,
+ "luaScript": "@webview_constructor"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "WebView.constructor.1c",
+ "author": "M66B",
+ "className": "android.webkit.WebView",
+ "parameterTypes": [
+ "android.content.Context",
+ "android.util.AttributeSet",
+ "int"
+ ],
+ "minSdk": 1,
+ "enabled": false,
+ "usage": false,
+ "luaScript": "@webview_constructor"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "WebView.constructor.21",
+ "author": "M66B",
+ "className": "android.webkit.WebView",
+ "parameterTypes": [
+ "android.content.Context",
+ "android.util.AttributeSet",
+ "int",
+ "int"
+ ],
+ "minSdk": 21,
+ "enabled": false,
+ "usage": false,
+ "luaScript": "@webview_constructor"
+ },
+ {
+ "collection": "Privacy",
+ "group": "Use.Tracking",
+ "name": "WebView.constructor.11",
+ "author": "M66B",
+ "className": "android.webkit.WebView",
+ "parameterTypes": [
+ "android.content.Context",
+ "android.util.AttributeSet",
+ "int",
+ "boolean"
+ ],
+ "minSdk": 11,
+ "enabled": false,
+ "usage": false,
+ "luaScript": "@webview_constructor"
}
]
diff --git a/app/src/main/assets/intent_createfromparcel.lua b/app/src/main/assets/intent_createfromparcel.lua
new file mode 100644
index 00000000..e129c3e1
--- /dev/null
+++ b/app/src/main/assets/intent_createfromparcel.lua
@@ -0,0 +1,67 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local intent = param:getResult()
+ if intent == nil then
+ return false
+ end
+
+ local action = intent:getAction()
+ if action == nil then
+ return false
+ end
+
+ local h = hook:getName()
+ local match = string.gmatch(h, '[^/]+')
+ local func = match()
+ local name = match()
+
+ if name == 'package' and (action == 'android.intent.action.PACKAGE_ADDED' or
+ action == 'android.intent.action.PACKAGE_CHANGED' or
+ action == 'android.intent.action.PACKAGE_DATA_CLEARED' or
+ action == 'android.intent.action.PACKAGE_FIRST_LAUNCH' or
+ action == 'android.intent.action.PACKAGE_FULLY_REMOVED' or
+ action == 'android.intent.action.PACKAGE_INSTALL' or
+ action == 'android.intent.action.PACKAGE_NEEDS_VERIFICATION' or
+ action == 'android.intent.action.PACKAGE_REMOVED' or
+ action == 'android.intent.action.PACKAGE_REPLACED' or
+ action == 'android.intent.action.PACKAGE_RESTARTED' or
+ action == 'android.intent.action.PACKAGE_VERIFIED') then
+ local uriClass = luajava.bindClass('android.net.Uri')
+ local uri = uriClass:parse('package:' .. param:getPackageName())
+ intent:setData(uri)
+ return true
+ elseif name == 'package' and (action == 'android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE' or
+ action == 'android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE' or
+ action == 'android.intent.action.PACKAGES_SUSPENDED' or
+ action == 'android.intent.action.PACKAGES_UNSUSPENDED') then
+ local stringClass = luajava.bindClass('java.lang.String')
+ local arrayClass = luajava.bindClass('java.lang.reflect.Array')
+ local stringArray = arrayClass:newInstance(stringClass, 0)
+ intent:putExtra('android.intent.extra.changed_package_list', stringArray)
+ return true
+ elseif name == 'message' and action == 'android.provider.Telephony.SMS_RECEIVED' then
+ local objectClass = luajava.bindClass('java.lang.Object')
+ local arrayClass = luajava.bindClass('java.lang.reflect.Array')
+ local objectArray = arrayClass:newInstance(objectClass, 0)
+ intent:putExtra('pdus', objectArray)
+ return true
+ else
+ return false
+ end
+end
diff --git a/app/src/main/assets/location_createfromparcel.lua b/app/src/main/assets/location_createfromparcel.lua
index f61fc686..8e2d128d 100644
--- a/app/src/main/assets/location_createfromparcel.lua
+++ b/app/src/main/assets/location_createfromparcel.lua
@@ -1,22 +1,73 @@
--- This file is part of XPrivacy/Lua.
+-- This file is part of XPrivacyLua.
--- XPrivacy/Lua is free software: you can redistribute it and/or modify
+-- XPrivacyLua is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--- XPrivacy/Lua is distributed in the hope that it will be useful,
+-- XPrivacyLua is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- You should have received a copy of the GNU General Public License
--- along with XPrivacy/Lua. If not, see .
+-- along with XPrivacyLua. If not, see .
--- Copyright 2017-2018 Marcel Bokhorst (M66B)
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
function after(hook, param)
- param:getResult():setLatitude(0)
- param:getResult():setLongitude(0)
- return true
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+ local old = result:toString()
+
+ local latitude = 0
+ local longitude = 0
+ local type = param:getSetting('location.type')
+ if type == 'set' then
+ latitude = param:getSetting('location.latitude')
+ longitude = param:getSetting('location.longitude')
+ if latitude == nil or longitude == nil then
+ latitude = 0
+ longitude = 0
+ end
+ elseif type == 'coarse' then
+ local accuracy = param:getSetting('location.accuracy')
+ if accuracy ~= nil then
+ local clatitude = param:getValue('latitude', hook)
+ local clongitude = param:getValue('longitude', hook)
+ if clatitude == nil or clongitude == nil then
+ clatitude, clongitude = randomoffset(result:getLatitude(), result:getLongitude(), accuracy)
+ param:putValue('latitude', clatitude, hook)
+ param:putValue('longitude', clongitude, hook)
+ end
+ latitude = clatitude
+ longitude = clongitude
+ end
+ end
+
+ if result:hasAccuracy() then
+ local accuracy = result:getAccuracy()
+ if accuracy > 0 then
+ latitude, longitude = randomoffset(latitude, longitude, accuracy)
+ end
+ end
+
+ result:setLatitude(latitude)
+ result:setLongitude(longitude)
+ return true, old, result:toString()
+end
+
+function randomoffset(latitude, longitude, radius)
+ local r = radius / 111000; -- degrees
+
+ local w = r * math.sqrt(math.random())
+ local t = 2 * math.pi * math.random()
+ local lonoff = w * math.cos(t)
+ local latoff = w * math.sin(t)
+
+ lonoff = lonoff / math.cos(math.rad(latitude))
+
+ return latitude + latoff, longitude + lonoff
end
diff --git a/app/src/main/assets/mediarecorder_setsource.lua b/app/src/main/assets/mediarecorder_setsource.lua
new file mode 100644
index 00000000..adbf108e
--- /dev/null
+++ b/app/src/main/assets/mediarecorder_setsource.lua
@@ -0,0 +1,28 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ local this = param:getThis()
+ if this == nil then
+ return false
+ end
+
+ local source = param:getArgument(0)
+
+ param:putValue('source', source, this)
+ return false
+end
diff --git a/app/src/main/assets/mediarecorder_start.lua b/app/src/main/assets/mediarecorder_start.lua
index b3e125be..23fc0044 100644
--- a/app/src/main/assets/mediarecorder_start.lua
+++ b/app/src/main/assets/mediarecorder_start.lua
@@ -1,26 +1,31 @@
--- This file is part of XPrivacy/Lua.
+-- This file is part of XPrivacyLua.
--- XPrivacy/Lua is free software: you can redistribute it and/or modify
+-- XPrivacyLua is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--- XPrivacy/Lua is distributed in the hope that it will be useful,
+-- XPrivacyLua is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- You should have received a copy of the GNU General Public License
--- along with XPrivacy/Lua. If not, see .
+-- along with XPrivacyLua. If not, see .
--- Copyright 2017-2018 Marcel Bokhorst (M66B)
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
function before(hook, param)
- source = param:getValue('source')
- if source ~= nil then
- param:setResult(nil)
- return true
- else
+ local this = param:getThis()
+ if this == nil then
return false
end
+
+ local source = param:getValue('source', this)
+ if source == nil then
+ return false
+ end
+
+ param:setResult(nil)
+ return true
end
diff --git a/app/src/main/assets/mediarecorder_stop.lua b/app/src/main/assets/mediarecorder_stop.lua
new file mode 100644
index 00000000..75570d0a
--- /dev/null
+++ b/app/src/main/assets/mediarecorder_stop.lua
@@ -0,0 +1,32 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ local this = param:getThis()
+ if this == nil then
+ return false
+ end
+
+ local source = param:getValue('source', this)
+ if source == nil then
+ return false
+ end
+
+ param:putValue('source', nil, this)
+ param:setResult(nil)
+ return true
+end
diff --git a/app/src/main/assets/segment_getinstance.lua b/app/src/main/assets/segment_getinstance.lua
new file mode 100644
index 00000000..64eee8ad
--- /dev/null
+++ b/app/src/main/assets/segment_getinstance.lua
@@ -0,0 +1,33 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ local analytics = param:getArgument(0)
+ if analytics == nil or analytics.optOut == nil then
+ return false
+ end
+
+ if type(analytics.optOut) == 'function' then
+ analytics:optOut(true)
+ return true
+ elseif type(analytics.optOut) == 'userdata' then
+ analytics.optOut:set(false)
+ return true
+ else
+ return false
+ end
+end
diff --git a/app/src/main/assets/segment_optout.lua b/app/src/main/assets/segment_optout.lua
new file mode 100644
index 00000000..7029835e
--- /dev/null
+++ b/app/src/main/assets/segment_optout.lua
@@ -0,0 +1,24 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ local optout = param:getArgument(0)
+ if not optout then
+ param:setArgument(0, true)
+ end
+ return not optout
+end
diff --git a/app/src/main/assets/settingssecure_getstring.lua b/app/src/main/assets/settingssecure_getstring.lua
new file mode 100644
index 00000000..ba85feca
--- /dev/null
+++ b/app/src/main/assets/settingssecure_getstring.lua
@@ -0,0 +1,50 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local key = param:getArgument(1)
+ if key == nil then
+ return false
+ end
+
+ local h = hook:getName()
+ local match = string.gmatch(h, '[^/]+')
+ local func = match()
+ local name = match()
+
+ --log(key .. '=' .. result .. ' name=' .. name)
+
+ if name == 'android_id' and key == 'android_id' then
+ local fake = param:getSetting('value.android_id')
+ if fake == nil then
+ fake = '0000000000000000'
+ end
+ param:setResult(fake)
+ return true, result, fake
+ elseif name == 'bluetooth_name' and key == 'bluetooth_name' then
+ local fake
+ param:setResult(fake)
+ return true, result, fake
+ else
+ return false
+ end
+end
diff --git a/app/src/main/assets/statusbarnotification_getnotification.lua b/app/src/main/assets/statusbarnotification_getnotification.lua
new file mode 100644
index 00000000..d56c4547
--- /dev/null
+++ b/app/src/main/assets/statusbarnotification_getnotification.lua
@@ -0,0 +1,28 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil or result.length == 0 then
+ return false
+ end
+
+ local notificationClass = luajava.bindClass('android.app.Notification')
+ local fake = luajava.new(notificationClass, result.icon, 'private', result.when) -- deprecated
+ param:setResult(fake)
+ return true
+end
diff --git a/app/src/main/assets/systemproperties_get.lua b/app/src/main/assets/systemproperties_get.lua
new file mode 100644
index 00000000..5125d053
--- /dev/null
+++ b/app/src/main/assets/systemproperties_get.lua
@@ -0,0 +1,63 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local key = param:getArgument(0)
+
+ local h = hook:getName()
+ local match = string.gmatch(h, '[^/]+')
+ local func = match()
+ local name = match()
+
+ --log(key .. '=' .. result .. ' name=' .. name)
+
+ if name == 'serial' and (key == 'ro.serialno' or key == 'ro.boot.serialno') then
+ local fake = param:getSetting('value.serial')
+ if fake == nil then
+ fake = 'unknown'
+ end
+ param:setResult(fake)
+ return true, result, fake
+ elseif name == 'build' and string.sub(key, 1, string.len('ro.build.')) == 'ro.build.' then
+ local fake
+ param:setResult(fake)
+ return true, result, fake
+ elseif name == 'operator' and (key == 'gsm.operator.alpha' or key == 'gsm.sim.operator.alpha') then
+ local fake = 'unknown'
+ param:setResult(fake)
+ return true, result, fake
+ elseif name == 'operator' and (key == 'gsm.operator.numeric' or key == 'gsm.sim.operator.numeric') then
+ local fake = '00101' -- test network
+ param:setResult(fake)
+ return true, result, fake
+ elseif name == 'operator' and (key == 'gsm.operator.iso-country' or key == 'gsm.sim.operator.iso-country') then
+ local fake = 'xx'
+ param:setResult(fake)
+ return true, result, fake
+ elseif name == 'vendor' and string.sub(key, 1, string.len('ro.vendor.')) == 'ro.vendor.' then
+ local fake
+ param:setResult(fake)
+ return true, result, fake
+ else
+ return false
+ end
+end
diff --git a/app/src/main/assets/telephonymanager_listen.lua b/app/src/main/assets/telephonymanager_listen.lua
new file mode 100644
index 00000000..f9d2d087
--- /dev/null
+++ b/app/src/main/assets/telephonymanager_listen.lua
@@ -0,0 +1,33 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function before(hook, param)
+ local restricted = false;
+ local events = param:getArgument(1)
+ if bit32.band(events, 16) ~= 0 then -- cell location
+ restricted = true
+ events = bit32.bxor(events, 16)
+ end
+ if bit32.band(events, 1024) ~= 0 then -- cell info
+ restricted = true
+ events = bit32.bxor(events, 1024)
+ end
+ if restricted then
+ param:setArgument(1, events)
+ end
+ return restricted
+end
diff --git a/app/src/main/assets/value_device_id.lua b/app/src/main/assets/value_device_id.lua
new file mode 100644
index 00000000..2a673530
--- /dev/null
+++ b/app/src/main/assets/value_device_id.lua
@@ -0,0 +1,40 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local this = param:getThis()
+ if this == nil then
+ return false
+ end
+
+ local result = param:getResult()
+ local type = this:getPhoneType()
+
+ local fake
+ if type == 1 then -- GSM
+ fake = param:getSetting('value.imei')
+ elseif type == 2 then -- CDMA
+ fake = param:getSetting('value.meid')
+ end
+
+ if result == nil and fake == nil then
+ return false
+ end
+
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/value_imei.lua b/app/src/main/assets/value_imei.lua
new file mode 100644
index 00000000..0c7dc203
--- /dev/null
+++ b/app/src/main/assets/value_imei.lua
@@ -0,0 +1,38 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local this = param:getThis()
+ if this == nil then
+ return false
+ end
+
+ local result = param:getResult()
+ local type = this:getPhoneType()
+
+ local fake
+ if type == 1 then -- GSM
+ fake = param:getSetting('value.imei')
+ end
+
+ if result == nil and fake == nil then
+ return false
+ end
+
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/value_meid.lua b/app/src/main/assets/value_meid.lua
new file mode 100644
index 00000000..6e7b6f8a
--- /dev/null
+++ b/app/src/main/assets/value_meid.lua
@@ -0,0 +1,38 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local this = param:getThis()
+ if this == nil then
+ return false
+ end
+
+ local result = param:getResult()
+ local type = this:getPhoneType()
+
+ local fake
+ if type == 2 then -- CDMA
+ fake = param:getSetting('value.meid')
+ end
+
+ if result == nil and fake == nil then
+ return false
+ end
+
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/value_phone_number.lua b/app/src/main/assets/value_phone_number.lua
new file mode 100644
index 00000000..bf4f064b
--- /dev/null
+++ b/app/src/main/assets/value_phone_number.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake = param:getSetting('value.phone_number')
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/value_serial.lua b/app/src/main/assets/value_serial.lua
new file mode 100644
index 00000000..4abec167
--- /dev/null
+++ b/app/src/main/assets/value_serial.lua
@@ -0,0 +1,31 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake = param:getSetting('value.serial')
+ if fake == nil then
+ fake = 'unknown'
+ end
+
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/webview_constructor.lua b/app/src/main/assets/webview_constructor.lua
new file mode 100644
index 00000000..8c55618f
--- /dev/null
+++ b/app/src/main/assets/webview_constructor.lua
@@ -0,0 +1,49 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(h, param)
+ local this = param:getThis()
+ if this == nil then
+ return false
+ end
+
+ local hooked = param:getValue('hooked', this)
+ if hooked then
+ return false
+ else
+ param:putValue('hooked', true, this)
+ end
+
+ local settings = this:getSettings()
+ if settings == nil then
+ return false
+ else
+ local ua = 'Mozilla/5.0 (Linux; U; Android; en-us) AppleWebKit/999+ (KHTML, like Gecko) Safari/999.9'
+ hook(settings, 'setUserAgentString', setUserAgentString, ua)
+ settings:setUserAgentString('dummy')
+ return true
+ end
+end
+
+function setUserAgentString(when, param, ua)
+ if when == 'before' then
+ if param:getArgument(0) ~= ua then
+ log('Setting ua=' .. ua)
+ param:setArgument(0, ua)
+ end
+ end
+end
diff --git a/app/src/main/assets/wifiinfo_getbssid.lua b/app/src/main/assets/wifiinfo_getbssid.lua
new file mode 100644
index 00000000..a16f23ee
--- /dev/null
+++ b/app/src/main/assets/wifiinfo_getbssid.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake = '00:00:00:00:00:00'
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/wifiinfo_getssid.lua b/app/src/main/assets/wifiinfo_getssid.lua
new file mode 100644
index 00000000..3f64786b
--- /dev/null
+++ b/app/src/main/assets/wifiinfo_getssid.lua
@@ -0,0 +1,27 @@
+-- This file is part of XPrivacyLua.
+
+-- XPrivacyLua is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+
+-- XPrivacyLua is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+
+-- You should have received a copy of the GNU General Public License
+-- along with XPrivacyLua. If not, see .
+
+-- Copyright 2017-2019 Marcel Bokhorst (M66B)
+
+function after(hook, param)
+ local result = param:getResult()
+ if result == nil then
+ return false
+ end
+
+ local fake = '"private"'
+ param:setResult(fake)
+ return true, result, fake
+end
diff --git a/app/src/main/assets/xposed_init b/app/src/main/assets/xposed_init
index f8d073f5..e40f16f7 100644
--- a/app/src/main/assets/xposed_init
+++ b/app/src/main/assets/xposed_init
@@ -1 +1 @@
-eu.faircode.xlua.Xposed
+eu.faircode.xlua.XLua
diff --git a/app/src/main/banner_play_store.png b/app/src/main/banner_play_store.png
new file mode 100644
index 00000000..602daf5d
Binary files /dev/null and b/app/src/main/banner_play_store.png differ
diff --git a/app/src/main/banner_xprivacylua_v1.png b/app/src/main/banner_xprivacylua_v1.png
new file mode 100644
index 00000000..6693ccbe
Binary files /dev/null and b/app/src/main/banner_xprivacylua_v1.png differ
diff --git a/app/src/main/banner_xprivacylua_v2.png b/app/src/main/banner_xprivacylua_v2.png
new file mode 100644
index 00000000..5bf3f4ee
Binary files /dev/null and b/app/src/main/banner_xprivacylua_v2.png differ
diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png
index 07deaa95..1c4bb920 100644
Binary files a/app/src/main/ic_launcher-web.png and b/app/src/main/ic_launcher-web.png differ
diff --git a/app/src/main/java/eu/faircode/xlua/ActivityBase.java b/app/src/main/java/eu/faircode/xlua/ActivityBase.java
new file mode 100644
index 00000000..04eaa500
--- /dev/null
+++ b/app/src/main/java/eu/faircode/xlua/ActivityBase.java
@@ -0,0 +1,40 @@
+/*
+ This file is part of XPrivacyLua.
+
+ XPrivacyLua is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ XPrivacyLua is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with XPrivacyLua. If not, see .
+
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
+ */
+
+package eu.faircode.xlua;
+
+import android.os.Bundle;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+public class ActivityBase extends AppCompatActivity {
+ private String theme;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ theme = XProvider.getSetting(this, "global", "theme");
+ setTheme("dark".equals(theme) ? R.style.AppThemeDark : R.style.AppThemeLight);
+
+ super.onCreate(savedInstanceState);
+ }
+
+ String getThemeName() {
+ return (theme == null ? "light" : theme);
+ }
+}
diff --git a/app/src/main/java/eu/faircode/xlua/ActivityHelp.java b/app/src/main/java/eu/faircode/xlua/ActivityHelp.java
index e557ba32..170e8e98 100644
--- a/app/src/main/java/eu/faircode/xlua/ActivityHelp.java
+++ b/app/src/main/java/eu/faircode/xlua/ActivityHelp.java
@@ -1,36 +1,38 @@
/*
- This file is part of XPrivacy/Lua.
+ This file is part of XPrivacyLua.
- XPrivacy/Lua is free software: you can redistribute it and/or modify
+ XPrivacyLua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- XPrivacy/Lua is distributed in the hope that it will be useful,
+ XPrivacyLua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
+ along with XPrivacyLua. If not, see .
- Copyright 2017-2018 Marcel Bokhorst (M66B)
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
*/
package eu.faircode.xlua;
import android.os.Bundle;
-import android.support.v4.app.NavUtils;
-import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
import android.widget.TextView;
import java.util.Calendar;
-public class ActivityHelp extends AppCompatActivity {
+import androidx.core.app.NavUtils;
+
+public class ActivityHelp extends ActivityBase {
private static final String TAG = "XLua.Help";
@Override
@@ -43,12 +45,21 @@ protected void onCreate(Bundle savedInstanceState) {
TextView tvVersion = findViewById(R.id.tvVersion);
TextView tvLicense = findViewById(R.id.tvLicense);
+ TextView tvInstructions = findViewById(R.id.tvInstructions);
+ ImageView ivInstalled = findViewById(R.id.ivInstalled);
+ TextView tvInstalled = findViewById(R.id.tvInstalled);
+
tvLicense.setMovementMethod(LinkMovementMethod.getInstance());
+ tvInstructions.setMovementMethod(LinkMovementMethod.getInstance());
int year = Calendar.getInstance().get(Calendar.YEAR);
- tvVersion.setText(Util.getSelfVersionName(this));
+ tvVersion.setText(BuildConfig.VERSION_NAME);
tvLicense.setText(Html.fromHtml(getString(R.string.title_license, year)));
+ tvInstructions.setText(Html.fromHtml(getString(R.string.title_help_instructions)));
+
+ ivInstalled.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
+ tvInstalled.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
}
@Override
diff --git a/app/src/main/java/eu/faircode/xlua/ActivityMain.java b/app/src/main/java/eu/faircode/xlua/ActivityMain.java
index 336cab73..494eba21 100644
--- a/app/src/main/java/eu/faircode/xlua/ActivityMain.java
+++ b/app/src/main/java/eu/faircode/xlua/ActivityMain.java
@@ -1,20 +1,20 @@
/*
- This file is part of XPrivacy/Lua.
+ This file is part of XPrivacyLua.
- XPrivacy/Lua is free software: you can redistribute it and/or modify
+ XPrivacyLua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- XPrivacy/Lua is distributed in the hope that it will be useful,
+ XPrivacyLua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
+ along with XPrivacyLua. If not, see .
- Copyright 2017-2018 Marcel Bokhorst (M66B)
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
*/
package eu.faircode.xlua;
@@ -23,21 +23,11 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
-import android.os.Process;
-import android.os.RemoteException;
-import android.preference.PreferenceManager;
-import android.support.annotation.NonNull;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
-import android.support.v7.widget.SearchView;
+import android.preference.PreferenceManager;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.util.Log;
@@ -53,9 +43,21 @@
import android.widget.ListView;
import android.widget.TextView;
-import java.util.Calendar;
+import com.google.android.material.snackbar.Snackbar;
-public class ActivityMain extends AppCompatActivity {
+import java.util.Calendar;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.SearchView;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+public class ActivityMain extends ActivityBase {
private final static String TAG = "XLua.Main";
private FragmentMain fragmentMain = null;
@@ -63,21 +65,33 @@ public class ActivityMain extends AppCompatActivity {
private ListView drawerList;
private ActionBarDrawerToggle drawerToggle = null;
- private MenuItem menuSearch = null;
- private SearchView searchView = null;
+ private Menu menu = null;
private AlertDialog firstRunDialog = null;
+ public static final int LOADER_DATA = 1;
public static final String EXTRA_SEARCH_PACKAGE = "package";
+ private ExecutorService executor = Executors.newSingleThreadExecutor();
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Check if service is running
- final IService client = XService.getClient(this);
- if (client == null) {
- Snackbar.make(findViewById(android.R.id.content), getString(R.string.msg_no_service), Snackbar.LENGTH_INDEFINITE).show();
+ if (!XProvider.isAvailable(this)) {
+ Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), getString(R.string.msg_no_service), Snackbar.LENGTH_INDEFINITE);
+
+ final Intent intent = getPackageManager().getLaunchIntentForPackage("de.robv.android.xposed.installer");
+ if (intent != null && intent.resolveActivity(getPackageManager()) != null)
+ snackbar.setAction(R.string.title_fix, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(intent);
+ }
+ });
+
+ snackbar.show();
return;
}
@@ -96,6 +110,7 @@ protected void onCreate(Bundle savedInstanceState) {
// Get drawer layout
drawerLayout = findViewById(R.id.drawer_layout);
+ drawerLayout.setScrimColor(Util.resolveColor(this, R.attr.colorDrawerScrim));
// Create drawer toggle
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.app_name, R.string.app_name) {
@@ -127,70 +142,80 @@ public void onItemClick(AdapterView> parent, View view, int position, long id)
});
// Initialize drawer
- try {
- final int userId = Util.getUserId(Process.myUid());
- boolean showAll = Boolean.parseBoolean(client.getSetting(userId, "global", "show_all_apps"));
- boolean notifyNew = Boolean.parseBoolean(client.getSetting(userId, "global", "notify_new_apps"));
- boolean restrictNew = Boolean.parseBoolean(client.getSetting(userId, "global", "restrict_new_apps"));
-
- final ArrayAdapterDrawer drawerArray = new ArrayAdapterDrawer(ActivityMain.this, R.layout.draweritem);
+ boolean notifyNew = XProvider.getSettingBoolean(this, "global", "notify_new_apps");
+ boolean restrictNew = XProvider.getSettingBoolean(this, "global", "restrict_new_apps");
- drawerArray.add(new DrawerItem(this, R.string.menu_show_all, showAll, new DrawerItem.IListener() {
- @Override
- public void onClick(DrawerItem item) {
- try {
- client.putSetting(userId, "global", "show_all_apps", Boolean.toString(item.isChecked()));
- drawerArray.notifyDataSetChanged();
- fragmentMain.setShowAll(item.isChecked());
- } catch (RemoteException ex) {
- Log.e(TAG, Log.getStackTraceString(ex));
- Snackbar.make(findViewById(android.R.id.content), ex.toString(), Snackbar.LENGTH_INDEFINITE).show();
- }
- }
- }));
+ final ArrayAdapterDrawer drawerArray = new ArrayAdapterDrawer(ActivityMain.this, R.layout.draweritem);
+ if (!Util.isVirtualXposed())
drawerArray.add(new DrawerItem(this, R.string.menu_notify_new, notifyNew, new DrawerItem.IListener() {
@Override
public void onClick(DrawerItem item) {
- try {
- client.putSetting(userId, "global", "notify_new_apps", Boolean.toString(item.isChecked()));
- drawerArray.notifyDataSetChanged();
- } catch (RemoteException ex) {
- Log.e(TAG, Log.getStackTraceString(ex));
- Snackbar.make(findViewById(android.R.id.content), ex.toString(), Snackbar.LENGTH_INDEFINITE).show();
- }
+ XProvider.putSettingBoolean(ActivityMain.this, "global", "notify_new_apps", item.isChecked());
+ drawerArray.notifyDataSetChanged();
}
}));
+ if (!Util.isVirtualXposed())
drawerArray.add(new DrawerItem(this, R.string.menu_restrict_new, restrictNew, new DrawerItem.IListener() {
@Override
public void onClick(DrawerItem item) {
- try {
- client.putSetting(userId, "global", "restrict_new_apps", Boolean.toString(item.isChecked()));
- drawerArray.notifyDataSetChanged();
- } catch (RemoteException ex) {
- Log.e(TAG, Log.getStackTraceString(ex));
- Snackbar.make(findViewById(android.R.id.content), ex.toString(), Snackbar.LENGTH_INDEFINITE).show();
- }
+ XProvider.putSettingBoolean(ActivityMain.this, "global", "restrict_new_apps", item.isChecked());
+ drawerArray.notifyDataSetChanged();
}
}));
- drawerArray.add(new DrawerItem(this, R.string.menu_donate, new DrawerItem.IListener() {
- @Override
- public void onClick(DrawerItem item) {
- Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse("https://lua.xprivacy.eu/"));
- if (browse.resolveActivity(getPackageManager()) != null)
+ drawerArray.add(new DrawerItem(this, R.string.menu_companion, new DrawerItem.IListener() {
+ @Override
+ public void onClick(DrawerItem item) {
+ PackageManager pm = getPackageManager();
+ Intent companion = pm.getLaunchIntentForPackage(Util.PRO_PACKAGE_NAME);
+ if (companion == null) {
+ Intent browse = new Intent(Intent.ACTION_VIEW);
+ browse.setData(Uri.parse("https://lua.xprivacy.eu/pro/"));
+ if (browse.resolveActivity(pm) == null)
+ Snackbar.make(findViewById(android.R.id.content), getString(R.string.msg_no_browser), Snackbar.LENGTH_LONG).show();
+ else
startActivity(browse);
- }
- }));
+ } else
+ startActivity(companion);
+ }
+ }));
- drawerList.setAdapter(drawerArray);
+ drawerArray.add(new DrawerItem(this, R.string.menu_readme, new DrawerItem.IListener() {
+ @Override
+ public void onClick(DrawerItem item) {
+ Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/M66B/XPrivacyLua"));
+ if (browse.resolveActivity(getPackageManager()) == null)
+ Snackbar.make(findViewById(android.R.id.content), getString(R.string.msg_no_browser), Snackbar.LENGTH_LONG).show();
+ else
+ startActivity(browse);
+ }
+ }));
- fragmentMain.setShowAll(showAll);
- } catch (RemoteException ex) {
- Log.e(TAG, Log.getStackTraceString(ex));
- Snackbar.make(findViewById(android.R.id.content), ex.toString(), Snackbar.LENGTH_INDEFINITE).show();
- }
+ drawerArray.add(new DrawerItem(this, R.string.menu_faq, new DrawerItem.IListener() {
+ @Override
+ public void onClick(DrawerItem item) {
+ Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/M66B/XPrivacyLua/blob/master/FAQ.md"));
+ if (browse.resolveActivity(getPackageManager()) == null)
+ Snackbar.make(findViewById(android.R.id.content), getString(R.string.msg_no_browser), Snackbar.LENGTH_LONG).show();
+ else
+ startActivity(browse);
+ }
+ }));
+
+ drawerArray.add(new DrawerItem(this, R.string.menu_donate, new DrawerItem.IListener() {
+ @Override
+ public void onClick(DrawerItem item) {
+ Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse("https://lua.xprivacy.eu/"));
+ if (browse.resolveActivity(getPackageManager()) == null)
+ Snackbar.make(findViewById(android.R.id.content), getString(R.string.msg_no_browser), Snackbar.LENGTH_LONG).show();
+ else
+ startActivity(browse);
+ }
+ }));
+
+ drawerList.setAdapter(drawerArray);
checkFirstRun();
}
@@ -207,7 +232,9 @@ protected void onNewIntent(Intent intent) {
Log.i(TAG, "New " + intent);
super.onNewIntent(intent);
setIntent(intent);
- updateMenu();
+
+ if (this.menu != null)
+ updateMenu(this.menu);
}
@Override
@@ -229,12 +256,9 @@ public void onBackPressed() {
public boolean onCreateOptionsMenu(Menu menu) {
Log.i(TAG, "Create options");
- if (fragmentMain != null) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.main, menu);
-
- menuSearch = menu.findItem(R.id.menu_search);
- }
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.main, menu);
+ this.menu = menu;
return super.onCreateOptionsMenu(menu);
}
@@ -244,47 +268,49 @@ public boolean onPrepareOptionsMenu(Menu menu) {
Log.i(TAG, "Prepare options");
// Search
- if (menuSearch != null) {
- searchView = (SearchView) menuSearch.getActionView();
- searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- Log.i(TAG, "Search submit=" + query);
+ MenuItem menuSearch = menu.findItem(R.id.menu_search);
+ final SearchView searchView = (SearchView) menuSearch.getActionView();
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ Log.i(TAG, "Search submit=" + query);
+ if (fragmentMain != null) {
fragmentMain.filter(query);
searchView.clearFocus(); // close keyboard
- return true;
}
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String newText) {
- Log.i(TAG, "Search change=" + newText);
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ Log.i(TAG, "Search change=" + newText);
+ if (fragmentMain != null)
fragmentMain.filter(newText);
- return true;
- }
- });
+ return true;
+ }
+ });
- menuSearch.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
- @Override
- public boolean onMenuItemActionExpand(MenuItem menuItem) {
- Log.i(TAG, "Search expand");
- return true;
- }
+ menuSearch.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem menuItem) {
+ Log.i(TAG, "Search expand");
+ return true;
+ }
- @Override
- public boolean onMenuItemActionCollapse(MenuItem menuItem) {
- Log.i(TAG, "Search collapse");
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem menuItem) {
+ Log.i(TAG, "Search collapse");
- // Search uid once
- Intent intent = getIntent();
- intent.removeExtra(EXTRA_SEARCH_PACKAGE);
- setIntent(intent);
+ // Search uid once
+ Intent intent = getIntent();
+ intent.removeExtra(EXTRA_SEARCH_PACKAGE);
+ setIntent(intent);
- return true;
- }
- });
+ return true;
+ }
+ });
- updateMenu();
- }
+ updateMenu(menu);
return super.onPrepareOptionsMenu(menu);
}
@@ -296,6 +322,49 @@ public boolean onOptionsItemSelected(MenuItem item) {
Log.i(TAG, "Selected option " + item.getTitle());
switch (item.getItemId()) {
+ case R.id.menu_show:
+ AdapterApp.enumShow show = (fragmentMain == null ? AdapterApp.enumShow.none : fragmentMain.getShow());
+ this.menu.findItem(R.id.menu_show_user).setEnabled(show != AdapterApp.enumShow.none);
+ this.menu.findItem(R.id.menu_show_icon).setEnabled(show != AdapterApp.enumShow.none);
+ this.menu.findItem(R.id.menu_show_all).setEnabled(show != AdapterApp.enumShow.none);
+ switch (show) {
+ case user:
+ this.menu.findItem(R.id.menu_show_user).setChecked(true);
+ break;
+ case icon:
+ this.menu.findItem(R.id.menu_show_icon).setChecked(true);
+ break;
+ case all:
+ this.menu.findItem(R.id.menu_show_all).setChecked(true);
+ break;
+ }
+ return true;
+
+ case R.id.menu_show_user:
+ case R.id.menu_show_icon:
+ case R.id.menu_show_all:
+ item.setChecked(!item.isChecked());
+ final AdapterApp.enumShow set;
+ switch (item.getItemId()) {
+ case R.id.menu_show_user:
+ set = AdapterApp.enumShow.user;
+ break;
+ case R.id.menu_show_all:
+ set = AdapterApp.enumShow.all;
+ break;
+ default:
+ set = AdapterApp.enumShow.icon;
+ break;
+ }
+ fragmentMain.setShow(set);
+ executor.submit(new Runnable() {
+ @Override
+ public void run() {
+ XProvider.putSetting(ActivityMain.this, "global", "show", set.name());
+ }
+ });
+ return true;
+
case R.id.menu_help:
menuHelp();
return true;
@@ -309,8 +378,10 @@ private void menuHelp() {
startActivity(new Intent(this, ActivityHelp.class));
}
- public void updateMenu() {
+ public void updateMenu(Menu menu) {
// Search
+ MenuItem menuSearch = menu.findItem(R.id.menu_search);
+ final SearchView searchView = (SearchView) menuSearch.getActionView();
if (searchView != null) {
String pkg = getIntent().getStringExtra(EXTRA_SEARCH_PACKAGE);
if (pkg != null) {
@@ -365,11 +436,11 @@ public void onDismiss(DialogInterface dialogInterface) {
}
private static class DrawerItem {
- private int id;
- private String title;
- private boolean checkable;
+ private final int id;
+ private final String title;
+ private final boolean checkable;
private boolean checked;
- private IListener listener;
+ private final IListener listener;
DrawerItem(Context context, int title, IListener listener) {
this.id = title;
@@ -416,7 +487,7 @@ interface IListener {
}
private static class ArrayAdapterDrawer extends ArrayAdapter {
- private int resource;
+ private final int resource;
ArrayAdapterDrawer(@NonNull Context context, int resource) {
super(context, resource);
diff --git a/app/src/main/java/eu/faircode/xlua/AdapterApp.java b/app/src/main/java/eu/faircode/xlua/AdapterApp.java
index 947a27be..508c2b07 100644
--- a/app/src/main/java/eu/faircode/xlua/AdapterApp.java
+++ b/app/src/main/java/eu/faircode/xlua/AdapterApp.java
@@ -1,46 +1,49 @@
/*
- This file is part of XPrivacy/Lua.
+ This file is part of XPrivacyLua.
- XPrivacy/Lua is free software: you can redistribute it and/or modify
+ XPrivacyLua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- XPrivacy/Lua is distributed in the hope that it will be useful,
+ XPrivacyLua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
+ along with XPrivacyLua. If not, see .
- Copyright 2017-2018 Marcel Bokhorst (M66B)
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
*/
package eu.faircode.xlua;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Process;
-import android.support.design.widget.Snackbar;
-import android.support.v7.util.DiffUtil;
-import android.support.v7.util.ListUpdateCallback;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
+import com.bumptech.glide.load.DecodeFormat;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.request.RequestOptions;
+import com.google.android.material.snackbar.Snackbar;
+
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
@@ -52,35 +55,48 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import androidx.appcompat.widget.AppCompatCheckBox;
+import androidx.constraintlayout.widget.Group;
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
public class AdapterApp extends RecyclerView.Adapter implements Filterable {
private static final String TAG = "XLua.App";
private int iconSize;
- private boolean showAll = false;
+ public enum enumShow {none, user, icon, all}
+
+ private enumShow show = enumShow.icon;
+ private String group = null;
private CharSequence query = null;
- private List hooks;
+ private List collection = new ArrayList<>();
+ private boolean dataChanged = false;
+ private List hooks = new ArrayList<>();
private List all = new ArrayList<>();
private List filtered = new ArrayList<>();
private Map expanded = new HashMap<>();
- private ExecutorService executor = Executors.newCachedThreadPool();
+ private ExecutorService executor = Executors.newSingleThreadExecutor();
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, View.OnLongClickListener, CompoundButton.OnCheckedChangeListener, XApp.IListener {
- XApp app;
-
- View itemView;
- ImageView ivExpander;
- ImageView ivIcon;
- TextView tvLabel;
- TextView tvUid;
- TextView tvPackage;
- ImageView ivPersistent;
- CheckBox cbAssigned;
- RecyclerView rvGroup;
-
- AdapterGroup adapter;
+ final View itemView;
+ final ImageView ivExpander;
+ final ImageView ivIcon;
+ final TextView tvLabel;
+ final TextView tvUid;
+ final TextView tvPackage;
+ final ImageView ivPersistent;
+ final ImageView ivSettings;
+ final TextView tvAndroid;
+ final AppCompatCheckBox cbAssigned;
+ final AppCompatCheckBox cbForceStop;
+ final RecyclerView rvGroup;
+ final Group grpExpanded;
+
+ final AdapterGroup adapter;
ViewHolder(View itemView) {
super(itemView);
@@ -92,7 +108,10 @@ public class ViewHolder extends RecyclerView.ViewHolder
tvUid = itemView.findViewById(R.id.tvUid);
tvPackage = itemView.findViewById(R.id.tvPackage);
ivPersistent = itemView.findViewById(R.id.ivPersistent);
+ ivSettings = itemView.findViewById(R.id.ivSettings);
+ tvAndroid = itemView.findViewById(R.id.tvAndroid);
cbAssigned = itemView.findViewById(R.id.cbAssigned);
+ cbForceStop = itemView.findViewById(R.id.cbForceStop);
rvGroup = itemView.findViewById(R.id.rvGroup);
rvGroup.setHasFixedSize(true);
@@ -101,100 +120,131 @@ public class ViewHolder extends RecyclerView.ViewHolder
rvGroup.setLayoutManager(llm);
adapter = new AdapterGroup();
rvGroup.setAdapter(adapter);
+
+ grpExpanded = itemView.findViewById(R.id.grpExpanded);
}
private void wire() {
- ivExpander.setOnClickListener(this);
- ivIcon.setOnClickListener(this);
- tvLabel.setOnClickListener(this);
- tvUid.setOnClickListener(this);
- tvPackage.setOnClickListener(this);
-
- ivIcon.setOnLongClickListener(this);
- tvLabel.setOnLongClickListener(this);
- tvUid.setOnLongClickListener(this);
- tvPackage.setOnLongClickListener(this);
-
+ itemView.setOnClickListener(this);
+ itemView.setOnLongClickListener(this);
+ ivSettings.setOnClickListener(this);
cbAssigned.setOnCheckedChangeListener(this);
+ cbForceStop.setOnCheckedChangeListener(this);
}
private void unwire() {
- ivExpander.setOnClickListener(null);
- ivIcon.setOnClickListener(null);
- tvLabel.setOnClickListener(null);
- tvUid.setOnClickListener(null);
- tvPackage.setOnClickListener(null);
-
- ivIcon.setOnLongClickListener(null);
- tvLabel.setOnLongClickListener(null);
- tvUid.setOnLongClickListener(null);
- tvPackage.setOnLongClickListener(null);
-
+ itemView.setOnClickListener(null);
+ itemView.setOnLongClickListener(null);
+ ivSettings.setOnClickListener(null);
cbAssigned.setOnCheckedChangeListener(null);
+ cbForceStop.setOnCheckedChangeListener(null);
}
@Override
public void onClick(View view) {
- int id = view.getId();
- if (id == R.id.ivExpander ||
- id == R.id.ivIcon || id == R.id.tvLabel ||
- id == R.id.tvUid || id == R.id.tvPackage) {
- if (!expanded.containsKey(app.packageName))
- expanded.put(app.packageName, false);
- expanded.put(app.packageName, !expanded.get(app.packageName));
- updateExpand();
+ XApp app = filtered.get(getAdapterPosition());
+ switch (view.getId()) {
+ case R.id.itemView:
+ if (!expanded.containsKey(app.packageName))
+ expanded.put(app.packageName, false);
+ expanded.put(app.packageName, !expanded.get(app.packageName));
+ updateExpand();
+ break;
+
+ case R.id.ivSettings:
+ PackageManager pm = view.getContext().getPackageManager();
+ Intent settings = pm.getLaunchIntentForPackage(Util.PRO_PACKAGE_NAME);
+ if (settings == null) {
+ Intent browse = new Intent(Intent.ACTION_VIEW);
+ browse.setData(Uri.parse("https://lua.xprivacy.eu/pro/"));
+ if (browse.resolveActivity(pm) == null)
+ Snackbar.make(view, view.getContext().getString(R.string.msg_no_browser), Snackbar.LENGTH_LONG).show();
+ else
+ view.getContext().startActivity(browse);
+ } else {
+ settings.putExtra("packageName", app.packageName);
+ view.getContext().startActivity(settings);
+ }
+ break;
}
}
@Override
public boolean onLongClick(View view) {
- Intent intent = view.getContext().getPackageManager().getLaunchIntentForPackage(app.packageName);
- if (intent != null)
- view.getContext().startActivity(intent);
- return (intent != null);
+ XApp app = filtered.get(getAdapterPosition());
+ Intent launch = view.getContext().getPackageManager().getLaunchIntentForPackage(app.packageName);
+ if (launch != null)
+ view.getContext().startActivity(launch);
+ return true;
}
@Override
- public void onCheckedChanged(CompoundButton compoundButton, final boolean checked) {
+ public void onCheckedChanged(final CompoundButton compoundButton, boolean checked) {
Log.i(TAG, "Check changed");
- int id = compoundButton.getId();
- if (id == R.id.cbAssigned) {
- if (checked) {
- for (XHook hook : hooks)
- app.assignments.add(new XAssignment(hook));
- } else
- app.assignments.clear();
-
- adapter.set(app, hooks);
-
- executor.submit(new Runnable() {
- @Override
- public void run() {
- List hookids = new ArrayList<>();
- for (XHook hook : hooks)
- hookids.add(hook.getId());
- try {
- XService.getClient().assignHooks(
- hookids, app.packageName, app.uid, !checked, !app.persistent);
- } catch (Throwable ex) {
- Log.e(TAG, Log.getStackTraceString(ex));
- Snackbar.make(itemView, ex.toString(), Snackbar.LENGTH_LONG).show();
+ final XApp app = filtered.get(getAdapterPosition());
+
+ switch (compoundButton.getId()) {
+ case R.id.cbAssigned:
+ updateAssignments(compoundButton.getContext(), app, group, checked);
+ notifyItemChanged(getAdapterPosition());
+ break;
+
+ case R.id.cbForceStop:
+ app.forceStop = checked;
+ executor.submit(new Runnable() {
+ @Override
+ public void run() {
+ XProvider.putSettingBoolean(
+ compoundButton.getContext(), app.packageName, "forcestop", app.forceStop);
}
- }
- });
+ });
+ break;
}
}
@Override
- public void onChange() {
+ public void onAssign(Context context, String groupName, boolean assign) {
Log.i(TAG, "Group changed");
+ XApp app = filtered.get(getAdapterPosition());
+ updateAssignments(context, app, groupName, assign);
notifyItemChanged(getAdapterPosition());
}
+ private void updateAssignments(final Context context, final XApp app, String groupName, final boolean assign) {
+ Log.i(TAG, app.packageName + " " + groupName + "=" + assign);
+
+ final ArrayList hookids = new ArrayList<>();
+ for (XHook hook : hooks)
+ if (hook.isAvailable(app.packageName, collection) &&
+ (groupName == null || groupName.equals(hook.getGroup()))) {
+ hookids.add(hook.getId());
+ if (assign)
+ app.assignments.add(new XAssignment(hook));
+ else
+ app.assignments.remove(new XAssignment(hook));
+ }
+
+ executor.submit(new Runnable() {
+ @Override
+ public void run() {
+ Bundle args = new Bundle();
+ args.putStringArrayList("hooks", hookids);
+ args.putString("packageName", app.packageName);
+ args.putInt("uid", app.uid);
+ args.putBoolean("delete", !assign);
+ args.putBoolean("kill", app.forceStop);
+ context.getContentResolver()
+ .call(XProvider.getURI(), "xlua", "assignHooks", args);
+ }
+ });
+ }
+
void updateExpand() {
- boolean isExpanded = (expanded.containsKey(app.packageName) && expanded.get(app.packageName));
+ XApp app = filtered.get(getAdapterPosition());
+ boolean isExpanded = (group == null && expanded.containsKey(app.packageName) && expanded.get(app.packageName));
ivExpander.setImageLevel(isExpanded ? 1 : 0);
- rvGroup.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
+ ivExpander.setVisibility(group == null ? View.VISIBLE : View.INVISIBLE);
+ grpExpanded.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
}
}
@@ -207,9 +257,21 @@ void updateExpand() {
setHasStableIds(true);
}
- void set(boolean showAll, String query, List hooks, List apps) {
- this.showAll = showAll;
- this.query = query;
+ void set(List collection, List hooks, List apps) {
+ this.dataChanged = (this.hooks.size() != hooks.size());
+ for (int i = 0; i < this.hooks.size() && !this.dataChanged; i++) {
+ XHook hook = this.hooks.get(i);
+ XHook other = hooks.get(i);
+ if (!hook.getGroup().equals(other.getGroup()) || !hook.getId().equals(other.getId()))
+ this.dataChanged = true;
+ }
+
+ Log.i(TAG, "Set collections=" + collection.size() +
+ " hooks=" + hooks.size() +
+ " apps=" + apps.size() +
+ " changed=" + this.dataChanged);
+
+ this.collection = collection;
this.hooks = hooks;
final Collator collator = Collator.getInstance(Locale.getDefault());
@@ -228,13 +290,80 @@ public int compare(XApp app1, XApp app2) {
getFilter().filter(query);
}
- void setShowAll(boolean value) {
- if (showAll != value) {
- showAll = value;
+ void setShow(enumShow value) {
+ if (show != value) {
+ show = value;
+ getFilter().filter(query);
+ }
+ }
+
+ void setGroup(String name) {
+ if (group == null ? name != null : !group.equals(name)) {
+ group = name;
+ this.dataChanged = true;
getFilter().filter(query);
}
}
+ void restrict(final Context context) {
+ final List actions = new ArrayList<>();
+
+ boolean revert = false;
+ for (XApp app : filtered)
+ for (XHook hook : hooks)
+ if (group == null || group.equals(hook.getGroup())) {
+ XAssignment assignment = new XAssignment(hook);
+ if (app.assignments.contains(assignment)) {
+ revert = true;
+ break;
+ }
+ }
+ Log.i(TAG, "revert=" + revert);
+
+ for (XApp app : filtered) {
+ ArrayList hookids = new ArrayList<>();
+
+ for (XHook hook : hooks)
+ if (hook.isAvailable(app.packageName, this.collection) &&
+ (group == null || group.equals(hook.getGroup()))) {
+ XAssignment assignment = new XAssignment(hook);
+ if (revert) {
+ if (app.assignments.contains(assignment)) {
+ hookids.add(hook.getId());
+ app.assignments.remove(assignment);
+ }
+ } else {
+ if (!app.assignments.contains(assignment)) {
+ hookids.add(hook.getId());
+ app.assignments.add(assignment);
+ }
+ }
+ }
+
+ if (hookids.size() > 0) {
+ Log.i(TAG, "Applying " + group + "=" + hookids.size() + "=" + revert + " package=" + app.packageName);
+ Bundle args = new Bundle();
+ args.putStringArrayList("hooks", hookids);
+ args.putString("packageName", app.packageName);
+ args.putInt("uid", app.uid);
+ args.putBoolean("delete", revert);
+ args.putBoolean("kill", app.forceStop);
+ actions.add(args);
+ }
+ }
+
+ notifyDataSetChanged();
+
+ executor.submit(new Runnable() {
+ @Override
+ public void run() {
+ for (Bundle args : actions)
+ context.getContentResolver()
+ .call(XProvider.getURI(), "xlua", "assignHooks", args);
+ }
+ });
+ }
+
@Override
public Filter getFilter() {
return new Filter() {
@@ -245,30 +374,72 @@ protected FilterResults performFiltering(CharSequence query) {
AdapterApp.this.query = query;
List visible = new ArrayList<>();
- if (showAll || !TextUtils.isEmpty(query))
+ if (show == enumShow.all || !TextUtils.isEmpty(query))
visible.addAll(all);
else
for (XApp app : all)
- if (app.uid > Process.FIRST_APPLICATION_UID && app.icon > 0 && app.enabled)
+ if (app.uid > Process.FIRST_APPLICATION_UID && app.enabled &&
+ (show == enumShow.icon ? app.icon > 0 : !app.system))
visible.add(app);
List results = new ArrayList<>();
+
if (TextUtils.isEmpty(query))
results.addAll(visible);
else {
- query = query.toString().toLowerCase().trim();
+ String q = query.toString().toLowerCase().trim();
+
+ boolean restricted = false;
+ boolean unrestricted = false;
+ boolean system = false;
+ boolean user = false;
+
+ while (true) {
+ if (q.startsWith("!")) {
+ restricted = true;
+ q = q.substring(1);
+ continue;
+ } else if (q.startsWith("?")) {
+ unrestricted = true;
+ q = q.substring(1);
+ continue;
+ } else if (q.startsWith("#")) {
+ system = true;
+ q = q.substring(1);
+ continue;
+ } else if (q.startsWith("@")) {
+ user = true;
+ q = q.substring(1);
+ continue;
+ }
+ break;
+ }
+
int uid;
try {
- uid = Integer.parseInt(query.toString());
+ uid = Integer.parseInt(q);
} catch (NumberFormatException ignore) {
uid = -1;
}
- for (XApp app : visible)
+ for (XApp app : visible) {
+ if (restricted || unrestricted) {
+ int assigments = app.getAssignments(group).size();
+ if (restricted && assigments == 0)
+ continue;
+ if (unrestricted && assigments > 0)
+ continue;
+ }
+ if (system && !app.system)
+ continue;
+ if (user && app.system)
+ continue;
+
if (app.uid == uid ||
- app.packageName.toLowerCase().contains(query) ||
- (app.label != null && app.label.toLowerCase().contains(query)))
+ app.packageName.toLowerCase().contains(q) ||
+ (app.label != null && app.label.toLowerCase().contains(q)))
results.add(app);
+ }
}
if (results.size() == 1) {
@@ -292,46 +463,27 @@ protected void publishResults(CharSequence query, FilterResults result) {
: (List) result.values);
Log.i(TAG, "Filtered apps count=" + apps.size());
- DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new AppDiffCallback(expanded1, filtered, apps));
-
- filtered.clear();
- filtered.addAll(apps);
- notifyDataSetChanged();
-
- diff.dispatchUpdatesTo(AdapterApp.this);
-
- diff.dispatchUpdatesTo(new ListUpdateCallback() {
- @Override
- public void onInserted(int position, int count) {
- Log.i(TAG, "Inserted " + count + " @" + position);
- }
-
- @Override
- public void onRemoved(int position, int count) {
- Log.i(TAG, "Removed " + count + " @" + position);
- }
-
- @Override
- public void onMoved(int fromPosition, int toPosition) {
- Log.i(TAG, "Moved from" + fromPosition + " to " + toPosition);
- }
-
- @Override
- public void onChanged(int position, int count, Object payload) {
- Log.i(TAG, "Changed " + count + " @" + position);
- }
- });
+ if (dataChanged) {
+ dataChanged = false;
+ filtered = apps;
+ notifyDataSetChanged();
+ } else {
+ DiffUtil.DiffResult diff =
+ DiffUtil.calculateDiff(new AppDiffCallback(expanded1, filtered, apps));
+ filtered = apps;
+ diff.dispatchUpdatesTo(AdapterApp.this);
+ }
}
};
}
private class AppDiffCallback extends DiffUtil.Callback {
- private boolean expanded1;
- private List prev;
- private List next;
+ private final boolean refresh;
+ private final List prev;
+ private final List next;
- AppDiffCallback(boolean expanded1, List prev, List next) {
- this.expanded1 = expanded1;
+ AppDiffCallback(boolean refresh, List prev, List next) {
+ this.refresh = refresh;
this.prev = prev;
this.next = next;
}
@@ -348,8 +500,10 @@ public int getNewListSize() {
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
- return (!expanded1 &&
- prev.get(oldItemPosition).packageName.equals(next.get(newItemPosition).packageName));
+ XApp app1 = prev.get(oldItemPosition);
+ XApp app2 = next.get(newItemPosition);
+
+ return (!refresh && app1.packageName.equals(app2.packageName) && app1.uid == app2.uid);
}
@Override
@@ -357,16 +511,14 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
XApp app1 = prev.get(oldItemPosition);
XApp app2 = next.get(newItemPosition);
- if (!app1.packageName.equals(app2.packageName) ||
- app1.uid != app2.uid ||
- app1.icon != app2.icon ||
+ if (app1.icon != app2.icon ||
!app1.label.equals(app2.label) ||
app1.enabled != app2.enabled ||
app1.persistent != app2.persistent ||
- app1.assignments.size() != app2.assignments.size())
+ app1.getAssignments(group).size() != app2.getAssignments(group).size())
return false;
- for (XAssignment a1 : app1.assignments) {
+ for (XAssignment a1 : app1.getAssignments(group)) {
int i2 = app2.assignments.indexOf(a1); // by hookid
if (i2 < 0)
return false;
@@ -383,8 +535,8 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
@Override
public long getItemId(int position) {
- XApp assigment = filtered.get(position);
- return assigment.packageName.hashCode() << 32 | assigment.uid;
+ XApp assignment = filtered.get(position);
+ return ((long) assignment.packageName.hashCode()) << 32 | assignment.uid;
}
@Override
@@ -400,39 +552,56 @@ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.unwire();
- holder.app = filtered.get(position);
- holder.app.setListener(holder);
+ XApp app = filtered.get(position);
+ app.setListener(holder);
+
+ Resources resources = holder.itemView.getContext().getResources();
// App icon
- if (holder.app.icon <= 0)
+ if (app.icon <= 0)
holder.ivIcon.setImageResource(android.R.drawable.sym_def_app_icon);
else {
- Uri uri = Uri.parse("android.resource://" + holder.app.packageName + "/" + holder.app.icon);
+ Uri uri = Uri.parse("android.resource://" + app.packageName + "/" + app.icon);
GlideApp.with(holder.itemView.getContext())
+ .applyDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_RGB_565))
.load(uri)
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .skipMemoryCache(true)
.override(iconSize, iconSize)
.into(holder.ivIcon);
}
// App info
- holder.tvLabel.setText(holder.app.label);
- holder.tvUid.setText(Integer.toString(holder.app.uid));
- holder.tvPackage.setText(holder.app.packageName);
- holder.ivPersistent.setVisibility(holder.app.persistent ? View.VISIBLE : View.GONE);
+ holder.itemView.setBackgroundColor(app.system
+ ? Util.resolveColor(holder.itemView.getContext(), R.attr.colorSystem)
+ : resources.getColor(android.R.color.transparent, null));
+ holder.tvLabel.setText(app.label);
+ holder.tvUid.setText(Integer.toString(app.uid));
+ holder.tvPackage.setText(app.packageName);
+ holder.ivPersistent.setVisibility(app.persistent ? View.VISIBLE : View.GONE);
+
+ List selectedHooks = new ArrayList<>();
+ for (XHook hook : hooks)
+ if (hook.isAvailable(app.packageName, collection) &&
+ (group == null || group.equals(hook.getGroup())))
+ selectedHooks.add(hook);
// Assignment info
- holder.cbAssigned.setChecked(holder.app.assignments.size() > 0);
- holder.cbAssigned.setEnabled(
- holder.app.assignments.size() == 0 || holder.app.assignments.size() == hooks.size());
- holder.adapter.set(holder.app, hooks);
+ holder.cbAssigned.setChecked(app.getAssignments(group).size() > 0);
+ holder.cbAssigned.setButtonTintList(ColorStateList.valueOf(resources.getColor(
+ selectedHooks.size() > 0 && app.getAssignments(group).size() == selectedHooks.size()
+ ? R.color.colorAccent
+ : android.R.color.darker_gray, null)));
+
+ holder.tvAndroid.setVisibility("android".equals(app.packageName) ? View.VISIBLE : View.GONE);
+
+ holder.cbForceStop.setChecked(app.forceStop);
+ holder.cbForceStop.setEnabled(!app.persistent);
+
+ holder.adapter.set(app, selectedHooks, holder.itemView.getContext());
holder.updateExpand();
holder.wire();
}
-
- @Override
- public void onViewRecycled(ViewHolder holder) {
- holder.unwire();
- }
}
diff --git a/app/src/main/java/eu/faircode/xlua/AdapterGroup.java b/app/src/main/java/eu/faircode/xlua/AdapterGroup.java
index 309d7d4e..ddcc0b63 100644
--- a/app/src/main/java/eu/faircode/xlua/AdapterGroup.java
+++ b/app/src/main/java/eu/faircode/xlua/AdapterGroup.java
@@ -1,36 +1,32 @@
/*
- This file is part of XPrivacy/Lua.
+ This file is part of XPrivacyLua.
- XPrivacy/Lua is free software: you can redistribute it and/or modify
+ XPrivacyLua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- XPrivacy/Lua is distributed in the hope that it will be useful,
+ XPrivacyLua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
+ along with XPrivacyLua. If not, see .
- Copyright 2017-2018 Marcel Bokhorst (M66B)
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
*/
package eu.faircode.xlua;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.format.DateUtils;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
@@ -43,29 +39,25 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.AppCompatCheckBox;
+import androidx.recyclerview.widget.RecyclerView;
public class AdapterGroup extends RecyclerView.Adapter {
private static final String TAG = "XLua.Group";
private XApp app;
- private List groups;
- private Map> hooks;
-
- private ExecutorService executor = Executors.newCachedThreadPool();
+ private List groups = new ArrayList<>();
public class ViewHolder extends RecyclerView.ViewHolder
implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
- String group;
- List hooks;
-
- View itemView;
- ImageView ivException;
- ImageView ivInstalled;
- TextView tvUsed;
- TextView tvGroup;
- CheckBox cbAssigned;
+ final View itemView;
+ final ImageView ivException;
+ final ImageView ivInstalled;
+ final TextView tvUsed;
+ final TextView tvGroup;
+ final AppCompatCheckBox cbAssigned;
ViewHolder(View itemView) {
super(itemView);
@@ -92,17 +84,21 @@ private void unwire() {
@Override
public void onClick(View view) {
+ Group group = groups.get(getAdapterPosition());
switch (view.getId()) {
case R.id.ivException:
StringBuilder sb = new StringBuilder();
- for (XAssignment assignment : app.assignments)
- if (assignment.hook.getGroup().equals(group))
+ for (XAssignment assignment : app.getAssignments(group.name))
+ if (assignment.hook.getGroup().equals(group.name))
if (assignment.exception != null) {
sb.append("");
sb.append(Html.escapeHtml(assignment.hook.getId()));
- sb.append(" ");
- sb.append(Html.escapeHtml(assignment.exception));
- sb.append(" ");
+ sb.append("
");
+ for (String line : assignment.exception.split("\n")) {
+ sb.append(Html.escapeHtml(line));
+ sb.append(" ");
+ }
+ sb.append("
");
}
LayoutInflater inflater = LayoutInflater.from(view.getContext());
@@ -123,60 +119,66 @@ public void onClick(View view) {
}
@Override
- public void onCheckedChanged(CompoundButton compoundButton, final boolean checked) {
+ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
+ final Group group = groups.get(getAdapterPosition());
switch (compoundButton.getId()) {
case R.id.cbAssigned:
- for (XHook hook : hooks)
- app.assignments.remove(new XAssignment(hook));
- if (checked)
- for (XHook hook : hooks)
- app.assignments.add(new XAssignment(hook));
- app.notifyChanged();
-
- executor.submit(new Runnable() {
- @Override
- public void run() {
- List hookids = new ArrayList<>();
- for (XHook hook : hooks)
- hookids.add(hook.getId());
- try {
- XService.getClient().assignHooks(
- hookids, app.packageName, app.uid, !checked, !app.persistent);
- } catch (Throwable ex) {
- Log.e(TAG, Log.getStackTraceString(ex));
- Snackbar.make(itemView, ex.toString(), Snackbar.LENGTH_LONG).show();
- }
- }
- });
+ app.notifyAssign(compoundButton.getContext(), group.name, checked);
break;
}
}
}
AdapterGroup() {
- setHasStableIds(false);
+ setHasStableIds(true);
}
- void set(XApp app, List hooks) {
+ void set(XApp app, List hooks, Context context) {
this.app = app;
- this.groups = new ArrayList<>();
- this.hooks = new HashMap<>();
+ Map map = new HashMap<>();
for (XHook hook : hooks) {
- if (!this.groups.contains(hook.getGroup())) {
- this.groups.add(hook.getGroup());
- this.hooks.put(hook.getGroup(), new ArrayList());
+ Group group;
+ if (map.containsKey(hook.getGroup()))
+ group = map.get(hook.getGroup());
+ else {
+ group = new Group();
+
+ Resources resources = context.getResources();
+ String name = hook.getGroup().toLowerCase().replaceAll("[^a-z]", "_");
+ group.id = resources.getIdentifier("group_" + name, "string", context.getPackageName());
+ group.name = hook.getGroup();
+ group.title = (group.id > 0 ? resources.getString(group.id) : hook.getGroup());
+
+ map.put(hook.getGroup(), group);
}
- this.hooks.get(hook.getGroup()).add(hook);
+ group.hooks.add(hook);
}
+ for (String groupid : map.keySet()) {
+ for (XAssignment assignment : app.assignments)
+ if (assignment.hook.getGroup().equals(groupid)) {
+ Group group = map.get(groupid);
+ if (assignment.exception != null)
+ group.exception = true;
+ if (assignment.installed >= 0)
+ group.installed++;
+ if (assignment.hook.isOptional())
+ group.optional++;
+ if (assignment.restricted)
+ group.used = Math.max(group.used, assignment.used);
+ group.assigned++;
+ }
+ }
+
+ this.groups = new ArrayList<>(map.values());
+
final Collator collator = Collator.getInstance(Locale.getDefault());
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc
-
- Collections.sort(this.groups, new Comparator() {
+ Collections.sort(this.groups, new Comparator() {
@Override
- public int compare(String group1, String group2) {
- return collator.compare(group1, group2);
+ public int compare(Group group1, Group group2) {
+ return collator.compare(group1.title, group2.title);
}
});
@@ -185,7 +187,7 @@ public int compare(String group1, String group2) {
@Override
public long getItemId(int position) {
- return -1;
+ return groups.get(position).id;
}
@Override
@@ -201,39 +203,62 @@ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.unwire();
- holder.group = groups.get(position);
- holder.hooks = hooks.get(holder.group);
-
- boolean exception = false;
- boolean installed = true;
- long used = -1;
- boolean assigned = false;
- for (XAssignment assignment : app.assignments)
- if (assignment.hook.getGroup().equals(holder.group)) {
- if (assignment.exception != null)
- exception = true;
- if (assignment.installed < 0)
- installed = false;
- if (assignment.restricted)
- used = Math.max(used, assignment.used);
- assigned = true;
- }
+ Group group = groups.get(position);
+ // Get localized group name
Context context = holder.itemView.getContext();
Resources resources = holder.itemView.getContext().getResources();
- String name = holder.group.toLowerCase().replaceAll("[^a-z]", "_");
- int resId = resources.getIdentifier("group_" + name, "string", context.getPackageName());
- if (resId > 0)
- name = resources.getString(resId);
-
- holder.ivException.setVisibility(exception && assigned ? View.VISIBLE : View.GONE);
- holder.ivInstalled.setVisibility(installed && assigned ? View.VISIBLE : View.GONE);
- holder.tvUsed.setVisibility(used < 0 ? View.GONE : View.VISIBLE);
- holder.tvUsed.setText(used < 0 ? "" : DateUtils.formatDateTime(context, used,
+
+ holder.ivException.setVisibility(group.hasException() ? View.VISIBLE : View.GONE);
+ holder.ivInstalled.setVisibility(group.hasInstalled() ? View.VISIBLE : View.GONE);
+ holder.ivInstalled.setAlpha(group.allInstalled() ? 1.0f : 0.5f);
+ holder.tvUsed.setVisibility(group.lastUsed() < 0 ? View.GONE : View.VISIBLE);
+ holder.tvUsed.setText(DateUtils.formatDateTime(context, group.lastUsed(),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL));
- holder.tvGroup.setText(name);
- holder.cbAssigned.setChecked(assigned);
+ holder.tvGroup.setText(group.title);
+ holder.cbAssigned.setChecked(group.hasAssigned());
+ holder.cbAssigned.setButtonTintList(ColorStateList.valueOf(resources.getColor(
+ group.allAssigned() ? R.color.colorAccent : android.R.color.darker_gray, null)));
holder.wire();
}
+
+ private class Group {
+ int id;
+ String name;
+ String title;
+ boolean exception = false;
+ int installed = 0;
+ int optional = 0;
+ long used = -1;
+ int assigned = 0;
+ List hooks = new ArrayList<>();
+
+ Group() {
+ }
+
+ boolean hasException() {
+ return (assigned > 0 && exception);
+ }
+
+ boolean hasInstalled() {
+ return (assigned > 0 && installed > 0);
+ }
+
+ boolean allInstalled() {
+ return (assigned > 0 && installed + optional == assigned);
+ }
+
+ long lastUsed() {
+ return used;
+ }
+
+ boolean hasAssigned() {
+ return (assigned > 0);
+ }
+
+ boolean allAssigned() {
+ return (assigned == hooks.size());
+ }
+ }
}
diff --git a/app/src/main/java/eu/faircode/xlua/ApplicationEx.java b/app/src/main/java/eu/faircode/xlua/ApplicationEx.java
new file mode 100644
index 00000000..cb39ea5d
--- /dev/null
+++ b/app/src/main/java/eu/faircode/xlua/ApplicationEx.java
@@ -0,0 +1,33 @@
+/*
+ This file is part of XPrivacyLua.
+
+ XPrivacyLua is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ XPrivacyLua is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with XPrivacyLua. If not, see .
+
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
+ */
+
+package eu.faircode.xlua;
+
+import android.app.Application;
+import android.util.Log;
+
+public class ApplicationEx extends Application {
+ private static final String TAG = "XLua.App";
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.i(TAG, "Create version=" + BuildConfig.VERSION_NAME);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/faircode/xlua/FragmentMain.java b/app/src/main/java/eu/faircode/xlua/FragmentMain.java
index d2929e13..4136fbda 100644
--- a/app/src/main/java/eu/faircode/xlua/FragmentMain.java
+++ b/app/src/main/java/eu/faircode/xlua/FragmentMain.java
@@ -1,20 +1,20 @@
/*
- This file is part of XPrivacy/Lua.
+ This file is part of XPrivacyLua.
- XPrivacy/Lua is free software: you can redistribute it and/or modify
+ XPrivacyLua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- XPrivacy/Lua is distributed in the hope that it will be useful,
+ XPrivacyLua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
+ along with XPrivacyLua. If not, see .
- Copyright 2017-2018 Marcel Bokhorst (M66B)
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
*/
package eu.faircode.xlua;
@@ -23,46 +23,138 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.Cursor;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.Fragment;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
+import android.os.Parcel;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+import com.google.android.material.snackbar.Snackbar;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.Locale;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.Group;
+import androidx.fragment.app.Fragment;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.AsyncTaskLoader;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
public class FragmentMain extends Fragment {
- private final static String TAG = "XLua.Main";
+ private final static String TAG = "XLua.Fragment";
- private boolean showAll = false;
- private String query = null;
+ private ProgressBar pbApplication;
+ private Spinner spGroup;
+ private ArrayAdapter spAdapter;
+ private Button btnRestrict;
+ private TextView tvRestrict;
+ private Group grpApplication;
+ private SwipeRefreshLayout swipeRefresh;
private AdapterApp rvAdapter;
- private ExecutorService executor = Executors.newCachedThreadPool();
+ private AdapterApp.enumShow show = AdapterApp.enumShow.none;
@Override
@Nullable
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
- View main = inflater.inflate(R.layout.restrictions, container, false);
+ final View main = inflater.inflate(R.layout.restrictions, container, false);
+
+ pbApplication = main.findViewById(R.id.pbApplication);
+ btnRestrict = main.findViewById(R.id.btnRestrict);
+ tvRestrict = main.findViewById(R.id.tvRestrict);
+ grpApplication = main.findViewById(R.id.grpApplication);
+
+ int colorAccent = Util.resolveColor(getContext(), R.attr.colorAccent);
+
+ swipeRefresh = main.findViewById(R.id.swipeRefresh);
+ swipeRefresh.setColorSchemeColors(colorAccent, colorAccent, colorAccent);
+ swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+ @Override
+ public void onRefresh() {
+ loadData();
+ }
+ });
// Initialize app list
RecyclerView rvApplication = main.findViewById(R.id.rvApplication);
rvApplication.setHasFixedSize(false);
- LinearLayoutManager llm = new LinearLayoutManager(getActivity());
+ LinearLayoutManager llm = new LinearLayoutManager(getActivity()) {
+ @Override
+ public boolean onRequestChildFocus(RecyclerView parent, RecyclerView.State state, View child, View focused) {
+ return true;
+ }
+ };
llm.setAutoMeasureEnabled(true);
rvApplication.setLayoutManager(llm);
rvAdapter = new AdapterApp(getActivity());
rvApplication.setAdapter(rvAdapter);
+ spAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item);
+ spAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+ spGroup = main.findViewById(R.id.spGroup);
+ spGroup.setTag(null);
+ spGroup.setAdapter(spAdapter);
+ spGroup.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ updateSelection();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> adapterView) {
+ updateSelection();
+ }
+
+ private void updateSelection() {
+ XGroup selected = (XGroup) spGroup.getSelectedItem();
+ String group = (selected == null ? null : selected.name);
+
+ if (group == null ? spGroup.getTag() != null : !group.equals(spGroup.getTag())) {
+ Log.i(TAG, "Select group=" + group);
+ spGroup.setTag(group);
+ rvAdapter.setGroup(group);
+ }
+
+ tvRestrict.setVisibility(group == null ? View.VISIBLE : View.GONE);
+ btnRestrict.setVisibility(group == null ? View.INVISIBLE : View.VISIBLE);
+ }
+ });
+
+ btnRestrict.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ XGroup selected = (XGroup) spGroup.getSelectedItem();
+ Util.areYouSure(
+ (ActivityBase) getActivity(),
+ getString(R.string.msg_restrict_sure, selected.title),
+ new Util.DoubtListener() {
+ @Override
+ public void onSure() {
+ rvAdapter.restrict(getContext());
+ }
+ });
+ }
+ });
+
return main;
}
@@ -70,87 +162,227 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
public void onResume() {
super.onResume();
- try {
- XService.getClient().registerEventListener(eventListener);
- } catch (Throwable ex) {
- Log.e(TAG, Log.getStackTraceString(ex));
- }
-
- // Listen for package changes
- IntentFilter piff = new IntentFilter();
- piff.addAction(Intent.ACTION_PACKAGE_ADDED); // installed
- piff.addAction(Intent.ACTION_PACKAGE_CHANGED); // enabled/disabled
- piff.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); // uninstalled
- piff.addDataScheme("package");
- getActivity().registerReceiver(packageChangedReceiver, piff);
+ IntentFilter ifPackage = new IntentFilter();
+ ifPackage.addAction(Intent.ACTION_PACKAGE_ADDED);
+ ifPackage.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ ifPackage.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ ifPackage.addDataScheme("package");
+ getContext().registerReceiver(packageChangedReceiver, ifPackage);
- // Load data
- updateData();
+ loadData();
}
@Override
public void onPause() {
super.onPause();
- try {
- XService.getClient().unregisterEventListener(eventListener);
- } catch (Throwable ex) {
- Log.e(TAG, Log.getStackTraceString(ex));
- }
-
- getActivity().unregisterReceiver(packageChangedReceiver);
+ getContext().unregisterReceiver(packageChangedReceiver);
}
- private void updateData() {
- executor.submit(new Runnable() {
- @Override
- public void run() {
- try {
- IService client = XService.getClient();
- final List hooks = client.getHooks();
- final List apps = client.getApps();
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- rvAdapter.set(showAll, query, hooks, apps);
- }
- });
- } catch (Throwable ex) {
- Log.e(TAG, Log.getStackTraceString(ex));
- Snackbar.make(getView(), ex.toString(), Snackbar.LENGTH_LONG).show();
- }
- }
- });
+ public AdapterApp.enumShow getShow() {
+ return this.show;
}
- public void setShowAll(boolean showAll) {
- this.showAll = showAll;
+ public void setShow(AdapterApp.enumShow value) {
+ this.show = value;
if (rvAdapter != null)
- rvAdapter.setShowAll(showAll);
+ rvAdapter.setShow(value);
}
public void filter(String query) {
- this.query = query;
if (rvAdapter != null)
rvAdapter.getFilter().filter(query);
}
- private IEventListener.Stub eventListener = new IEventListener.Stub() {
+ private void loadData() {
+ Log.i(TAG, "Starting data loader");
+ LoaderManager manager = getActivity().getSupportLoaderManager();
+ manager.restartLoader(ActivityMain.LOADER_DATA, new Bundle(), dataLoaderCallbacks).forceLoad();
+ }
+
+ LoaderManager.LoaderCallbacks dataLoaderCallbacks = new LoaderManager.LoaderCallbacks() {
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ return new DataLoader(getContext());
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, DataHolder data) {
+ if (data.exception == null) {
+ ActivityBase activity = (ActivityBase) getActivity();
+ if (!data.theme.equals(activity.getThemeName()))
+ activity.recreate();
+
+ spAdapter.clear();
+ spAdapter.addAll(data.groups);
+
+ show = data.show;
+ rvAdapter.setShow(data.show);
+ rvAdapter.set(data.collection, data.hooks, data.apps);
+
+ swipeRefresh.setRefreshing(false);
+ pbApplication.setVisibility(View.GONE);
+ grpApplication.setVisibility(View.VISIBLE);
+
+ XGroup selected = (XGroup) spGroup.getSelectedItem();
+ String group = (selected == null ? null : selected.name);
+ tvRestrict.setVisibility(group == null ? View.VISIBLE : View.GONE);
+ btnRestrict.setVisibility(group == null ? View.INVISIBLE : View.VISIBLE);
+ } else {
+ Log.e(TAG, Log.getStackTraceString(data.exception));
+ Snackbar.make(getView(), data.exception.toString(), Snackbar.LENGTH_LONG).show();
+ }
+ }
+
@Override
- public void usageDataChanged() throws RemoteException {
- Log.i(TAG, "Usage data changed");
- updateData();
+ public void onLoaderReset(Loader loader) {
+ // Do nothing
}
};
+ private static class DataLoader extends AsyncTaskLoader {
+ DataLoader(Context context) {
+ super(context);
+ setUpdateThrottle(1000);
+ }
+
+ @Nullable
+ @Override
+ public DataHolder loadInBackground() {
+ Log.i(TAG, "Data loader started");
+ DataHolder data = new DataHolder();
+ try {
+ data.theme = XProvider.getSetting(getContext(), "global", "theme");
+ if (data.theme == null)
+ data.theme = "light";
+
+ // Define hooks
+ if (BuildConfig.DEBUG) {
+ String apk = getContext().getApplicationInfo().publicSourceDir;
+ List hooks = XHook.readHooks(getContext(), apk);
+ Log.i(TAG, "Loaded hooks=" + hooks.size());
+ for (XHook hook : hooks) {
+ Bundle args = new Bundle();
+ args.putString("id", hook.getId());
+ args.putString("definition", hook.toJSON());
+ getContext().getContentResolver()
+ .call(XProvider.getURI(), "xlua", "putHook", args);
+ }
+ }
+
+ String show = XProvider.getSetting(getContext(), "global", "show");
+ if (show != null && show.equals("user"))
+ data.show = AdapterApp.enumShow.user;
+ else if (show != null && show.equals("all"))
+ data.show = AdapterApp.enumShow.all;
+ else
+ data.show = AdapterApp.enumShow.icon;
+
+ // Get collection
+ String collection = XProvider.getSetting(getContext(), "global", "collection");
+ if (collection == null)
+ data.collection.add("Privacy");
+ else
+ Collections.addAll(data.collection, collection.split(","));
+
+ // Load groups
+ Resources res = getContext().getResources();
+ Bundle result = getContext().getContentResolver()
+ .call(XProvider.getURI(), "xlua", "getGroups", new Bundle());
+ if (result != null)
+ for (String name : result.getStringArray("groups")) {
+ String g = name.toLowerCase().replaceAll("[^a-z]", "_");
+ int id = res.getIdentifier("group_" + g, "string", getContext().getPackageName());
+
+ XGroup group = new XGroup();
+ group.name = name;
+ group.title = (id > 0 ? res.getString(id) : name);
+ data.groups.add(group);
+ }
+
+ final Collator collator = Collator.getInstance(Locale.getDefault());
+ collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc
+ Collections.sort(data.groups, new Comparator() {
+ @Override
+ public int compare(XGroup group1, XGroup group2) {
+ return collator.compare(group1.title, group2.title);
+ }
+ });
+
+ XGroup all = new XGroup();
+ all.name = null;
+ all.title = getContext().getString(R.string.title_all);
+ data.groups.add(0, all);
+
+ // Load hooks
+ Cursor chooks = null;
+ try {
+ chooks = getContext().getContentResolver()
+ .query(XProvider.getURI(), new String[]{"xlua.getHooks2"}, null, null, null);
+ while (chooks != null && chooks.moveToNext()) {
+ byte[] marshaled = chooks.getBlob(0);
+ Parcel parcel = Parcel.obtain();
+ parcel.unmarshall(marshaled, 0, marshaled.length);
+ parcel.setDataPosition(0);
+ XHook hook = XHook.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ data.hooks.add(hook);
+ }
+ } finally {
+ if (chooks != null)
+ chooks.close();
+ }
+
+ // Load apps
+ Cursor capps = null;
+ try {
+ capps = getContext().getContentResolver()
+ .query(XProvider.getURI(), new String[]{"xlua.getApps2"}, null, null, null);
+ while (capps != null && capps.moveToNext()) {
+ byte[] marshaled = capps.getBlob(0);
+ Parcel parcel = Parcel.obtain();
+ parcel.unmarshall(marshaled, 0, marshaled.length);
+ parcel.setDataPosition(0);
+ XApp app = XApp.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ data.apps.add(app);
+ }
+ } finally {
+ if (capps != null)
+ capps.close();
+ }
+ } catch (Throwable ex) {
+ data.collection = null;
+ data.groups.clear();
+ data.hooks.clear();
+ data.apps.clear();
+ data.exception = ex;
+ }
+
+ Log.i(TAG, "Data loader finished groups=" + data.groups.size() +
+ " hooks=" + data.hooks.size() + " apps=" + data.apps.size());
+ return data;
+ }
+ }
+
private BroadcastReceiver packageChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Received " + intent);
- String pkg = intent.getData().getSchemeSpecificPart();
- int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
- Log.i(TAG, "pkg=" + pkg + ":" + uid);
- updateData();
+ String packageName = intent.getData().getSchemeSpecificPart();
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ Log.i(TAG, "pkg=" + packageName + ":" + uid);
+ loadData();
}
};
+
+ private static class DataHolder {
+ AdapterApp.enumShow show;
+ String theme;
+ List collection = new ArrayList<>();
+ List groups = new ArrayList<>();
+ List hooks = new ArrayList<>();
+ List apps = new ArrayList<>();
+ Throwable exception = null;
+ }
}
diff --git a/app/src/main/java/eu/faircode/xlua/ReceiverPackage.java b/app/src/main/java/eu/faircode/xlua/ReceiverPackage.java
new file mode 100644
index 00000000..674b9637
--- /dev/null
+++ b/app/src/main/java/eu/faircode/xlua/ReceiverPackage.java
@@ -0,0 +1,110 @@
+/*
+ This file is part of XPrivacyLua.
+
+ XPrivacyLua is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ XPrivacyLua is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with XPrivacyLua. If not, see .
+
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
+ */
+
+package eu.faircode.xlua;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+
+import de.robv.android.xposed.XposedBridge;
+
+public class ReceiverPackage extends BroadcastReceiver {
+ private static final String TAG = "XLua.Receiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ String packageName = intent.getData().getSchemeSpecificPart();
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ Log.i(TAG, "Received " + intent + " uid=" + uid);
+
+ int userid = Util.getUserId(uid);
+ Context ctx = Util.createContextForUser(context, userid);
+
+ if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
+ if (!replacing && !packageName.startsWith(BuildConfig.APPLICATION_ID)) {
+ // Initialize app
+ Bundle args = new Bundle();
+ args.putString("packageName", packageName);
+ args.putInt("uid", uid);
+ context.getContentResolver()
+ .call(XProvider.getURI(), "xlua", "clearApp", args);
+ if (XProvider.getSettingBoolean(context, userid, "global", "restrict_new_apps"))
+ context.getContentResolver()
+ .call(XProvider.getURI(), "xlua", "initApp", args);
+
+ // Notify new app
+ if (XProvider.getSettingBoolean(context, userid, "global", "notify_new_apps")) {
+ PackageManager pm = ctx.getPackageManager();
+ Resources resources = pm.getResourcesForApplication(BuildConfig.APPLICATION_ID);
+
+ Notification.Builder builder = new Notification.Builder(ctx);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ builder.setChannelId(XProvider.cChannelName);
+ builder.setSmallIcon(android.R.drawable.ic_dialog_alert);
+ builder.setContentTitle(resources.getString(R.string.msg_review_settings));
+ builder.setContentText(pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0)));
+
+ builder.setPriority(Notification.PRIORITY_HIGH);
+ builder.setCategory(Notification.CATEGORY_STATUS);
+ builder.setVisibility(Notification.VISIBILITY_SECRET);
+
+ // Main
+ Intent main = ctx.getPackageManager().getLaunchIntentForPackage(BuildConfig.APPLICATION_ID);
+ main.putExtra(ActivityMain.EXTRA_SEARCH_PACKAGE, packageName);
+ PendingIntent pi = PendingIntent.getActivity(ctx, uid, main, 0);
+ builder.setContentIntent(pi);
+
+ builder.setAutoCancel(true);
+
+ Util.notifyAsUser(ctx, "xlua_new_app", uid, builder.build(), userid);
+ }
+ }
+ } else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(intent.getAction())) {
+ if (BuildConfig.APPLICATION_ID.equals(packageName)) {
+ Bundle args = new Bundle();
+ args.putInt("user", userid);
+ context.getContentResolver()
+ .call(XProvider.getURI(), "xlua", "clearData", args);
+ } else {
+ Bundle args = new Bundle();
+ args.putString("packageName", packageName);
+ args.putInt("uid", uid);
+ args.putBoolean("settings", true);
+ context.getContentResolver()
+ .call(XProvider.getURI(), "xlua", "clearApp", args);
+
+ Util.cancelAsUser(ctx, "xlua_new_app", uid, userid);
+ }
+ }
+ } catch (Throwable ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ XposedBridge.log(ex);
+ }
+ }
+}
diff --git a/app/src/main/java/eu/faircode/xlua/Util.java b/app/src/main/java/eu/faircode/xlua/Util.java
index 1337f8bc..01565a3c 100644
--- a/app/src/main/java/eu/faircode/xlua/Util.java
+++ b/app/src/main/java/eu/faircode/xlua/Util.java
@@ -1,53 +1,59 @@
/*
- This file is part of XPrivacy/Lua.
+ This file is part of XPrivacyLua.
- XPrivacy/Lua is free software: you can redistribute it and/or modify
+ XPrivacyLua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- XPrivacy/Lua is distributed in the hope that it will be useful,
+ XPrivacyLua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
+ along with XPrivacyLua. If not, see .
- Copyright 2017-2018 Marcel Bokhorst (M66B)
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
*/
package eu.faircode.xlua;
+import android.annotation.SuppressLint;
import android.app.Dialog;
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleObserver;
-import android.arch.lifecycle.LifecycleOwner;
-import android.arch.lifecycle.OnLifecycleEvent;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Log;
+import android.util.TypedValue;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.OnLifecycleEvent;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import java.security.MessageDigest;
class Util {
private final static String TAG = "XLua.Util";
+ static final String PRO_PACKAGE_NAME = "eu.faircode.xlua.pro";
private static final int PER_USER_RANGE = 100000;
- static String getSelfVersionName(Context context) {
- try {
- PackageInfo pi = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
- return pi.versionName;
- } catch (PackageManager.NameNotFoundException ex) {
- return ex.toString();
- }
- }
-
static void setPermissions(String path, int mode, int uid, int gid) {
try {
Class> fileUtils = Class.forName("android.os.FileUtils");
@@ -103,6 +109,138 @@ static UserHandle getUserHandle(int userid) {
}
}
+ static byte[] getSha1Fingerprint(Context context, String packageName) throws Throwable {
+ PackageManager pm = context.getPackageManager();
+ PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ MessageDigest digest = MessageDigest.getInstance("SHA1");
+ return digest.digest(packageInfo.signatures[0].toByteArray());
+ }
+
+ static Context createContextForUser(Context context, int userid) throws Throwable {
+ if (isVirtualXposed())
+ return context;
+
+ // public UserHandle(int h)
+ Class> clsUH = Class.forName("android.os.UserHandle");
+ Constructor> cUH = clsUH.getDeclaredConstructor(int.class);
+ UserHandle uh = (UserHandle) cUH.newInstance(userid);
+
+ // public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
+ Method c = context.getClass().getDeclaredMethod("createPackageContextAsUser", String.class, int.class, UserHandle.class);
+ return (Context) c.invoke(context, "android", 0, uh);
+ }
+
+ static void notifyAsUser(Context context, String tag, int id, Notification notification, int userid) throws Throwable {
+ NotificationManager nm = context.getSystemService(NotificationManager.class);
+
+ // Create notification channel
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ PackageManager pm = context.getPackageManager();
+ Resources resources = pm.getResourcesForApplication(BuildConfig.APPLICATION_ID);
+ NotificationChannel channel = new NotificationChannel(
+ XProvider.cChannelName, resources.getString(R.string.channel_privacy), NotificationManager.IMPORTANCE_HIGH);
+ channel.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ nm.createNotificationChannel(channel);
+ }
+
+ if (Util.isVirtualXposed()) {
+ nm.notify(tag, id, notification);
+ return;
+ }
+
+ // public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
+ Method mNotifyAsUser = nm.getClass().getDeclaredMethod(
+ "notifyAsUser", String.class, int.class, Notification.class, UserHandle.class);
+ mNotifyAsUser.invoke(nm, tag, id, notification, Util.getUserHandle(userid));
+ Log.i(TAG, "Notified " + tag + ":" + id + " as " + userid);
+ }
+
+ static void cancelAsUser(Context context, String tag, int id, int userid) throws Throwable {
+ NotificationManager nm = context.getSystemService(NotificationManager.class);
+
+ if (Util.isVirtualXposed()) {
+ nm.cancel(tag, id);
+ return;
+ }
+
+ // public void cancelAsUser(String tag, int id, UserHandle user)
+ Method mCancelAsUser = nm.getClass().getDeclaredMethod(
+ "cancelAsUser", String.class, int.class, UserHandle.class);
+ mCancelAsUser.invoke(nm, tag, id, Util.getUserHandle(userid));
+ Log.i(TAG, "Cancelled " + tag + ":" + id + " as " + userid);
+ }
+
+ private static Boolean isExp;
+ private static boolean isExpModuleActive() {
+ if (isExp != null) {
+ return isExp;
+ }
+ try {
+ @SuppressLint("PrivateApi") Context context = (Context) Class.forName("android.app.ActivityThread")
+ .getDeclaredMethod("currentApplication", new Class[0]).invoke(null, new Object[0]);
+ if (context == null) {
+ return isExp = false;
+ }
+ try {
+ Bundle call = context.getContentResolver().call(Uri.parse("content://me.weishu.exposed.CP/"), "active", null, null);
+ if (call == null) {
+ return isExp = false;
+ }
+ isExp = call.getBoolean("active", false);
+ return isExp;
+ } catch (Throwable th) {
+ return isExp = false;
+ }
+ } catch (Throwable th2) {
+ return isExp = false;
+ }
+ }
+
+ static boolean isVirtualXposed() {
+ return !TextUtils.isEmpty(System.getProperty("vxp"))
+ || !TextUtils.isEmpty(System.getProperty("exp"))
+ || isExpModuleActive();
+ }
+
+ public static int resolveColor(Context context, int attr) {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ theme.resolveAttribute(attr, typedValue, true);
+ return typedValue.data;
+ }
+
+ static void areYouSure(ActivityBase activity, String question, final DoubtListener listener) {
+ final DialogObserver observer = new DialogObserver();
+ AlertDialog ad = new AlertDialog.Builder(activity)
+ .setMessage(question)
+ .setCancelable(true)
+ .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ listener.onSure();
+ }
+ })
+ .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // Do nothing
+ }
+ })
+ .setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialogInterface) {
+ observer.stopObserving();
+ }
+ })
+ .create();
+ ad.show();
+ observer.startObserving(activity, ad);
+ }
+
+ public interface DoubtListener {
+ void onSure();
+ }
+
static class DialogObserver implements LifecycleObserver {
private LifecycleOwner owner = null;
private Dialog dialog = null;
diff --git a/app/src/main/java/eu/faircode/xlua/VXP.java b/app/src/main/java/eu/faircode/xlua/VXP.java
new file mode 100644
index 00000000..467316bb
--- /dev/null
+++ b/app/src/main/java/eu/faircode/xlua/VXP.java
@@ -0,0 +1,93 @@
+/*
+ This file is part of XPrivacyLua.
+
+ XPrivacyLua is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ XPrivacyLua is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with XPrivacyLua. If not, see .
+
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
+ */
+
+package eu.faircode.xlua;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import de.robv.android.xposed.XposedBridge;
+
+public class VXP extends ContentProvider {
+ private static final String TAG = "XLua.VXP";
+
+ @Nullable
+ @Override
+ public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+ try {
+ Log.i(TAG, "Call " + arg +
+ " uid=" + android.os.Process.myUid() +
+ " cuid=" + android.os.Binder.getCallingUid());
+ return XProvider.call(getContext(), arg, extras);
+ } catch (RemoteException ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ XposedBridge.log(ex);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
+ try {
+ Log.i(TAG, "Query " + projection[0].split("\\.")[1] +
+ " uid=" + android.os.Process.myUid() +
+ " cuid=" + android.os.Binder.getCallingUid());
+ return XProvider.query(getContext(), projection[0].split("\\.")[1], selectionArgs);
+ } catch (RemoteException ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ XposedBridge.log(ex);
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ public String getType(@NonNull Uri uri) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/app/src/main/java/eu/faircode/xlua/XApp.java b/app/src/main/java/eu/faircode/xlua/XApp.java
index 29ddd29c..6996ec5e 100644
--- a/app/src/main/java/eu/faircode/xlua/XApp.java
+++ b/app/src/main/java/eu/faircode/xlua/XApp.java
@@ -1,98 +1,107 @@
/*
- This file is part of XPrivacy/Lua.
+ This file is part of XPrivacyLua.
- XPrivacy/Lua is free software: you can redistribute it and/or modify
+ XPrivacyLua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- XPrivacy/Lua is distributed in the hope that it will be useful,
+ XPrivacyLua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
+ along with XPrivacyLua. If not, see .
- Copyright 2017-2018 Marcel Bokhorst (M66B)
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
*/
package eu.faircode.xlua;
+import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.util.ArrayList;
import java.util.List;
-public class XApp implements Parcelable {
+class XApp implements Parcelable {
String packageName;
int uid;
int icon;
String label;
boolean enabled;
boolean persistent;
+ boolean system;
+ boolean forceStop = true;
List assignments;
- public XApp() {
+ XApp() {
}
- static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
- public XApp createFromParcel(Parcel in) {
- return new XApp(in);
- }
+ List getAssignments(String group) {
+ if (group == null)
+ return assignments;
- public XApp[] newArray(int size) {
- return new XApp[size];
- }
- };
+ List filtered = new ArrayList<>();
+ for (XAssignment assignment : assignments)
+ if (group.equals(assignment.hook.getGroup()))
+ filtered.add(assignment);
- private XApp(Parcel in) {
- readFromParcel(in);
+ return filtered;
}
- @Override
- public void writeToParcel(Parcel out, int flags) {
- writeString(out, this.packageName);
- out.writeInt(this.uid);
- out.writeInt(this.icon);
- writeString(out, this.label);
- out.writeInt(this.enabled ? 1 : 0);
- out.writeInt(this.persistent ? 1 : 0);
-
- int hookc = (this.assignments == null ? -1 : this.assignments.size());
- out.writeInt(hookc);
- for (int i = 0; i < hookc; i++)
- out.writeParcelable(this.assignments.get(i), 0);
+ String toJSON() throws JSONException {
+ return toJSONObject().toString(2);
}
- private void writeString(Parcel out, String value) {
- out.writeInt(value == null ? 1 : 0);
- if (value != null)
- out.writeString(value);
- }
+ JSONObject toJSONObject() throws JSONException {
+ JSONObject jroot = new JSONObject();
- private void readFromParcel(Parcel in) {
- this.packageName = readString(in);
- this.uid = in.readInt();
- this.icon = in.readInt();
- this.label = readString(in);
- this.enabled = (in.readInt() != 0);
- this.persistent = (in.readInt() != 0);
-
- int hookc = in.readInt();
- this.assignments = (hookc < 0 ? null : new ArrayList());
- for (int i = 0; i < hookc; i++)
- this.assignments.add((XAssignment) in.readParcelable(XAssignment.class.getClassLoader()));
+ jroot.put("packageName", this.packageName);
+ jroot.put("uid", this.uid);
+ jroot.put("icon", this.icon);
+ jroot.put("label", this.label);
+ jroot.put("enabled", this.enabled);
+ jroot.put("persistent", this.persistent);
+ jroot.put("system", this.system);
+ jroot.put("forcestop", this.forceStop);
+
+ JSONArray jassignments = new JSONArray();
+ for (XAssignment assignment : this.assignments)
+ jassignments.put(assignment.toJSONObject());
+ jroot.put("assignments", jassignments);
+
+ return jroot;
}
- private String readString(Parcel in) {
- return (in.readInt() > 0 ? null : in.readString());
+ static XApp fromJSON(String json) throws JSONException {
+ return fromJSONObject(new JSONObject(json));
}
- @Override
- public int describeContents() {
- return 0;
+ static XApp fromJSONObject(JSONObject jroot) throws JSONException {
+ XApp app = new XApp();
+
+ app.packageName = jroot.getString("packageName");
+ app.uid = jroot.getInt("uid");
+ app.icon = jroot.getInt("icon");
+ app.label = (jroot.has("label") ? jroot.getString("label") : null);
+ app.enabled = jroot.getBoolean("enabled");
+ app.persistent = jroot.getBoolean("persistent");
+ app.system = jroot.getBoolean("system");
+ app.forceStop = jroot.getBoolean("forcestop");
+
+ app.assignments = new ArrayList<>();
+ JSONArray jassignment = jroot.getJSONArray("assignments");
+ for (int i = 0; i < jassignment.length(); i++)
+ app.assignments.add(XAssignment.fromJSONObject((JSONObject) jassignment.get(i)));
+
+ return app;
}
private IListener listener = null;
@@ -101,12 +110,67 @@ void setListener(IListener listener) {
this.listener = listener;
}
- void notifyChanged() {
+ void notifyAssign(Context context, String groupName, boolean assign) {
if (this.listener != null)
- this.listener.onChange();
+ this.listener.onAssign(context, groupName, assign);
}
public interface IListener {
- void onChange();
+ void onAssign(Context context, String groupName, boolean assign);
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof XApp))
+ return false;
+ XApp other = (XApp) obj;
+ return (this.packageName.equals(other.packageName) && this.uid == other.uid);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.packageName.hashCode();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(this.packageName);
+ dest.writeInt(this.uid);
+ dest.writeInt(this.icon);
+ dest.writeString(this.label);
+ dest.writeByte(this.enabled ? (byte) 1 : (byte) 0);
+ dest.writeByte(this.persistent ? (byte) 1 : (byte) 0);
+ dest.writeByte(this.system ? (byte) 1 : (byte) 0);
+ dest.writeByte(this.forceStop ? (byte) 1 : (byte) 0);
+ dest.writeTypedList(this.assignments);
+ }
+
+ protected XApp(Parcel in) {
+ this.packageName = in.readString();
+ this.uid = in.readInt();
+ this.icon = in.readInt();
+ this.label = in.readString();
+ this.enabled = (in.readByte() != 0);
+ this.persistent = (in.readByte() != 0);
+ this.system = (in.readByte() != 0);
+ this.forceStop = (in.readByte() != 0);
+ this.assignments = in.createTypedArrayList(XAssignment.CREATOR);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ @Override
+ public XApp createFromParcel(Parcel source) {
+ return new XApp(source);
+ }
+
+ @Override
+ public XApp[] newArray(int size) {
+ return new XApp[size];
+ }
+ };
}
diff --git a/app/src/main/java/eu/faircode/xlua/XAssignment.java b/app/src/main/java/eu/faircode/xlua/XAssignment.java
index 9a40bb81..a6716a3f 100644
--- a/app/src/main/java/eu/faircode/xlua/XAssignment.java
+++ b/app/src/main/java/eu/faircode/xlua/XAssignment.java
@@ -1,63 +1,74 @@
+/*
+ This file is part of XPrivacyLua.
+
+ XPrivacyLua is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ XPrivacyLua is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with XPrivacyLua. If not, see .
+
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
+ */
+
package eu.faircode.xlua;
import android.os.Parcel;
import android.os.Parcelable;
-public class XAssignment implements Parcelable {
+import org.json.JSONException;
+import org.json.JSONObject;
+
+class XAssignment implements Parcelable {
XHook hook;
long installed = -1;
long used = -1;
boolean restricted = false;
String exception;
+ private XAssignment() {
+ }
+
XAssignment(XHook hook) {
this.hook = hook;
}
- public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
- public XAssignment createFromParcel(Parcel in) {
- return new XAssignment(in);
- }
+ String toJSON() throws JSONException {
+ return toJSONObject().toString(2);
+ }
- public XAssignment[] newArray(int size) {
- return new XAssignment[size];
- }
- };
+ JSONObject toJSONObject() throws JSONException {
+ JSONObject jroot = new JSONObject();
- private XAssignment(Parcel in) {
- readFromParcel(in);
- }
+ jroot.put("hook", this.hook.toJSONObject());
+ jroot.put("installed", this.installed);
+ jroot.put("used", this.used);
+ jroot.put("restricted", this.restricted);
+ jroot.put("exception", this.exception);
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(this.hook, 0);
- out.writeLong(this.installed);
- out.writeLong(this.used);
- out.writeInt(this.restricted ? 1 : 0);
- writeString(out, this.exception);
+ return jroot;
}
- private void writeString(Parcel out, String value) {
- out.writeInt(value == null ? 1 : 0);
- if (value != null)
- out.writeString(value);
+ static XAssignment fromJSON(String json) throws JSONException {
+ return fromJSONObject(new JSONObject(json));
}
- private void readFromParcel(Parcel in) {
- this.hook = in.readParcelable(XHook.class.getClassLoader());
- this.installed = in.readLong();
- this.used = in.readLong();
- this.restricted = (in.readInt() == 1);
- this.exception = readString(in);
- }
+ static XAssignment fromJSONObject(JSONObject jroot) throws JSONException {
+ XAssignment assignment = new XAssignment();
- private String readString(Parcel in) {
- return (in.readInt() > 0 ? null : in.readString());
- }
+ assignment.hook = XHook.fromJSONObject(jroot.getJSONObject("hook"));
+ assignment.installed = jroot.getLong("installed");
+ assignment.used = jroot.getLong("used");
+ assignment.restricted = jroot.getBoolean("restricted");
+ assignment.exception = (jroot.has("exception") ? jroot.getString("exception") : null);
- @Override
- public int describeContents() {
- return 0;
+ return assignment;
}
@Override
@@ -72,4 +83,38 @@ public boolean equals(Object obj) {
public int hashCode() {
return this.hook.getId().hashCode();
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(this.hook, flags);
+ dest.writeLong(this.installed);
+ dest.writeLong(this.used);
+ dest.writeByte(this.restricted ? (byte) 1 : (byte) 0);
+ dest.writeString(this.exception);
+ }
+
+ protected XAssignment(Parcel in) {
+ this.hook = in.readParcelable(XHook.class.getClassLoader());
+ this.installed = in.readLong();
+ this.used = in.readLong();
+ this.restricted = (in.readByte() != 0);
+ this.exception = in.readString();
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ @Override
+ public XAssignment createFromParcel(Parcel source) {
+ return new XAssignment(source);
+ }
+
+ @Override
+ public XAssignment[] newArray(int size) {
+ return new XAssignment[size];
+ }
+ };
}
diff --git a/app/src/main/java/eu/faircode/xlua/XGroup.java b/app/src/main/java/eu/faircode/xlua/XGroup.java
new file mode 100644
index 00000000..128e90d5
--- /dev/null
+++ b/app/src/main/java/eu/faircode/xlua/XGroup.java
@@ -0,0 +1,38 @@
+/*
+ This file is part of XPrivacyLua.
+
+ XPrivacyLua is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ XPrivacyLua is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with XPrivacyLua. If not, see .
+
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
+ */
+
+package eu.faircode.xlua;
+
+class XGroup {
+ String name;
+ String title;
+
+ @Override
+ public String toString() {
+ return title;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof XGroup))
+ return false;
+ XGroup other = (XGroup) obj;
+ return this.name.equals(other.name);
+ }
+}
diff --git a/app/src/main/java/eu/faircode/xlua/XHook.java b/app/src/main/java/eu/faircode/xlua/XHook.java
index e3939b8e..ff185891 100644
--- a/app/src/main/java/eu/faircode/xlua/XHook.java
+++ b/app/src/main/java/eu/faircode/xlua/XHook.java
@@ -1,55 +1,93 @@
/*
- This file is part of XPrivacy/Lua.
+ This file is part of XPrivacyLua.
- XPrivacy/Lua is free software: you can redistribute it and/or modify
+ XPrivacyLua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- XPrivacy/Lua is distributed in the hope that it will be useful,
+ XPrivacyLua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
+ along with XPrivacyLua. If not, see .
- Copyright 2017-2018 Marcel Bokhorst (M66B)
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
*/
package eu.faircode.xlua;
+import android.app.ActivityManager;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.hardware.camera2.CameraManager;
+import android.media.AudioManager;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.SmsManager;
+import android.text.TextUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
public class XHook implements Parcelable {
+ private final static String TAG = "XLua.XHook";
+
+ private boolean builtin = false;
private String collection;
private String group;
private String name;
private String author;
+ private int version = 0;
+ private String description;
private String className;
+ private String resolvedClassName = null;
private String methodName;
private String[] parameterTypes;
private String returnType;
private int minSdk;
private int maxSdk;
+ private int minApk;
+ private int maxApk;
+ private String[] excludePackages;
private boolean enabled;
+ private boolean optional;
+ private boolean usage;
+ private boolean notify;
private String luaScript;
- public XHook() {
+ private String[] settings;
+
+ final static int FLAG_WITH_LUA = 2; // =PARCELABLE_ELIDE_DUPLICATES
+
+ private XHook() {
}
public String getId() {
return this.collection + "." + this.name;
}
+ public boolean isBuiltin() {
+ return this.builtin;
+ }
+
@SuppressWarnings("unused")
public String getCollection() {
return this.collection;
@@ -68,10 +106,20 @@ public String getAuthor() {
return this.author;
}
+ @SuppressWarnings("unused")
+ public String getDescription() {
+ return this.description;
+ }
+
+ @SuppressWarnings("unused")
public String getClassName() {
return this.className;
}
+ public String getResolvedClassName() {
+ return (this.resolvedClassName == null ? this.className : this.resolvedClassName);
+ }
+
public String getMethodName() {
return this.methodName;
}
@@ -84,164 +132,285 @@ public String getReturnType() {
return this.returnType;
}
- public int getMinSdk() {
- return this.minSdk;
- }
-
- public int getMaxSdk() {
- return this.maxSdk;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
+ public boolean isAvailable(String packageName, List collection) {
+ if (!collection.contains(this.collection))
+ return false;
- public String getLuaScript() {
- return this.luaScript;
- }
+ if (!this.enabled)
+ return false;
- public void setLuaScript(String script) {
- this.luaScript = script;
- }
+ if (Build.VERSION.SDK_INT < this.minSdk || Build.VERSION.SDK_INT > this.maxSdk)
+ return false;
- public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
- public XHook createFromParcel(Parcel in) {
- return new XHook(in);
- }
+ if (packageName == null)
+ return true;
- public XHook[] newArray(int size) {
- return new XHook[size];
- }
- };
+ if (this.excludePackages == null)
+ return true;
- private XHook(Parcel in) {
- readFromParcel(in);
+ boolean included = true;
+ for (String excluded : this.excludePackages)
+ if (Pattern.matches(excluded, packageName)) {
+ included = false;
+ break;
+ }
+ return included;
}
- @Override
- public void writeToParcel(Parcel out, int flags) {
- writeString(out, this.collection);
- writeString(out, this.group);
- writeString(out, this.name);
- writeString(out, this.author);
-
- writeString(out, this.className);
- writeString(out, this.methodName);
-
- int argc = (this.parameterTypes == null ? -1 : this.parameterTypes.length);
- out.writeInt(argc);
- for (int i = 0; i < argc; i++)
- out.writeString(this.parameterTypes[i]);
-
- writeString(out, this.returnType);
-
- out.writeInt(this.minSdk);
- out.writeInt(this.maxSdk);
- out.writeInt(this.enabled ? 1 : 0);
-
- writeString(out, this.luaScript);
+ public boolean isAvailable(int versionCode) {
+ return (versionCode >= this.minApk && versionCode <= maxApk);
}
- private void writeString(Parcel out, String value) {
- out.writeInt(value == null ? 1 : 0);
- if (value != null)
- out.writeString(value);
+ public boolean isOptional() {
+ return this.optional;
}
- private void readFromParcel(Parcel in) {
- this.collection = readString(in);
- this.group = readString(in);
- this.name = readString(in);
- this.author = readString(in);
-
- this.className = readString(in);
- this.methodName = readString(in);
-
- int argc = in.readInt();
- this.parameterTypes = (argc < 0 ? null : new String[argc]);
- for (int i = 0; i < argc; i++)
- this.parameterTypes[i] = in.readString();
-
- this.returnType = readString(in);
+ public boolean doUsage() {
+ return this.usage;
+ }
- this.minSdk = in.readInt();
- this.maxSdk = in.readInt();
- this.enabled = (in.readInt() == 1);
+ public boolean doNotify() {
+ return this.notify;
+ }
- this.luaScript = readString(in);
+ public String getLuaScript() {
+ return this.luaScript;
}
- private String readString(Parcel in) {
- return (in.readInt() > 0 ? null : in.readString());
+ public void resolveClassName(Context context) {
+ if ("android.app.ActivityManager".equals(this.className)) {
+ Object service = context.getSystemService(ActivityManager.class);
+ if (service != null)
+ this.resolvedClassName = service.getClass().getName();
+
+ } else if ("android.appwidget.AppWidgetManager".equals(this.className)) {
+ Object service = context.getSystemService(AppWidgetManager.class);
+ if (service != null)
+ this.resolvedClassName = service.getClass().getName();
+
+ } else if ("android.media.AudioManager".equals(this.className)) {
+ Object service = context.getSystemService(AudioManager.class);
+ if (service != null)
+ this.resolvedClassName = service.getClass().getName();
+
+ } else if ("android.hardware.camera2.CameraManager".equals(this.className)) {
+ Object service = context.getSystemService(CameraManager.class);
+ if (service != null)
+ this.resolvedClassName = service.getClass().getName();
+
+ } else if ("android.content.ContentResolver".equals(this.className)) {
+ this.resolvedClassName = context.getContentResolver().getClass().getName();
+
+ } else if ("android.content.pm.PackageManager".equals(this.className)) {
+ this.resolvedClassName = context.getPackageManager().getClass().getName();
+
+ } else if ("android.hardware.SensorManager".equals(this.className)) {
+ Object service = context.getSystemService(SensorManager.class);
+ if (service != null)
+ this.resolvedClassName = service.getClass().getName();
+
+ } else if ("android.telephony.SmsManager".equals(this.className)) {
+ Object service = SmsManager.getDefault();
+ if (service != null)
+ this.resolvedClassName = service.getClass().getName();
+
+ } else if ("android.telephony.TelephonyManager".equals(this.className)) {
+ Object service = context.getSystemService(Context.TELEPHONY_SERVICE);
+ if (service != null)
+ this.resolvedClassName = service.getClass().getName();
+
+ } else if ("android.net.wifi.WifiManager".equals(this.className)) {
+ Object service = context.getSystemService(Context.WIFI_SERVICE);
+ if (service != null)
+ this.resolvedClassName = service.getClass().getName();
+ }
}
- @Override
- public int describeContents() {
- return 0;
+ // Read hook definitions from asset file
+ static ArrayList readHooks(Context context, String apk) throws IOException, JSONException {
+ ZipFile zipFile = null;
+ try {
+ zipFile = new ZipFile(apk);
+ ZipEntry zipEntry = zipFile.getEntry("assets/hooks.json");
+ if (zipEntry == null)
+ throw new IllegalArgumentException("assets/hooks.json not found in " + apk);
+
+ InputStream is = null;
+ try {
+ is = zipFile.getInputStream(zipEntry);
+ String json = new Scanner(is).useDelimiter("\\A").next();
+ ArrayList hooks = new ArrayList<>();
+ JSONArray jarray = new JSONArray(json);
+ for (int i = 0; i < jarray.length(); i++) {
+ XHook hook = XHook.fromJSONObject(jarray.getJSONObject(i));
+ hook.builtin = true;
+
+ // Link script
+ if (hook.luaScript.startsWith("@")) {
+ ZipEntry luaEntry = zipFile.getEntry("assets/" + hook.luaScript.substring(1) + ".lua");
+ if (luaEntry == null)
+ throw new IllegalArgumentException(hook.luaScript + " not found for " + hook.getId());
+ else {
+ InputStream lis = null;
+ try {
+ lis = zipFile.getInputStream(luaEntry);
+ hook.luaScript = new Scanner(lis).useDelimiter("\\A").next();
+ } finally {
+ if (lis != null)
+ try {
+ lis.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ }
+
+ hooks.add(hook);
+ }
+
+ return hooks;
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch (IOException ignored) {
+ }
+ }
+ } finally {
+ if (zipFile != null)
+ try {
+ zipFile.close();
+ } catch (IOException ignored) {
+ }
+ }
}
- public String toJSON() throws JSONException {
+ String toJSON() throws JSONException {
return toJSONObject().toString(2);
}
- public JSONObject toJSONObject() throws JSONException {
+ JSONObject toJSONObject() throws JSONException {
JSONObject jroot = new JSONObject();
+ jroot.put("builtin", this.builtin);
jroot.put("collection", this.collection);
jroot.put("group", this.group);
jroot.put("name", this.name);
jroot.put("author", this.author);
+ jroot.put("version", this.version);
+ if (this.description != null)
+ jroot.put("description", this.description);
jroot.put("className", this.className);
- jroot.put("methodName", this.methodName);
+ if (this.resolvedClassName != null)
+ jroot.put("resolvedClassName", this.resolvedClassName);
+ if (this.methodName != null)
+ jroot.put("methodName", this.methodName);
JSONArray jparam = new JSONArray();
for (int i = 0; i < this.parameterTypes.length; i++)
jparam.put(this.parameterTypes[i]);
jroot.put("parameterTypes", jparam);
- jroot.put("returnType", this.returnType);
+ if (this.returnType != null)
+ jroot.put("returnType", this.returnType);
jroot.put("minSdk", this.minSdk);
jroot.put("maxSdk", this.maxSdk);
+
+ jroot.put("minApk", this.minApk);
+ jroot.put("maxApk", this.maxApk);
+
+ if (this.excludePackages != null)
+ jroot.put("excludePackages", TextUtils.join(",", this.excludePackages));
+
jroot.put("enabled", this.enabled);
+ jroot.put("optional", this.optional);
+ jroot.put("usage", this.usage);
+ jroot.put("notify", this.notify);
jroot.put("luaScript", this.luaScript);
+ if (this.settings != null) {
+ JSONArray jsettings = new JSONArray();
+ for (int i = 0; i < this.settings.length; i++)
+ jsettings.put(this.settings[i]);
+ jroot.put("settings", jsettings);
+ }
+
return jroot;
}
- public static XHook fromJSON(String json) throws JSONException {
+ static XHook fromJSON(String json) throws JSONException {
return fromJSONObject(new JSONObject(json));
}
- public static XHook fromJSONObject(JSONObject jroot) throws JSONException {
+ static XHook fromJSONObject(JSONObject jroot) throws JSONException {
XHook hook = new XHook();
+ hook.builtin = (jroot.has("builtin") ? jroot.getBoolean("builtin") : false);
hook.collection = jroot.getString("collection");
hook.group = jroot.getString("group");
hook.name = jroot.getString("name");
hook.author = jroot.getString("author");
+ hook.version = (jroot.has("version") ? jroot.getInt("version") : 0);
+ hook.description = (jroot.has("description") ? jroot.getString("description") : null);
hook.className = jroot.getString("className");
- hook.methodName = jroot.getString("methodName");
+ hook.resolvedClassName = (jroot.has("resolvedClassName") ? jroot.getString("resolvedClassName") : null);
+ hook.methodName = (jroot.has("methodName") ? jroot.getString("methodName") : null);
JSONArray jparam = jroot.getJSONArray("parameterTypes");
hook.parameterTypes = new String[jparam.length()];
for (int i = 0; i < jparam.length(); i++)
hook.parameterTypes[i] = jparam.getString(i);
- hook.returnType = jroot.getString("returnType");
+ hook.returnType = (jroot.has("returnType") ? jroot.getString("returnType") : null);
hook.minSdk = jroot.getInt("minSdk");
- hook.maxSdk = jroot.getInt("maxSdk");
- hook.enabled = jroot.getBoolean("enabled");
+ hook.maxSdk = (jroot.has("maxSdk") ? jroot.getInt("maxSdk") : 999);
+
+ hook.minApk = (jroot.has("minApk") ? jroot.getInt("minApk") : 0);
+ hook.maxApk = (jroot.has("maxApk") ? jroot.getInt("maxApk") : Integer.MAX_VALUE);
+
+ hook.excludePackages = (jroot.has("excludePackages")
+ ? jroot.getString("excludePackages").split(",") : null);
+
+ hook.enabled = (jroot.has("enabled") ? jroot.getBoolean("enabled") : true);
+ hook.optional = (jroot.has("optional") ? jroot.getBoolean("optional") : false);
+ hook.usage = (jroot.has("usage") ? jroot.getBoolean("usage") : true);
+ hook.notify = (jroot.has("notify") ? jroot.getBoolean("notify") : false);
hook.luaScript = jroot.getString("luaScript");
+ if (jroot.has("settings")) {
+ JSONArray jsettings = jroot.getJSONArray("settings");
+ hook.settings = new String[jsettings.length()];
+ for (int i = 0; i < jsettings.length(); i++)
+ hook.settings[i] = jsettings.getString(i);
+ } else
+ hook.settings = null;
+
return hook;
}
+ public void validate() {
+ if (TextUtils.isEmpty(this.collection))
+ throw new IllegalArgumentException("collection missing");
+ if (TextUtils.isEmpty(this.group))
+ throw new IllegalArgumentException("group missing");
+ if (TextUtils.isEmpty(this.name))
+ throw new IllegalArgumentException("name missing");
+ if (TextUtils.isEmpty(this.author))
+ throw new IllegalArgumentException("author missing");
+ if (TextUtils.isEmpty(this.className))
+ throw new IllegalArgumentException("class name missing");
+ if (parameterTypes == null)
+ throw new IllegalArgumentException("parameter types missing");
+ if (TextUtils.isEmpty(this.luaScript))
+ throw new IllegalArgumentException("Lua script missing");
+ }
+
@Override
public String toString() {
return this.getId() + "@" + this.className + ":" + this.methodName;
@@ -259,4 +428,77 @@ public boolean equals(Object obj) {
public int hashCode() {
return this.getId().hashCode();
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte(this.builtin ? (byte) 1 : (byte) 0);
+ dest.writeString(this.collection);
+ dest.writeString(this.group);
+ dest.writeString(this.name);
+ dest.writeString(this.author);
+ dest.writeInt(this.version);
+ dest.writeString(this.description);
+ dest.writeString(this.className);
+ dest.writeString(this.resolvedClassName);
+ dest.writeString(this.methodName);
+ dest.writeStringArray(this.parameterTypes);
+ dest.writeString(this.returnType);
+ dest.writeInt(this.minSdk);
+ dest.writeInt(this.maxSdk);
+ dest.writeInt(this.minApk);
+ dest.writeInt(this.maxApk);
+ dest.writeStringArray(this.excludePackages);
+ dest.writeByte(this.enabled ? (byte) 1 : (byte) 0);
+ dest.writeByte(this.optional ? (byte) 1 : (byte) 0);
+ dest.writeByte(this.usage ? (byte) 1 : (byte) 0);
+ dest.writeByte(this.notify ? (byte) 1 : (byte) 0);
+ if ((flags & FLAG_WITH_LUA) == 0)
+ dest.writeString(null);
+ else
+ dest.writeString(this.luaScript);
+ dest.writeStringArray(this.settings);
+ }
+
+ protected XHook(Parcel in) {
+ this.builtin = (in.readByte() != 0);
+ this.collection = in.readString();
+ this.group = in.readString();
+ this.name = in.readString();
+ this.author = in.readString();
+ this.version = in.readInt();
+ this.description = in.readString();
+ this.className = in.readString();
+ this.resolvedClassName = in.readString();
+ this.methodName = in.readString();
+ this.parameterTypes = in.createStringArray();
+ this.returnType = in.readString();
+ this.minSdk = in.readInt();
+ this.maxSdk = in.readInt();
+ this.minApk = in.readInt();
+ this.maxApk = in.readInt();
+ this.excludePackages = in.createStringArray();
+ this.enabled = (in.readByte() != 0);
+ this.optional = (in.readByte() != 0);
+ this.usage = (in.readByte() != 0);
+ this.notify = (in.readByte() != 0);
+ this.luaScript = in.readString();
+ this.settings = in.createStringArray();
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ @Override
+ public XHook createFromParcel(Parcel source) {
+ return new XHook(source);
+ }
+
+ @Override
+ public XHook[] newArray(int size) {
+ return new XHook[size];
+ }
+ };
}
diff --git a/app/src/main/java/eu/faircode/xlua/XLua.java b/app/src/main/java/eu/faircode/xlua/XLua.java
new file mode 100644
index 00000000..c1952f40
--- /dev/null
+++ b/app/src/main/java/eu/faircode/xlua/XLua.java
@@ -0,0 +1,910 @@
+/*
+ This file is part of XPrivacyLua.
+
+ XPrivacyLua is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ XPrivacyLua is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with XPrivacyLua. If not, see .
+
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
+ */
+
+package eu.faircode.xlua;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LuaClosure;
+import org.luaj.vm2.LuaError;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Prototype;
+import org.luaj.vm2.Varargs;
+import org.luaj.vm2.compiler.LuaC;
+import org.luaj.vm2.lib.DebugLib;
+import org.luaj.vm2.lib.OneArgFunction;
+import org.luaj.vm2.lib.VarArgFunction;
+import org.luaj.vm2.lib.jse.CoerceJavaToLua;
+import org.luaj.vm2.lib.jse.JsePlatform;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.WeakHashMap;
+
+import de.robv.android.xposed.IXposedHookLoadPackage;
+import de.robv.android.xposed.IXposedHookZygoteInit;
+import de.robv.android.xposed.XC_MethodHook;
+import de.robv.android.xposed.XposedBridge;
+import de.robv.android.xposed.callbacks.XC_LoadPackage;
+
+public class XLua implements IXposedHookZygoteInit, IXposedHookLoadPackage {
+ private static final String TAG = "XLua.Xposed";
+
+ private static int version = -1;
+ private Timer timer = null;
+ private final Map> queue = new HashMap<>();
+
+ public void initZygote(final IXposedHookZygoteInit.StartupParam startupParam) throws Throwable {
+ Log.i(TAG, "initZygote system=" + startupParam.startsSystemServer + " debug=" + BuildConfig.DEBUG);
+ }
+
+ public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
+ int uid = Process.myUid();
+ Log.i(TAG, "Loaded " + lpparam.packageName + ":" + uid);
+
+ if ("android".equals(lpparam.packageName))
+ hookAndroid(lpparam);
+
+ if ("com.android.providers.settings".equals(lpparam.packageName))
+ hookSettings(lpparam);
+
+ if (!"android".equals(lpparam.packageName) &&
+ !lpparam.packageName.startsWith(BuildConfig.APPLICATION_ID))
+ hookApplication(lpparam);
+ }
+
+ private void hookAndroid(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
+ // https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActivityManagerService.java
+ Class> clsAM = Class.forName("com.android.server.am.ActivityManagerService", false, lpparam.classLoader);
+
+ XposedBridge.hookAllMethods(clsAM, "systemReady", new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ try {
+ Log.i(TAG, "Preparing system");
+ Context context = getContext(param.thisObject);
+ hookPackage(lpparam, Process.myUid(), context);
+ } catch (Throwable ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ XposedBridge.log(ex);
+ }
+ }
+
+ @Override
+ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
+ try {
+ Log.i(TAG, "System ready");
+ Context context = getContext(param.thisObject);
+
+ // Store current module version
+ PackageInfo pi = context.getPackageManager().getPackageInfo(BuildConfig.APPLICATION_ID, 0);
+ version = pi.versionCode;
+ Log.i(TAG, "Module version " + version);
+
+ // public static UserManagerService getInstance()
+ Class> clsUM = Class.forName("com.android.server.pm.UserManagerService", false, param.thisObject.getClass().getClassLoader());
+ Object um = clsUM.getDeclaredMethod("getInstance").invoke(null);
+
+ // public int[] getUserIds()
+ int[] userids = (int[]) um.getClass().getDeclaredMethod("getUserIds").invoke(um);
+
+ // Listen for package changes
+ for (int userid : userids) {
+ Log.i(TAG, "Registering package listener user=" + userid);
+ IntentFilter ifPackageAdd = new IntentFilter();
+ ifPackageAdd.addAction(Intent.ACTION_PACKAGE_ADDED);
+ ifPackageAdd.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ ifPackageAdd.addDataScheme("package");
+ Util.createContextForUser(context, userid).registerReceiver(new ReceiverPackage(), ifPackageAdd);
+ }
+ } catch (Throwable ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ XposedBridge.log(ex);
+ }
+ }
+
+ @NonNull
+ private Context getContext(Object am) throws Throwable {
+ // Searching for context
+ Context context = null;
+ Class> cAm = am.getClass();
+ while (cAm != null && context == null) {
+ for (Field field : cAm.getDeclaredFields())
+ if (field.getType().equals(Context.class)) {
+ field.setAccessible(true);
+ context = (Context) field.get(am);
+ Log.i(TAG, "Context found in " + cAm + " as " + field.getName());
+ break;
+ }
+ cAm = cAm.getSuperclass();
+ }
+ if (context == null)
+ throw new Throwable("Context not found");
+
+ return context;
+ }
+ });
+ }
+
+ private void hookSettings(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
+ // https://android.googlesource.com/platform/frameworks/base/+/master/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+ Class> clsSet = Class.forName("com.android.providers.settings.SettingsProvider", false, lpparam.classLoader);
+
+ // Bundle call(String method, String arg, Bundle extras)
+ Method mCall = clsSet.getMethod("call", String.class, String.class, Bundle.class);
+ XposedBridge.hookMethod(mCall, new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ try {
+ String method = (String) param.args[0];
+ String arg = (String) param.args[1];
+ Bundle extras = (Bundle) param.args[2];
+
+ if ("xlua".equals(method))
+ if ("getVersion".equals(arg)) {
+ Bundle result = new Bundle();
+ result.putInt("version", version);
+ param.setResult(result);
+ } else
+ try {
+ Method mGetContext = param.thisObject.getClass().getMethod("getContext");
+ Context context = (Context) mGetContext.invoke(param.thisObject);
+ param.setResult(XProvider.call(context, arg, extras));
+ } catch (IllegalArgumentException ex) {
+ Log.i(TAG, "Error: " + ex.getMessage());
+ param.setThrowable(ex);
+ } catch (Throwable ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ XposedBridge.log(ex);
+ param.setResult(null);
+ }
+ } catch (Throwable ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ XposedBridge.log(ex);
+ }
+ }
+ });
+
+ // Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
+ Method mQuery = clsSet.getMethod("query", Uri.class, String[].class, String.class, String[].class, String.class);
+ XposedBridge.hookMethod(mQuery, new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ try {
+ String[] projection = (String[]) param.args[1];
+ String[] selection = (String[]) param.args[3];
+ if (projection != null && projection.length > 0 &&
+ projection[0] != null && projection[0].startsWith("xlua.")) {
+ try {
+ Method mGetContext = param.thisObject.getClass().getMethod("getContext");
+ Context context = (Context) mGetContext.invoke(param.thisObject);
+ param.setResult(XProvider.query(context, projection[0].split("\\.")[1], selection));
+ } catch (Throwable ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ XposedBridge.log(ex);
+ param.setResult(null);
+ }
+ }
+ } catch (Throwable ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ XposedBridge.log(ex);
+ }
+ }
+ });
+ }
+
+ private void hookApplication(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
+ final int uid = Process.myUid();
+ Class> at = Class.forName("android.app.LoadedApk", false, lpparam.classLoader);
+ XposedBridge.hookAllMethods(at, "makeApplication", new XC_MethodHook() {
+ private boolean made = false;
+
+ @Override
+ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
+ try {
+ if (!made) {
+ made = true;
+ Context context = (Application) param.getResult();
+
+ // Check for isolate process
+ int userid = Util.getUserId(uid);
+ int start = Util.getUserUid(userid, 99000);
+ int end = Util.getUserUid(userid, 99999);
+ boolean isolated = (uid >= start && uid <= end);
+ if (isolated) {
+ Log.i(TAG, "Skipping isolated " + lpparam.packageName + ":" + uid);
+ return;
+ }
+
+ hookPackage(lpparam, uid, context);
+ }
+ } catch (Throwable ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ XposedBridge.log(ex);
+ }
+ }
+ });
+ }
+
+ private void hookPackage(final XC_LoadPackage.LoadPackageParam lpparam, int uid, final Context context) throws Throwable {
+ // Get assigned hooks
+ List hooks = new ArrayList<>();
+ Cursor chooks = null;
+ try {
+ chooks = context.getContentResolver()
+ .query(XProvider.getURI(), new String[]{"xlua.getAssignedHooks2"},
+ "pkg = ? AND uid = ?", new String[]{lpparam.packageName, Integer.toString(uid)},
+ null);
+ while (chooks != null && chooks.moveToNext()) {
+ byte[] marshaled = chooks.getBlob(0);
+ Parcel parcel = Parcel.obtain();
+ parcel.unmarshall(marshaled, 0, marshaled.length);
+ parcel.setDataPosition(0);
+ XHook hook = XHook.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ hooks.add(hook);
+ }
+ } finally {
+ if (chooks != null)
+ chooks.close();
+ }
+
+ final Map settings = new HashMap<>();
+
+ // Get global settings
+ Cursor csettings1 = null;
+ try {
+ csettings1 = context.getContentResolver()
+ .query(XProvider.getURI(), new String[]{"xlua.getSettings"},
+ "pkg = ? AND uid = ?", new String[]{"global", Integer.toString(uid)},
+ null);
+ while (csettings1 != null && csettings1.moveToNext())
+ settings.put(csettings1.getString(0), csettings1.getString(1));
+ } finally {
+ if (csettings1 != null)
+ csettings1.close();
+ }
+
+ // Get app settings
+ Cursor csettings2 = null;
+ try {
+ csettings2 = context.getContentResolver()
+ .query(XProvider.getURI(), new String[]{"xlua.getSettings"},
+ "pkg = ? AND uid = ?", new String[]{lpparam.packageName, Integer.toString(uid)},
+ null);
+ while (csettings2 != null && csettings2.moveToNext())
+ settings.put(csettings2.getString(0), csettings2.getString(1));
+ } finally {
+ if (csettings2 != null)
+ csettings2.close();
+ }
+
+ Map scriptPrototype = new HashMap<>();
+
+ // Apply hooks
+ PackageInfo pi = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
+ for (final XHook hook : hooks)
+ try {
+ if (!hook.isAvailable(pi.versionCode))
+ continue;
+
+ long install = SystemClock.elapsedRealtime();
+
+ // Compile script
+ final Prototype compiledScript;
+ ScriptHolder sh = new ScriptHolder(hook.getLuaScript());
+ if (scriptPrototype.containsKey(sh))
+ compiledScript = scriptPrototype.get(sh);
+ else {
+ InputStream is = new ByteArrayInputStream(sh.script.getBytes());
+ compiledScript = LuaC.instance.compile(is, "script");
+ scriptPrototype.put(sh, compiledScript);
+ }
+
+ // Get class
+ Class> cls = Class.forName(hook.getResolvedClassName(), false, context.getClassLoader());
+
+ // Handle field method
+ String methodName = hook.getMethodName();
+ if (methodName != null) {
+ String[] m = methodName.split(":");
+ if (m.length > 1) {
+ Field field = cls.getField(m[0]);
+ Object obj = field.get(null);
+ cls = obj.getClass();
+ }
+ methodName = m[m.length - 1];
+ }
+
+ // Get parameter types
+ String[] p = hook.getParameterTypes();
+ final Class>[] paramTypes = new Class[p.length];
+ for (int i = 0; i < p.length; i++)
+ paramTypes[i] = resolveClass(p[i], context.getClassLoader());
+
+ // Get return type
+ final Class> returnType = (hook.getReturnType() == null ? null :
+ resolveClass(hook.getReturnType(), context.getClassLoader()));
+
+ if (methodName != null && methodName.startsWith("#")) {
+ // Get field
+ Field field = resolveField(cls, methodName.substring(1), returnType);
+ field.setAccessible(true);
+
+ if (paramTypes.length > 0)
+ throw new NoSuchFieldException("Field with parameters");
+
+ try {
+ long run = SystemClock.elapsedRealtime();
+
+ // Initialize Lua runtime
+ Globals globals = getGlobals(context, hook, settings);
+ LuaClosure closure = new LuaClosure(compiledScript, globals);
+ closure.call();
+
+ // Check if function exists
+ LuaValue func = globals.get("after");
+ if (func.isnil())
+ return;
+
+ LuaValue[] args = new LuaValue[]{
+ CoerceJavaToLua.coerce(hook),
+ CoerceJavaToLua.coerce(new XParam(context, field, settings))
+ };
+
+ // Run function
+ Varargs result = func.invoke(args);
+
+ // Report use
+ boolean restricted = result.arg1().checkboolean();
+ if (restricted && hook.doUsage()) {
+ Bundle data = new Bundle();
+ data.putString("function", "after");
+ data.putInt("restricted", restricted ? 1 : 0);
+ data.putLong("duration", SystemClock.elapsedRealtime() - run);
+ if (result.narg() > 1) {
+ data.putString("old", result.isnil(2) ? null : result.checkjstring(2));
+ data.putString("new", result.isnil(3) ? null : result.checkjstring(3));
+ }
+ report(context, hook.getId(), "after", "use", data);
+ }
+ } catch (Throwable ex) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Exception:\n");
+ sb.append(Log.getStackTraceString(ex));
+ sb.append("\n");
+
+ sb.append("\nPackage:\n");
+ sb.append(context.getPackageName());
+ sb.append(':');
+ sb.append(Integer.toString(context.getApplicationInfo().uid));
+ sb.append("\n");
+
+ sb.append("\nField:\n");
+ sb.append(field.toString());
+ sb.append("\n");
+
+ Log.e(TAG, sb.toString());
+
+ // Report use error
+ Bundle data = new Bundle();
+ data.putString("function", "after");
+ data.putString("exception", sb.toString());
+ report(context, hook.getId(), "after", "use", data);
+ }
+ } else {
+ // Get method
+ final Member member = resolveMember(cls, methodName, paramTypes);
+
+ // Check return type
+ final Class> memberReturnType = (methodName == null ? null : ((Method) member).getReturnType());
+ if (returnType != null && memberReturnType != null && !memberReturnType.isAssignableFrom(returnType))
+ throw new Throwable("Invalid return type " + memberReturnType + " got " + returnType);
+
+ // Hook method
+ XposedBridge.hookMethod(member, new XC_MethodHook() {
+ private final WeakHashMap threadGlobals = new WeakHashMap<>();
+
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ execute(param, "before");
+ }
+
+ @Override
+ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
+ execute(param, "after");
+ }
+
+ // Execute hook
+ private void execute(MethodHookParam param, String function) {
+ try {
+ long run = SystemClock.elapsedRealtime();
+
+ // Initialize Lua runtime
+ LuaValue func;
+ LuaValue[] args;
+ synchronized (threadGlobals) {
+ Thread thread = Thread.currentThread();
+ if (!threadGlobals.containsKey(thread))
+ threadGlobals.put(thread, getGlobals(context, hook, settings));
+ Globals globals = threadGlobals.get(thread);
+
+ // Define functions
+ LuaClosure closure = new LuaClosure(compiledScript, globals);
+ closure.call();
+
+ // Check if function exists
+ func = globals.get(function);
+ if (func.isnil())
+ return;
+
+ // Build arguments
+ args = new LuaValue[]{
+ CoerceJavaToLua.coerce(hook),
+ CoerceJavaToLua.coerce(new XParam(context, param, settings))
+ };
+ }
+
+ // Run function
+ Varargs result = func.invoke(args);
+
+ // Report use
+ boolean restricted = result.arg1().checkboolean();
+ if (restricted && hook.doUsage()) {
+ Bundle data = new Bundle();
+ data.putString("function", function);
+ data.putInt("restricted", restricted ? 1 : 0);
+ data.putLong("duration", SystemClock.elapsedRealtime() - run);
+ if (result.narg() > 1) {
+ data.putString("old", result.isnil(2) ? null : result.checkjstring(2));
+ data.putString("new", result.isnil(3) ? null : result.checkjstring(3));
+ }
+ report(context, hook.getId(), function, "use", data);
+ }
+ } catch (Throwable ex) {
+ synchronized (threadGlobals) {
+ threadGlobals.remove(Thread.currentThread());
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Exception:\n");
+ sb.append(Log.getStackTraceString(ex));
+ sb.append("\n");
+
+ sb.append("\nPackage:\n");
+ sb.append(context.getPackageName());
+ sb.append(':');
+ sb.append(Integer.toString(context.getApplicationInfo().uid));
+ sb.append("\n");
+
+ sb.append("\nMethod:\n");
+ sb.append(function);
+ sb.append(' ');
+ sb.append(member.toString());
+ sb.append("\n");
+
+ sb.append("\nArguments:\n");
+ if (param.args == null)
+ sb.append("null\n");
+ else
+ for (int i = 0; i < param.args.length; i++) {
+ sb.append(i);
+ sb.append(": ");
+ if (param.args[i] == null)
+ sb.append("null");
+ else {
+ sb.append(param.args[i].toString());
+ sb.append(" (");
+ sb.append(param.args[i].getClass().getName());
+ sb.append(')');
+ }
+ sb.append("\n");
+ }
+
+ sb.append("\nReturn:\n");
+ if (param.getResult() == null)
+ sb.append("null");
+ else {
+ sb.append(param.getResult().toString());
+ sb.append(" (");
+ sb.append(param.getResult().getClass().getName());
+ sb.append(')');
+ }
+ sb.append("\n");
+
+ Log.e(TAG, sb.toString());
+
+ // Report use error
+ Bundle data = new Bundle();
+ data.putString("function", function);
+ data.putString("exception", sb.toString());
+ report(context, hook.getId(), function, "use", data);
+ }
+ }
+ });
+ }
+
+ // Report install
+ if (BuildConfig.DEBUG) {
+ Bundle data = new Bundle();
+ data.putLong("duration", SystemClock.elapsedRealtime() - install);
+ report(context, hook.getId(), null, "install", data);
+ }
+ } catch (Throwable ex) {
+ if (hook.isOptional() &&
+ (ex instanceof NoSuchFieldException ||
+ ex instanceof NoSuchMethodException ||
+ ex instanceof ClassNotFoundException ||
+ ex instanceof NoClassDefFoundError))
+ Log.i(TAG, "Optional hook=" + hook.getId() +
+ ": " + ex.getClass().getName() + ": " + ex.getMessage());
+ else {
+ Log.e(TAG, hook.getId() + ": " + Log.getStackTraceString(ex));
+
+ // Report install error
+ Bundle data = new Bundle();
+ data.putString("exception", ex instanceof LuaError ? ex.getMessage() : Log.getStackTraceString(ex));
+ report(context, hook.getId(), null, "install", data);
+ }
+ }
+ }
+
+ private void report(final Context context, String hook, String function, String event, Bundle data) {
+ final String packageName = context.getPackageName();
+ final int uid = context.getApplicationInfo().uid;
+
+ Bundle args = new Bundle();
+ args.putString("hook", hook);
+ args.putString("packageName", packageName);
+ args.putInt("uid", uid);
+ args.putString("event", event);
+ args.putLong("time", new Date().getTime());
+ args.putBundle("data", data);
+
+ synchronized (queue) {
+ String key = (function == null ? "*" : function) + ":" + event;
+ if (!queue.containsKey(key))
+ queue.put(key, new HashMap());
+ queue.get(key).put(hook, args);
+
+ if (timer == null) {
+ timer = new Timer();
+ timer.schedule(new TimerTask() {
+ public void run() {
+ Log.i(TAG, "Processing event queue package=" + packageName + ":" + uid);
+
+ List work = new ArrayList<>();
+ synchronized (queue) {
+ for (String key : queue.keySet())
+ for (String hook : queue.get(key).keySet())
+ work.add(queue.get(key).get(hook));
+ queue.clear();
+ timer = null;
+ }
+
+ for (Bundle args : work)
+ context.getContentResolver()
+ .call(XProvider.getURI(), "xlua", "report", args);
+ }
+ }, 1000);
+ }
+ }
+ }
+
+ private static Class> resolveClass(String name, ClassLoader loader) throws ClassNotFoundException {
+ if ("boolean".equals(name))
+ return boolean.class;
+ else if ("byte".equals(name))
+ return byte.class;
+ else if ("char".equals(name))
+ return char.class;
+ else if ("short".equals(name))
+ return short.class;
+ else if ("int".equals(name))
+ return int.class;
+ else if ("long".equals(name))
+ return long.class;
+ else if ("float".equals(name))
+ return float.class;
+ else if ("double".equals(name))
+ return double.class;
+
+ else if ("boolean[]".equals(name))
+ return boolean[].class;
+ else if ("byte[]".equals(name))
+ return byte[].class;
+ else if ("char[]".equals(name))
+ return char[].class;
+ else if ("short[]".equals(name))
+ return short[].class;
+ else if ("int[]".equals(name))
+ return int[].class;
+ else if ("long[]".equals(name))
+ return long[].class;
+ else if ("float[]".equals(name))
+ return float[].class;
+ else if ("double[]".equals(name))
+ return double[].class;
+
+ else if ("void".equals(name))
+ return Void.TYPE;
+
+ else
+ return Class.forName(name, false, loader);
+ }
+
+ private static Field resolveField(Class> cls, String name, Class> type) throws NoSuchFieldException {
+ try {
+ Class> c = cls;
+ while (c != null && !c.equals(Object.class))
+ try {
+ Field field = c.getDeclaredField(name);
+ if (!field.getType().equals(type))
+ throw new NoSuchFieldException();
+ return field;
+ } catch (NoSuchFieldException ex) {
+ for (Field field : c.getDeclaredFields()) {
+ if (!name.equals(field.getName()))
+ continue;
+
+ if (!field.getType().equals(type))
+ continue;
+
+ Log.i(TAG, "Resolved field=" + field);
+ return field;
+ }
+ }
+ throw new NoSuchFieldException(name);
+ } catch (NoSuchFieldException ex) {
+ Class> c = cls;
+ while (c != null && !c.equals(Object.class)) {
+ Log.i(TAG, c.toString());
+ for (Method method : c.getDeclaredMethods())
+ Log.i(TAG, "- " + method.toString());
+ c = c.getSuperclass();
+ }
+ throw ex;
+ }
+ }
+
+ private static Member resolveMember(Class> cls, String name, Class>[] params) throws NoSuchMethodException {
+ boolean exists = false;
+ try {
+ Class> c = cls;
+ while (c != null && !c.equals(Object.class))
+ try {
+ if (name == null)
+ return c.getDeclaredConstructor(params);
+ else
+ return c.getDeclaredMethod(name, params);
+ } catch (NoSuchMethodException ex) {
+ for (Member member : name == null ? c.getDeclaredConstructors() : c.getDeclaredMethods()) {
+ if (name != null && !name.equals(member.getName()))
+ continue;
+
+ exists = true;
+
+ Class>[] mparams = (name == null
+ ? ((Constructor) member).getParameterTypes()
+ : ((Method) member).getParameterTypes());
+
+ if (mparams.length != params.length)
+ continue;
+
+ boolean same = true;
+ for (int i = 0; i < mparams.length; i++) {
+ if (!mparams[i].isAssignableFrom(params[i])) {
+ same = false;
+ break;
+ }
+ }
+ if (!same)
+ continue;
+
+ Log.i(TAG, "Resolved member=" + member);
+ return member;
+ }
+ c = c.getSuperclass();
+ if (c == null)
+ throw ex;
+ }
+ throw new NoSuchMethodException(name);
+ } catch (NoSuchMethodException ex) {
+ Class> c = cls;
+ while (c != null && !c.equals(Object.class)) {
+ Log.i(TAG, c.toString());
+ for (Member member : name == null ? c.getDeclaredConstructors() : c.getDeclaredMethods())
+ if (!exists || name == null || name.equals(member.getName()))
+ Log.i(TAG, " " + member.toString());
+ c = c.getSuperclass();
+ }
+ throw ex;
+ }
+ }
+
+ private static Globals getGlobals(Context context, XHook hook, Map settings) {
+ Globals globals = JsePlatform.standardGlobals();
+ // base, bit32, coroutine, io, math, os, package, string, table, luajava
+
+ if (BuildConfig.DEBUG)
+ globals.load(new DebugLib());
+
+ globals.set("log", new LuaLog(context.getPackageName(), context.getApplicationInfo().uid, hook.getId()));
+ globals.set("hook", new LuaHook(context, settings));
+
+ return new LuaLocals(globals);
+ }
+
+ private static class LuaLocals extends Globals {
+ LuaLocals(Globals globals) {
+ this.presize(globals.length(), 0);
+ Varargs entry = globals.next(LuaValue.NIL);
+ while (!entry.arg1().isnil()) {
+ LuaValue key = entry.arg1();
+ LuaValue value = entry.arg(2);
+ super.rawset(key, value);
+ entry = globals.next(entry.arg1());
+ }
+ }
+
+ @Override
+ public void set(int key, LuaValue value) {
+ if (value.isfunction())
+ super.set(key, value);
+ else
+ error("Globals not allowed: set " + value);
+ }
+
+ @Override
+ public void rawset(int key, LuaValue value) {
+ if (value.isfunction())
+ super.rawset(key, value);
+ else
+ error("Globals not allowed: rawset " + value);
+ }
+
+ @Override
+ public void rawset(LuaValue key, LuaValue value) {
+ if (value.isfunction())
+ super.rawset(key, value);
+ else
+ error("Globals not allowed: " + key + "=" + value);
+ }
+ }
+
+ private static class LuaHook extends VarArgFunction {
+ private Context context;
+ private Map settings;
+
+ LuaHook(Context context, Map settings) {
+ this.context = context;
+ this.settings = settings;
+ }
+
+ @Override
+ public Varargs invoke(final Varargs args) {
+ Class> cls = args.arg(1).checkuserdata().getClass();
+ String m = args.arg(2).checkjstring();
+ args.arg(3).checkfunction();
+ Log.i(TAG, "Dynamic hook " + cls.getName() + "." + m);
+ final LuaValue fun = args.arg(3);
+ final List xargs = new ArrayList<>();
+ for (int i = 4; i <= args.narg(); i++)
+ xargs.add(args.arg(i));
+
+ XposedBridge.hookAllMethods(cls, m, new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ execute("before", param);
+ }
+
+ @Override
+ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
+ execute("after", param);
+ }
+
+ private void execute(String when, MethodHookParam param) {
+ Log.i(TAG, "Dynamic invoke " + param.method);
+ List values = new ArrayList<>();
+ values.add(LuaValue.valueOf(when));
+ values.add(CoerceJavaToLua.coerce(new XParam(context, param, settings)));
+ for (int i = 0; i < xargs.size(); i++)
+ values.add(xargs.get(i));
+ fun.invoke(values.toArray(new LuaValue[0]));
+ }
+ });
+
+ return LuaValue.NIL;
+ }
+ }
+
+ private static class LuaLog extends OneArgFunction {
+ private final String packageName;
+ private final int uid;
+ private final String hook;
+
+ LuaLog(String packageName, int uid, String hook) {
+ this.packageName = packageName;
+ this.uid = uid;
+ this.hook = hook;
+ }
+
+ @Override
+ public LuaValue call(LuaValue arg) {
+ Log.i(TAG, "Log " + packageName + ":" + uid + " " + hook + " " +
+ arg.toString() + " (" + arg.typename() + ")");
+ return LuaValue.NIL;
+ }
+ }
+
+ private class ScriptHolder {
+ String script;
+
+ ScriptHolder(String script) {
+ String[] lines = script.split("\\r?\\n");
+ StringBuilder sb = new StringBuilder();
+ for (String line : lines) {
+ if (!line.startsWith("--"))
+ sb.append(line.trim());
+ sb.append("\n");
+ }
+ this.script = sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ScriptHolder))
+ return false;
+ ScriptHolder other = (ScriptHolder) obj;
+ return this.script.equals(other.script);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.script.hashCode();
+ }
+ }
+}
diff --git a/app/src/main/java/eu/faircode/xlua/XParam.java b/app/src/main/java/eu/faircode/xlua/XParam.java
index b7dc7081..6ce29bd0 100644
--- a/app/src/main/java/eu/faircode/xlua/XParam.java
+++ b/app/src/main/java/eu/faircode/xlua/XParam.java
@@ -1,26 +1,30 @@
/*
- This file is part of XPrivacy/Lua.
+ This file is part of XPrivacyLua.
- XPrivacy/Lua is free software: you can redistribute it and/or modify
+ XPrivacyLua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- XPrivacy/Lua is distributed in the hope that it will be useful,
+ XPrivacyLua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with XPrivacy/Lua. If not, see .
+ along with XPrivacyLua. If not, see .
- Copyright 2017-2018 Marcel Bokhorst (M66B)
+ Copyright 2017-2019 Marcel Bokhorst (M66B)
*/
package eu.faircode.xlua;
+import android.content.Context;
import android.util.Log;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
@@ -30,72 +34,204 @@
public class XParam {
private static final String TAG = "XLua.XParam";
- private String packageName;
- private int uid;
- private XC_MethodHook.MethodHookParam param;
+ private final Context context;
+ private final Field field;
+ private final XC_MethodHook.MethodHookParam param;
+ private final Class>[] paramTypes;
+ private final Class> returnType;
+ private final Map settings;
private static final Map