Change exact alarm permission to be denied by default

The permission SCHEDULE_EXACT_ALARM will change from being allowed by
default to denied by default for apps targeting T.
However, to reduce user impact, already installed apps will continue
having the permission if they did so before this change.

To achieve this, app-ops will modify MODE_DEFAULT to MODE_ALLOWED for
installed apps that have requested the permission in the same change.
Since this upgrade step requires appops service to talk to other system
services, moving the upgrade to happen on systemReady.

Test: atest FrameworksMockingServicesTests:AppOpsUpgradeTest
Test: atest FrameworksMockingServicesTests:AlarmManagerServiceTest
Test: atest CtsAlarmManagerTestCases

Bug: 226439802
Change-Id: I9c964f8957c6a91deca5035d27d21aa7746d2318
This commit is contained in:
Suprabh Shukla
2022-12-01 16:44:21 -08:00
parent 6d43f3321e
commit 220589b382
4 changed files with 1075 additions and 55 deletions

View File

@@ -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

View File

@@ -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();

View File

@@ -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>

View File

@@ -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);
// Pretent everybody has all permissions
doNothing().when(testContext).enforcePermission(anyString(), anyInt(), anyInt(),
nullable(String.class));
PackageManager testPM = mock(PackageManager.class);
when(testContext.getPackageManager()).thenReturn(testPM);
// Stub out package calls to disable AppOpsService#updatePermissionRevokedCompat
when(testPM.getPackagesForUid(anyInt())).thenReturn(null);
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).upgradeRunAnyInBackgroundLocked();
verify(testService).upgradeScheduleExactAlarmLocked();
testService.writeState();
assertTrue(parser.parse());
assertEquals(CURRENT_VERSION, parser.mVersion);
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(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(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;