diff --git a/CHANGELOG.md b/CHANGELOG.md
index 20fa461a9..a6f07ba1f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@ Changelog
* Added restriction *getInstalledProvidersForProfile* ([issue](/../../issues/1757))
* Allow white listing / show parameter of *getExternalStorageState* ([issue](/../../issues/1757))
* Added quirk *nousage* to disable usage data for specific applications ([issue](/../../issues/2085))
+* Added restrictions for [UsageStatsManager](https://developer.android.com/reference/android/app/usage/UsageStatsManager.html) ([issue](/../../issues/1757))
[Open issues](https://github.com/M66B/XPrivacy/issues?state=open)
diff --git a/res/values/functions.xml b/res/values/functions.xml
index 61299eee5..221a71d12 100644
--- a/res/values/functions.xml
+++ b/res/values/functions.xml
@@ -395,6 +395,13 @@
Google documentation]]>
Google documentation]]>
Documentation]]>
+ Documentation]]>
+ Documentation]]>
+ Documentation]]>
+ Documentation]]>
+ Documentation]]>
+ Documentation]]>
+ Documentation]]>
Google documentation]]>
diff --git a/src/biz/bokhorst/xprivacy/Meta.java b/src/biz/bokhorst/xprivacy/Meta.java
index 9f1ce845d..17b2b33b2 100644
--- a/src/biz/bokhorst/xprivacy/Meta.java
+++ b/src/biz/bokhorst/xprivacy/Meta.java
@@ -467,6 +467,14 @@ public static List get() {
mListHook.add(new Hook("system", "IntentFirewall", "", 19, "2.2.2", null).AOSP(19).dangerous().whitelist(cTypeAction));
+ mListHook.add(new Hook("system", "queryAndAggregateUsageStats", null, 21, "3.5.6", null).notAOSP(21));
+ mListHook.add(new Hook("system", "queryConfigurations", null, 21, "3.5.6", null).notAOSP(21));
+ mListHook.add(new Hook("system", "queryEvents", null, 21, "3.5.6", null).notAOSP(21));
+ mListHook.add(new Hook("system", "queryUsageStats", null, 21, "3.5.6", null).notAOSP(21));
+ mListHook.add(new Hook("system", "Srv_queryConfigurationStats", null, 21, "3.5.6", null).AOSP(21));
+ mListHook.add(new Hook("system", "Srv_queryEvents", null, 21, "3.5.6", null).AOSP(21));
+ mListHook.add(new Hook("system", "Srv_queryUsageStats", null, 21, "3.5.6", null).AOSP(21));
+
mListHook.add(new Hook("view", "loadUrl", "", 1, null, null).unsafe().whitelist(cTypeUrl));
mListHook.add(new Hook("view", "WebView", "", 1, null, null).unsafe());
mListHook.add(new Hook("view", "getDefaultUserAgent", "", 17, null, null).unsafe());
diff --git a/src/biz/bokhorst/xprivacy/XPrivacy.java b/src/biz/bokhorst/xprivacy/XPrivacy.java
index a9bab554f..77426fe51 100644
--- a/src/biz/bokhorst/xprivacy/XPrivacy.java
+++ b/src/biz/bokhorst/xprivacy/XPrivacy.java
@@ -328,6 +328,9 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// Telephone service
hookAll(XTelephonyManager.getInstances(null), null, mSecret);
+ // Usage statistics manager
+ hookAll(XUsageStatsManager.getInstances(), null, mSecret);
+
// USB device
hookAll(XUsbDevice.getInstances(), null, mSecret);
diff --git a/src/biz/bokhorst/xprivacy/XUsageStatsManager.java b/src/biz/bokhorst/xprivacy/XUsageStatsManager.java
new file mode 100644
index 000000000..0a0fbbbe5
--- /dev/null
+++ b/src/biz/bokhorst/xprivacy/XUsageStatsManager.java
@@ -0,0 +1,111 @@
+package biz.bokhorst.xprivacy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import android.app.usage.ConfigurationStats;
+import android.app.usage.UsageStats;
+
+import biz.bokhorst.xprivacy.XHook;
+
+public class XUsageStatsManager extends XHook {
+ private Methods mMethod;
+
+ private XUsageStatsManager(Methods method, String restrictionName) {
+ super(restrictionName, method.name(), null);
+ mMethod = method;
+ }
+
+ public String getClassName() {
+ if (mMethod.name().startsWith("Srv_"))
+ return "com.android.server.usage.UserUsageStatsService";
+ else
+ return "android.app.usage.UsageStatsManager";
+ }
+
+ // @formatter:off
+
+ // public Map queryAndAggregateUsageStats(long beginTime, long endTime)
+ // public List queryConfigurations(int intervalType, long beginTime, long endTime)
+ // public UsageEvents queryEvents(long beginTime, long endTime)
+ // public List queryUsageStats(int intervalType, long beginTime, long endTime)
+ // https://developer.android.com/reference/android/app/usage/UsageStatsManager.html
+
+ // List queryConfigurationStats(int userId, int bucketType, long beginTime, long endTime)
+ // UsageEvents queryEvents(int userId, long beginTime, long endTime)
+ // List queryUsageStats(int userId, int bucketType, long beginTime, long endTime)
+ // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/com/android/server/usage/UsageStatsService.java
+
+ private enum Methods {
+ queryAndAggregateUsageStats, queryConfigurations, queryEvents, queryUsageStats,
+ Srv_queryConfigurationStats, Srv_queryEvents, Srv_queryUsageStats
+ };
+
+ // @formatter:on
+
+ public static List getInstances() {
+ List listHook = new ArrayList();
+ listHook.add(new XUsageStatsManager(Methods.queryAndAggregateUsageStats, PrivacyManager.cSystem));
+ listHook.add(new XUsageStatsManager(Methods.queryConfigurations, PrivacyManager.cSystem));
+ listHook.add(new XUsageStatsManager(Methods.queryEvents, PrivacyManager.cSystem));
+ listHook.add(new XUsageStatsManager(Methods.queryUsageStats, PrivacyManager.cSystem));
+
+ listHook.add(new XUsageStatsManager(Methods.Srv_queryConfigurationStats, PrivacyManager.cSystem));
+ listHook.add(new XUsageStatsManager(Methods.Srv_queryEvents, PrivacyManager.cSystem));
+ listHook.add(new XUsageStatsManager(Methods.Srv_queryUsageStats, PrivacyManager.cSystem));
+ return listHook;
+ }
+
+ @Override
+ protected void before(XParam param) throws Throwable {
+ switch (mMethod) {
+ case queryAndAggregateUsageStats:
+ case queryConfigurations:
+ case queryUsageStats:
+ case Srv_queryConfigurationStats:
+ case Srv_queryUsageStats:
+ // Do nothing
+ break;
+ case queryEvents:
+ if (isRestricted(param))
+ if (param.args.length > 1) {
+ param.args[0] = 0;
+ param.args[1] = 0;
+ }
+ break;
+
+ case Srv_queryEvents:
+ if (isRestricted(param))
+ if (param.args.length > 2) {
+ param.args[1] = 0;
+ param.args[2] = 0;
+ }
+ break;
+ }
+ }
+
+ @Override
+ protected void after(XParam param) throws Throwable {
+ switch (mMethod) {
+ case queryAndAggregateUsageStats:
+ if (isRestricted(param))
+ param.setResult(new HashMap());
+ break;
+ case queryConfigurations:
+ case Srv_queryConfigurationStats:
+ if (isRestricted(param))
+ param.setResult(new ArrayList());
+ break;
+ case queryEvents:
+ case Srv_queryEvents:
+ // Do nothing
+ break;
+ case queryUsageStats:
+ case Srv_queryUsageStats:
+ if (isRestricted(param))
+ param.setResult(new ArrayList());
+ break;
+ }
+ }
+}