summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2022-12-08 19:36:13 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-12-08 19:36:13 +0000
commit2da4d090932a5f07a965c9ae7e4dac264a9a60f6 (patch)
treedd36517b5c7a9ff702570b3ee94983316810c3ba
parent748489854e2cc6b5477d9deb68c0b595e50fd3b4 (diff)
parent220589b3824cb19d089c7fe0664da40df4bd3b76 (diff)
Merge "Change exact alarm permission to be denied by default"
-rw-r--r--apex/jobscheduler/framework/java/android/app/AlarmManager.java4
-rw-r--r--services/core/java/com/android/server/appop/AppOpsServiceImpl.java81
-rw-r--r--services/tests/mockingservicestests/assets/AppOpsUpgradeTest/appops-version-1.xml781
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java256
4 files changed, 1071 insertions, 51 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index dade7c3d84a8..53e81c789d2b 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -27,7 +27,6 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -285,11 +284,10 @@ public class AlarmManager {
* The permission {@link Manifest.permission#SCHEDULE_EXACT_ALARM} will be denied, unless the
* user explicitly allows it from Settings.
*
- * TODO (b/226439802): Either enable it in the next SDK or replace it with a better alternative.
* @hide
*/
@ChangeId
- @Disabled
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = 226439802L;
@UnsupportedAppUsage
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceImpl.java b/services/core/java/com/android/server/appop/AppOpsServiceImpl.java
index 70f3bcc64ef0..ddcccb527ff4 100644
--- a/services/core/java/com/android/server/appop/AppOpsServiceImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsServiceImpl.java
@@ -44,6 +44,7 @@ import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
+import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
import static android.app.AppOpsManager.OP_VIBRATE;
import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED;
import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_STARTED;
@@ -130,6 +131,8 @@ import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemServerInitThreadPool;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.component.ParsedAttribution;
@@ -163,12 +166,30 @@ class AppOpsServiceImpl implements AppOpsServiceInterface {
static final String TAG = "AppOps";
static final boolean DEBUG = false;
+ /**
+ * Sentinel integer version to denote that there was no appops.xml found on boot.
+ * This will happen when a device boots with no existing userdata.
+ */
+ private static final int NO_FILE_VERSION = -2;
+
+ /**
+ * Sentinel integer version to denote that there was no version in the appops.xml found on boot.
+ * This means the file is coming from a build before versioning was added.
+ */
private static final int NO_VERSION = -1;
+
/**
* Increment by one every time and add the corresponding upgrade logic in
- * {@link #upgradeLocked(int)} below. The first version was 1
+ * {@link #upgradeLocked(int)} below. The first version was 1.
*/
- private static final int CURRENT_VERSION = 1;
+ @VisibleForTesting
+ static final int CURRENT_VERSION = 2;
+
+ /**
+ * This stores the version of appops.xml seen at boot. If this is smaller than
+ * {@link #CURRENT_VERSION}, then we will run {@link #upgradeLocked(int)} on startup.
+ */
+ private int mVersionAtBoot = NO_FILE_VERSION;
// Write at most every 30 minutes.
static final long WRITE_DELAY = DEBUG ? 1000 : 30 * 60 * 1000;
@@ -936,6 +957,10 @@ class AppOpsServiceImpl implements AppOpsServiceInterface {
@Override
public void systemReady() {
+ synchronized (this) {
+ upgradeLocked(mVersionAtBoot);
+ }
+
mConstants.startMonitoring(mContext.getContentResolver());
mHistoricalRegistry.systemReady(mContext.getContentResolver());
@@ -3191,7 +3216,6 @@ class AppOpsServiceImpl implements AppOpsServiceInterface {
@Override
public void readState() {
- int oldVersion = NO_VERSION;
synchronized (mFile) {
synchronized (this) {
FileInputStream stream;
@@ -3216,7 +3240,7 @@ class AppOpsServiceImpl implements AppOpsServiceInterface {
throw new IllegalStateException("no start tag found");
}
- oldVersion = parser.getAttributeInt(null, "v", NO_VERSION);
+ mVersionAtBoot = parser.getAttributeInt(null, "v", NO_VERSION);
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -3261,12 +3285,11 @@ class AppOpsServiceImpl implements AppOpsServiceInterface {
}
}
}
- synchronized (this) {
- upgradeLocked(oldVersion);
- }
}
- private void upgradeRunAnyInBackgroundLocked() {
+ @VisibleForTesting
+ @GuardedBy("this")
+ void upgradeRunAnyInBackgroundLocked() {
for (int i = 0; i < mUidStates.size(); i++) {
final UidState uidState = mUidStates.valueAt(i);
if (uidState == null) {
@@ -3303,8 +3326,45 @@ class AppOpsServiceImpl implements AppOpsServiceInterface {
}
}
+ /**
+ * The interpretation of the default mode - MODE_DEFAULT - for OP_SCHEDULE_EXACT_ALARM is
+ * changing. Simultaneously, we want to change this op's mode from MODE_DEFAULT to MODE_ALLOWED
+ * for already installed apps. For newer apps, it will stay as MODE_DEFAULT.
+ */
+ @VisibleForTesting
+ @GuardedBy("this")
+ void upgradeScheduleExactAlarmLocked() {
+ final PermissionManagerServiceInternal pmsi = LocalServices.getService(
+ PermissionManagerServiceInternal.class);
+ final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+ final PackageManagerInternal pmi = getPackageManagerInternal();
+
+ final String[] packagesDeclaringPermission = pmsi.getAppOpPermissionPackages(
+ AppOpsManager.opToPermission(OP_SCHEDULE_EXACT_ALARM));
+ final int[] userIds = umi.getUserIds();
+
+ for (final String pkg : packagesDeclaringPermission) {
+ for (int userId : userIds) {
+ final int uid = pmi.getPackageUid(pkg, 0, userId);
+
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null) {
+ uidState = new UidState(uid);
+ mUidStates.put(uid, uidState);
+ }
+ final int oldMode = uidState.getUidMode(OP_SCHEDULE_EXACT_ALARM);
+ if (oldMode == AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM)) {
+ uidState.setUidMode(OP_SCHEDULE_EXACT_ALARM, MODE_ALLOWED);
+ }
+ }
+ // This appop is meant to be controlled at a uid level. So we leave package modes as
+ // they are.
+ }
+ }
+
+ @GuardedBy("this")
private void upgradeLocked(int oldVersion) {
- if (oldVersion >= CURRENT_VERSION) {
+ if (oldVersion == NO_FILE_VERSION || oldVersion >= CURRENT_VERSION) {
return;
}
Slog.d(TAG, "Upgrading app-ops xml from version " + oldVersion + " to " + CURRENT_VERSION);
@@ -3313,6 +3373,9 @@ class AppOpsServiceImpl implements AppOpsServiceInterface {
upgradeRunAnyInBackgroundLocked();
// fall through
case 1:
+ upgradeScheduleExactAlarmLocked();
+ // fall through
+ case 2:
// for future upgrades
}
scheduleFastWriteLocked();
diff --git a/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/appops-version-1.xml b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/appops-version-1.xml
new file mode 100644
index 000000000000..1dde7dcc4720
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/appops-version-1.xml
@@ -0,0 +1,781 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<app-ops v="1">
+ <uid n="1001">
+ <op n="0" m="4" />
+ <op n="1" m="4" />
+ <op n="15" m="0" />
+ <op n="27" m="4" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="1002">
+ <op n="0" m="4" />
+ <op n="1" m="4" />
+ <op n="15" m="0" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10077">
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10079">
+ <op n="116" m="1" />
+ </uid>
+ <uid n="10080">
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10081">
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10086">
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10087">
+ <op n="59" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10090">
+ <op n="0" m="4" />
+ <op n="1" m="4" />
+ </uid>
+ <uid n="10096">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ </uid>
+ <uid n="10112">
+ <op n="11" m="1" />
+ <op n="51" m="1" />
+ <op n="59" m="1" />
+ <op n="62" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10113">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="51" m="1" />
+ <op n="62" m="1" />
+ </uid>
+ <uid n="10114">
+ <op n="4" m="1" />
+ </uid>
+ <uid n="10115">
+ <op n="0" m="1" />
+ </uid>
+ <uid n="10116">
+ <op n="0" m="1" />
+ </uid>
+ <uid n="10117">
+ <op n="0" m="4" />
+ <op n="1" m="4" />
+ <op n="13" m="1" />
+ <op n="14" m="1" />
+ <op n="16" m="1" />
+ <op n="26" m="4" />
+ <op n="27" m="4" />
+ </uid>
+ <uid n="10118">
+ <op n="51" m="1" />
+ </uid>
+ <uid n="10119">
+ <op n="11" m="1" />
+ <op n="77" m="1" />
+ <op n="111" m="1" />
+ </uid>
+ <uid n="10120">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="4" m="1" />
+ <op n="6" m="1" />
+ <op n="7" m="1" />
+ <op n="11" m="1" />
+ <op n="13" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ <op n="54" m="1" />
+ <op n="59" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10121">
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10122">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="51" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10123">
+ <op n="0" m="4" />
+ <op n="1" m="4" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10124">
+ <op n="11" m="1" />
+ <op n="26" m="1" />
+ </uid>
+ <uid n="10125">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10127">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="26" m="4" />
+ <op n="27" m="4" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="65" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ <op n="111" m="1" />
+ </uid>
+ <uid n="10129">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ </uid>
+ <uid n="10130">
+ <op n="51" m="1" />
+ </uid>
+ <uid n="10131">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10132">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ <op n="69" m="1" />
+ <op n="79" m="1" />
+ </uid>
+ <uid n="10133">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10136">
+ <op n="0" m="1" />
+ <op n="4" m="1" />
+ <op n="77" m="1" />
+ <op n="87" m="1" />
+ <op n="111" m="1" />
+ <op n="114" m="1" />
+ </uid>
+ <uid n="10137">
+ <op n="62" m="1" />
+ </uid>
+ <uid n="10138">
+ <op n="26" m="4" />
+ </uid>
+ <uid n="10140">
+ <op n="26" m="4" />
+ <op n="27" m="4" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10141">
+ <op n="11" m="1" />
+ <op n="27" m="1" />
+ <op n="111" m="1" />
+ </uid>
+ <uid n="10142">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10144">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="4" m="1" />
+ <op n="11" m="1" />
+ <op n="27" m="4" />
+ <op n="111" m="1" />
+ </uid>
+ <uid n="10145">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10149">
+ <op n="11" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10150">
+ <op n="51" m="1" />
+ </uid>
+ <uid n="10151">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10152">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10154">
+ <op n="0" m="4" />
+ <op n="1" m="4" />
+ <op n="27" m="4" />
+ </uid>
+ <uid n="10155">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ </uid>
+ <uid n="10157">
+ <op n="13" m="1" />
+ </uid>
+ <uid n="10158">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10160">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="11" m="1" />
+ <op n="26" m="4" />
+ <op n="27" m="4" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10161">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="11" m="1" />
+ <op n="51" m="1" />
+ <op n="77" m="1" />
+ <op n="87" m="1" />
+ <op n="111" m="1" />
+ <op n="114" m="1" />
+ </uid>
+ <uid n="10162">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="15" m="0" />
+ <op n="26" m="4" />
+ <op n="27" m="4" />
+ <op n="87" m="1" />
+ <op n="89" m="0" />
+ </uid>
+ <uid n="10163">
+ <op n="26" m="4" />
+ <op n="27" m="4" />
+ <op n="56" m="4" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10164">
+ <op n="26" m="1" />
+ <op n="27" m="4" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="69" m="1" />
+ <op n="79" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10169">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10170">
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10171">
+ <op n="26" m="1" />
+ <op n="51" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10172">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10173">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="23" m="0" />
+ <op n="26" m="1" />
+ <op n="51" m="1" />
+ <op n="62" m="1" />
+ <op n="65" m="1" />
+ </uid>
+ <uid n="10175">
+ <op n="0" m="1" />
+ <op n="11" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ </uid>
+ <uid n="10178">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="11" m="1" />
+ <op n="27" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10179">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="4" m="1" />
+ <op n="11" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ <op n="51" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="62" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10180">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10181">
+ <op n="4" m="1" />
+ <op n="11" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ <op n="59" m="1" />
+ <op n="62" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="1110181">
+ <op n="4" m="1" />
+ <op n="11" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ <op n="107" m="2" />
+ </uid>
+ <uid n="10182">
+ <op n="27" m="4" />
+ </uid>
+ <uid n="10183">
+ <op n="11" m="1" />
+ <op n="26" m="4" />
+ </uid>
+ <uid n="10184">
+ <op n="4" m="1" />
+ <op n="11" m="1" />
+ <op n="13" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="4" />
+ </uid>
+ <uid n="10185">
+ <op n="8" m="1" />
+ <op n="59" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10187">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10189">
+ <op n="11" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ <op n="51" m="1" />
+ </uid>
+ <uid n="10190">
+ <op n="0" m="1" />
+ <op n="13" m="1" />
+ </uid>
+ <uid n="10191">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10192">
+ <op n="11" m="1" />
+ <op n="13" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ <op n="51" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10193">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10197">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10198">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="4" m="1" />
+ <op n="11" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="77" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ <op n="90" m="1" />
+ <op n="107" m="0" />
+ <op n="111" m="1" />
+ </uid>
+ <uid n="10199">
+ <op n="4" m="1" />
+ <op n="11" m="1" />
+ <op n="26" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="62" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10200">
+ <op n="11" m="1" />
+ <op n="65" m="1" />
+ <op n="107" m="1" />
+ </uid>
+ <uid n="1110200">
+ <op n="11" m="1" />
+ <op n="65" m="1" />
+ <op n="107" m="2" />
+ </uid>
+ <uid n="10201">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="4" m="1" />
+ <op n="11" m="1" />
+ <op n="51" m="1" />
+ <op n="62" m="1" />
+ <op n="84" m="0" />
+ <op n="86" m="0" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10206">
+ <op n="0" m="4" />
+ <op n="1" m="4" />
+ <op n="26" m="4" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10209">
+ <op n="11" m="1" />
+ <op n="51" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10210">
+ <op n="51" m="1" />
+ </uid>
+ <uid n="10212">
+ <op n="11" m="1" />
+ <op n="62" m="1" />
+ </uid>
+ <uid n="10214">
+ <op n="26" m="4" />
+ </uid>
+ <uid n="10216">
+ <op n="51" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10225">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="51" m="1" />
+ <op n="59" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10229">
+ <op n="11" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="62" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10231">
+ <op n="51" m="1" />
+ </uid>
+ <uid n="10232">
+ <op n="51" m="1" />
+ </uid>
+ <uid n="10234">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="11" m="1" />
+ <op n="13" m="1" />
+ <op n="20" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10235">
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10237">
+ <op n="0" m="4" />
+ <op n="1" m="4" />
+ </uid>
+ <uid n="10238">
+ <op n="26" m="4" />
+ <op n="27" m="4" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10240">
+ <op n="112" m="1" />
+ </uid>
+ <uid n="10241">
+ <op n="59" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10245">
+ <op n="13" m="1" />
+ <op n="51" m="1" />
+ </uid>
+ <uid n="10247">
+ <op n="0" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="0" />
+ <op n="90" m="1" />
+ </uid>
+ <uid n="10254">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10255">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10256">
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10258">
+ <op n="11" m="1" />
+ </uid>
+ <uid n="10260">
+ <op n="51" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <uid n="10262">
+ <op n="15" m="0" />
+ </uid>
+ <uid n="10266">
+ <op n="0" m="4" />
+ </uid>
+ <uid n="10267">
+ <op n="0" m="1" />
+ <op n="1" m="1" />
+ <op n="4" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="62" m="1" />
+ <op n="77" m="1" />
+ <op n="81" m="1" />
+ <op n="83" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ <op n="107" m="2" />
+ <op n="111" m="1" />
+ </uid>
+ <uid n="10268">
+ <op n="4" m="1" />
+ <op n="11" m="1" />
+ <op n="62" m="1" />
+ </uid>
+ <uid n="10269">
+ <op n="11" m="1" />
+ <op n="26" m="1" />
+ <op n="27" m="1" />
+ <op n="59" m="1" />
+ <op n="60" m="1" />
+ <op n="85" m="1" />
+ <op n="87" m="1" />
+ </uid>
+ <pkg n="com.google.android.iwlan">
+ <uid n="0">
+ <op n="1" />
+ <op n="75" m="0" />
+ </uid>
+ </pkg>
+ <pkg n="com.android.phone">
+ <uid n="0">
+ <op n="1" />
+ <op n="75" m="0" />
+ </uid>
+ </pkg>
+ <pkg n="android">
+ <uid n="1000">
+ <op n="0">
+ <st n="214748364801" t="1670287941040" />
+ </op>
+ <op n="4">
+ <st n="214748364801" t="1670289665522" />
+ </op>
+ <op n="6">
+ <st n="214748364801" t="1670287946650" />
+ </op>
+ <op n="8">
+ <st n="214748364801" t="1670289624396" />
+ </op>
+ <op n="14">
+ <st n="214748364801" t="1670287951031" />
+ </op>
+ <op n="40">
+ <st n="214748364801" t="1670291786337" d="156" />
+ </op>
+ <op n="41">
+ <st id="SensorNotificationService" n="214748364801" t="1670287585567" d="4251183" />
+ <st id="CountryDetector" n="214748364801" t="1670287583306" d="6700" />
+ </op>
+ <op n="43">
+ <st n="214748364801" r="1670291755062" />
+ </op>
+ <op n="61">
+ <st n="214748364801" r="1670291754997" />
+ </op>
+ <op n="105">
+ <st n="214748364801" r="1670291473903" />
+ <st id="GnssService" n="214748364801" r="1670288044920" />
+ </op>
+ <op n="111">
+ <st n="214748364801" t="1670291441554" />
+ </op>
+ </uid>
+ </pkg>
+ <pkg n="com.android.server.telecom">
+ <uid n="1000">
+ <op n="6">
+ <st n="214748364801" t="1670287609092" />
+ </op>
+ <op n="111">
+ <st n="214748364801" t="1670287583728" />
+ </op>
+ </uid>
+ </pkg>
+ <pkg n="com.android.settings">
+ <uid n="1000">
+ <op n="43">
+ <st n="214748364801" r="1670291447349" />
+ </op>
+ <op n="105">
+ <st n="214748364801" r="1670291399231" />
+ </op>
+ <op n="111">
+ <st n="214748364801" t="1670291756910" />
+ </op>
+ </uid>
+ </pkg>
+ <pkg n="com.android.phone">
+ <uid n="1001">
+ <op n="15">
+ <st n="214748364801" t="1670287951022" />
+ </op>
+ <op n="40">
+ <st n="214748364801" t="1670291786177" />
+ </op>
+ <op n="105">
+ <st n="214748364801" r="1670291756403" />
+ </op>
+ </uid>
+ </pkg>
+ <pkg n="com.android.bluetooth">
+ <uid n="1002">
+ <op n="4">
+ <st n="214748364801" t="1670289671076" />
+ </op>
+ <op n="40">
+ <st n="214748364801" t="1670287585676" d="8" />
+ </op>
+ <op n="43">
+ <st n="214748364801" r="1670287585818" />
+ </op>
+ <op n="77">
+ <st n="214748364801" t="1670288037629" />
+ </op>
+ <op n="111">
+ <st n="214748364801" t="1670287592081" />
+ </op>
+ </uid>
+ </pkg>
+ <pkg n="com.android.vending">
+ <uid n="10136">
+ <op n="40">
+ <st n="429496729601" t="1670289621210" d="114" />
+ <st n="858993459201" t="1670289879730" d="349" />
+ <st n="1288490188801" t="1670287942622" d="937" />
+ </op>
+ <op n="43">
+ <st n="429496729601" r="1670289755305" />
+ <st n="858993459201" r="1670288019246" />
+ <st n="1073741824001" r="1670289571783" />
+ <st n="1288490188801" r="1670289373336" />
+ </op>
+ <op n="76">
+ <st n="429496729601" t="1670289748735" d="15991" />
+ <st n="858993459201" t="1670291395180" d="79201" />
+ <st n="1073741824001" t="1670291395168" d="12" />
+ <st n="1288490188801" t="1670291526029" d="3" />
+ <st n="1503238553601" t="1670291526032" d="310718" />
+ </op>
+ <op n="105">
+ <st n="429496729601" r="1670289538910" />
+ <st n="858993459201" r="1670288054519" />
+ <st n="1073741824001" r="1670287599379" />
+ <st n="1288490188801" r="1670289526854" />
+ <st n="1503238553601" r="1670289528242" />
+ </op>
+ </uid>
+ </pkg>
+ <pkg n="com.android.nfc">
+ <uid n="1027">
+ <op n="40">
+ <st n="214748364801" t="1670291786330" d="22" />
+ </op>
+ </uid>
+ </pkg>
+</app-ops> \ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index 298dbf47d86d..302fa0f0c528 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -16,23 +16,32 @@
package com.android.server.appop;
+import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.res.AssetManager;
import android.os.Handler;
-import android.os.HandlerThread;
+import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -42,11 +51,20 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.ArrayUtils;
import com.android.modules.utils.TypedXmlPullParser;
+import com.android.server.LocalServices;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
import org.xmlpull.v1.XmlPullParser;
import java.io.File;
@@ -64,29 +82,39 @@ public class AppOpsUpgradeTest {
private static final String TAG = AppOpsUpgradeTest.class.getSimpleName();
private static final String APP_OPS_UNVERSIONED_ASSET_PATH =
"AppOpsUpgradeTest/appops-unversioned.xml";
+ private static final String APP_OPS_VERSION_1_ASSET_PATH =
+ "AppOpsUpgradeTest/appops-version-1.xml";
private static final String APP_OPS_FILENAME = "appops-test.xml";
private static final int NON_DEFAULT_OPS_IN_FILE = 4;
- private static final int CURRENT_VERSION = 1;
- private File mAppOpsFile;
- private Context mContext;
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+ private static final File sAppOpsFile = new File(sContext.getFilesDir(), APP_OPS_FILENAME);
+
+ private Context mTestContext;
+ private MockitoSession mMockitoSession;
+
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private UserManagerInternal mUserManagerInternal;
+ @Mock
+ private PermissionManagerServiceInternal mPermissionManagerInternal;
+ @Mock
private Handler mHandler;
- private void extractAppOpsFile() {
- mAppOpsFile.getParentFile().mkdirs();
- if (mAppOpsFile.exists()) {
- mAppOpsFile.delete();
- }
- try (FileOutputStream out = new FileOutputStream(mAppOpsFile);
- InputStream in = mContext.getAssets().open(APP_OPS_UNVERSIONED_ASSET_PATH,
- AssetManager.ACCESS_BUFFER)) {
+ private static void extractAppOpsFile(String assetPath) {
+ sAppOpsFile.getParentFile().mkdirs();
+ try (FileOutputStream out = new FileOutputStream(sAppOpsFile);
+ InputStream in = sContext.getAssets().open(assetPath, AssetManager.ACCESS_BUFFER)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) >= 0) {
out.write(buffer, 0, bytesRead);
}
out.flush();
- Log.d(TAG, "Successfully copied xml to " + mAppOpsFile.getAbsolutePath());
+ Log.d(TAG, "Successfully copied xml to " + sAppOpsFile.getAbsolutePath());
} catch (IOException exc) {
Log.e(TAG, "Exception while copying appops xml", exc);
fail();
@@ -98,7 +126,7 @@ public class AppOpsUpgradeTest {
int numberOfNonDefaultOps = 0;
final int defaultModeOp1 = AppOpsManager.opToDefaultMode(op1);
final int defaultModeOp2 = AppOpsManager.opToDefaultMode(op2);
- for(int i = 0; i < uidStates.size(); i++) {
+ for (int i = 0; i < uidStates.size(); i++) {
final AppOpsServiceImpl.UidState uidState = uidStates.valueAt(i);
SparseIntArray opModes = uidState.getNonDefaultUidModes();
if (opModes != null) {
@@ -132,41 +160,191 @@ public class AppOpsUpgradeTest {
@Before
public void setUp() {
- mContext = InstrumentationRegistry.getTargetContext();
- mAppOpsFile = new File(mContext.getFilesDir(), APP_OPS_FILENAME);
- extractAppOpsFile();
- HandlerThread handlerThread = new HandlerThread(TAG);
- handlerThread.start();
- mHandler = new Handler(handlerThread.getLooper());
+ if (sAppOpsFile.exists()) {
+ sAppOpsFile.delete();
+ }
+
+ mMockitoSession = mockitoSession()
+ .initMocks(this)
+ .spyStatic(LocalServices.class)
+ .mockStatic(SystemServerInitThreadPool.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ doReturn(mPermissionManagerInternal).when(
+ () -> LocalServices.getService(PermissionManagerServiceInternal.class));
+ doReturn(mUserManagerInternal).when(
+ () -> LocalServices.getService(UserManagerInternal.class));
+ doReturn(mPackageManagerInternal).when(
+ () -> LocalServices.getService(PackageManagerInternal.class));
+
+ mTestContext = spy(sContext);
+
+ // Pretend everybody has all permissions
+ doNothing().when(mTestContext).enforcePermission(anyString(), anyInt(), anyInt(),
+ nullable(String.class));
+
+ doReturn(mPackageManager).when(mTestContext).getPackageManager();
+
+ // Stub out package calls to disable AppOpsService#updatePermissionRevokedCompat
+ doReturn(null).when(mPackageManager).getPackagesForUid(anyInt());
+ }
+
+ @After
+ public void tearDown() {
+ mMockitoSession.finishMocking();
}
@Test
- public void testUpgradeFromNoVersion() throws Exception {
- AppOpsDataParser parser = new AppOpsDataParser(mAppOpsFile);
+ public void upgradeRunAnyInBackground() {
+ extractAppOpsFile(APP_OPS_UNVERSIONED_ASSET_PATH);
+
+ AppOpsServiceImpl testService = new AppOpsServiceImpl(sAppOpsFile, mHandler, mTestContext);
+
+ testService.upgradeRunAnyInBackgroundLocked();
+ assertSameModes(testService.mUidStates, AppOpsManager.OP_RUN_IN_BACKGROUND,
+ AppOpsManager.OP_RUN_ANY_IN_BACKGROUND);
+ }
+
+ private static int getModeInFile(int uid) {
+ switch (uid) {
+ case 10198:
+ return 0;
+ case 10200:
+ return 1;
+ case 1110200:
+ case 10267:
+ case 1110181:
+ return 2;
+ default:
+ return AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM);
+ }
+ }
+
+ @Test
+ public void upgradeScheduleExactAlarm() {
+ extractAppOpsFile(APP_OPS_VERSION_1_ASSET_PATH);
+
+ String[] packageNames = {"p1", "package2", "pkg3", "package.4", "pkg-5", "pkg.6"};
+ int[] appIds = {10267, 10181, 10198, 10199, 10200, 4213};
+ int[] userIds = {0, 10, 11};
+
+ doReturn(userIds).when(mUserManagerInternal).getUserIds();
+
+ doReturn(packageNames).when(mPermissionManagerInternal).getAppOpPermissionPackages(
+ AppOpsManager.opToPermission(OP_SCHEDULE_EXACT_ALARM));
+
+ doAnswer(invocation -> {
+ String pkg = invocation.getArgument(0);
+ int index = ArrayUtils.indexOf(packageNames, pkg);
+ if (index < 0) {
+ return index;
+ }
+ int userId = invocation.getArgument(2);
+ return UserHandle.getUid(userId, appIds[index]);
+ }).when(mPackageManagerInternal).getPackageUid(anyString(), anyLong(), anyInt());
+
+ AppOpsServiceImpl testService = new AppOpsServiceImpl(sAppOpsFile, mHandler, mTestContext);
+
+ testService.upgradeScheduleExactAlarmLocked();
+
+ for (int userId : userIds) {
+ for (int appId : appIds) {
+ final int uid = UserHandle.getUid(userId, appId);
+ final int previousMode = getModeInFile(uid);
+
+ final int expectedMode;
+ if (previousMode == AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM)) {
+ expectedMode = AppOpsManager.MODE_ALLOWED;
+ } else {
+ expectedMode = previousMode;
+ }
+ final AppOpsServiceImpl.UidState uidState = testService.mUidStates.get(uid);
+ assertEquals(expectedMode, uidState.getUidMode(OP_SCHEDULE_EXACT_ALARM));
+ }
+ }
+
+ // These uids don't even declare the permission. So should stay as default / empty.
+ int[] unrelatedUidsInFile = {10225, 10178};
+
+ for (int uid : unrelatedUidsInFile) {
+ final AppOpsServiceImpl.UidState uidState = testService.mUidStates.get(uid);
+ assertEquals(AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM),
+ uidState.getUidMode(OP_SCHEDULE_EXACT_ALARM));
+ }
+ }
+
+ @Test
+ public void upgradeFromNoFile() {
+ assertFalse(sAppOpsFile.exists());
+
+ AppOpsServiceImpl testService = spy(
+ new AppOpsServiceImpl(sAppOpsFile, mHandler, mTestContext));
+
+ doNothing().when(testService).upgradeRunAnyInBackgroundLocked();
+ doNothing().when(testService).upgradeScheduleExactAlarmLocked();
+
+ // trigger upgrade
+ testService.systemReady();
+
+ verify(testService, never()).upgradeRunAnyInBackgroundLocked();
+ verify(testService, never()).upgradeScheduleExactAlarmLocked();
+
+ testService.writeState();
+
+ assertTrue(sAppOpsFile.exists());
+
+ AppOpsDataParser parser = new AppOpsDataParser(sAppOpsFile);
+ assertTrue(parser.parse());
+ assertEquals(AppOpsServiceImpl.CURRENT_VERSION, parser.mVersion);
+ }
+
+ @Test
+ public void upgradeFromNoVersion() {
+ extractAppOpsFile(APP_OPS_UNVERSIONED_ASSET_PATH);
+ AppOpsDataParser parser = new AppOpsDataParser(sAppOpsFile);
assertTrue(parser.parse());
assertEquals(AppOpsDataParser.NO_VERSION, parser.mVersion);
- // Use mock context and package manager to fake permision package manager calls.
- Context testContext = spy(mContext);
+ AppOpsServiceImpl testService = spy(
+ new AppOpsServiceImpl(sAppOpsFile, mHandler, mTestContext));
- // Pretent everybody has all permissions
- doNothing().when(testContext).enforcePermission(anyString(), anyInt(), anyInt(),
- nullable(String.class));
+ doNothing().when(testService).upgradeRunAnyInBackgroundLocked();
+ doNothing().when(testService).upgradeScheduleExactAlarmLocked();
- PackageManager testPM = mock(PackageManager.class);
- when(testContext.getPackageManager()).thenReturn(testPM);
+ // trigger upgrade
+ testService.systemReady();
- // Stub out package calls to disable AppOpsService#updatePermissionRevokedCompat
- when(testPM.getPackagesForUid(anyInt())).thenReturn(null);
+ verify(testService).upgradeRunAnyInBackgroundLocked();
+ verify(testService).upgradeScheduleExactAlarmLocked();
+
+ testService.writeState();
+ assertTrue(parser.parse());
+ assertEquals(AppOpsServiceImpl.CURRENT_VERSION, parser.mVersion);
+ }
+
+ @Test
+ public void upgradeFromVersion1() {
+ extractAppOpsFile(APP_OPS_VERSION_1_ASSET_PATH);
+ AppOpsDataParser parser = new AppOpsDataParser(sAppOpsFile);
+ assertTrue(parser.parse());
+ assertEquals(1, parser.mVersion);
AppOpsServiceImpl testService = spy(
- new AppOpsServiceImpl(mAppOpsFile, mHandler, testContext)); // trigger upgrade
- assertSameModes(testService.mUidStates, AppOpsManager.OP_RUN_IN_BACKGROUND,
- AppOpsManager.OP_RUN_ANY_IN_BACKGROUND);
- mHandler.removeCallbacks(testService.mWriteRunner);
+ new AppOpsServiceImpl(sAppOpsFile, mHandler, mTestContext));
+
+ doNothing().when(testService).upgradeRunAnyInBackgroundLocked();
+ doNothing().when(testService).upgradeScheduleExactAlarmLocked();
+
+ // trigger upgrade
+ testService.systemReady();
+
+ verify(testService, never()).upgradeRunAnyInBackgroundLocked();
+ verify(testService).upgradeScheduleExactAlarmLocked();
+
testService.writeState();
assertTrue(parser.parse());
- assertEquals(CURRENT_VERSION, parser.mVersion);
+ assertEquals(AppOpsServiceImpl.CURRENT_VERSION, parser.mVersion);
}
/**
@@ -174,7 +352,7 @@ public class AppOpsUpgradeTest {
* Other fields may be added as and when required for testing.
*/
private static final class AppOpsDataParser {
- static final int NO_VERSION = -1;
+ static final int NO_VERSION = -123;
int mVersion;
private File mFile;