diff options
| author | 2022-12-08 19:36:13 +0000 | |
|---|---|---|
| committer | 2022-12-08 19:36:13 +0000 | |
| commit | 2da4d090932a5f07a965c9ae7e4dac264a9a60f6 (patch) | |
| tree | dd36517b5c7a9ff702570b3ee94983316810c3ba | |
| parent | 748489854e2cc6b5477d9deb68c0b595e50fd3b4 (diff) | |
| parent | 220589b3824cb19d089c7fe0664da40df4bd3b76 (diff) | |
Merge "Change exact alarm permission to be denied by default"
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; |