Merge "Adding a normal permission for exact alarms"
This commit is contained in:
committed by
Android (Google) Code Review
commit
007b90ff84
@@ -72,6 +72,7 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.PermissionChecker;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.database.ContentObserver;
|
||||
@@ -1839,6 +1840,9 @@ public class AlarmManagerService extends SystemService {
|
||||
if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) {
|
||||
return false;
|
||||
}
|
||||
if (hasUseExactAlarmPermission(a.packageName, a.uid)) {
|
||||
return false;
|
||||
}
|
||||
return !isExemptFromExactAlarmPermission(a.uid);
|
||||
};
|
||||
removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
|
||||
@@ -1900,6 +1904,9 @@ public class AlarmManagerService extends SystemService {
|
||||
|| !isExactAlarmChangeEnabled(packageName, userId)) {
|
||||
return;
|
||||
}
|
||||
if (hasUseExactAlarmPermission(packageName, uid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean requested = mExactAlarmCandidates.contains(
|
||||
UserHandle.getAppId(uid));
|
||||
@@ -2534,6 +2541,8 @@ public class AlarmManagerService extends SystemService {
|
||||
|
||||
private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed,
|
||||
int appOpMode) {
|
||||
// This does not account for the state of the USE_EXACT_ALARM permission.
|
||||
// The caller should do that separately.
|
||||
if (!requested) {
|
||||
return false;
|
||||
}
|
||||
@@ -2543,7 +2552,16 @@ public class AlarmManagerService extends SystemService {
|
||||
return appOpMode == AppOpsManager.MODE_ALLOWED;
|
||||
}
|
||||
|
||||
boolean hasUseExactAlarmPermission(String packageName, int uid) {
|
||||
return PermissionChecker.checkPermissionForPreflight(getContext(),
|
||||
Manifest.permission.USE_EXACT_ALARM, PermissionChecker.PID_UNKNOWN, uid,
|
||||
packageName) == PermissionChecker.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
boolean hasScheduleExactAlarmInternal(String packageName, int uid) {
|
||||
if (hasUseExactAlarmPermission(packageName, uid)) {
|
||||
return true;
|
||||
}
|
||||
// Not using getScheduleExactAlarmState as this can avoid some calls to AppOpsService.
|
||||
// Not using #mLastOpScheduleExactAlarm as it may contain stale values.
|
||||
// No locking needed as all internal containers being queried are immutable.
|
||||
@@ -3759,6 +3777,9 @@ public class AlarmManagerService extends SystemService {
|
||||
if (!isExactAlarmChangeEnabled(changedPackage, userId)) {
|
||||
continue;
|
||||
}
|
||||
if (hasUseExactAlarmPermission(changedPackage, uid)) {
|
||||
continue;
|
||||
}
|
||||
final int appOpMode;
|
||||
synchronized (mLock) {
|
||||
appOpMode = mLastOpScheduleExactAlarm.get(uid,
|
||||
@@ -3778,7 +3799,8 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
if (added) {
|
||||
synchronized (mLock) {
|
||||
removeExactAlarmsOnPermissionRevokedLocked(uid, changedPackage);
|
||||
removeExactAlarmsOnPermissionRevokedLocked(uid,
|
||||
changedPackage, /*killUid = */ true);
|
||||
}
|
||||
} else {
|
||||
sendScheduleExactAlarmPermissionStateChangedBroadcast(changedPackage, userId);
|
||||
@@ -3794,7 +3816,7 @@ public class AlarmManagerService extends SystemService {
|
||||
* This is not expected to get called frequently.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
|
||||
void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName, boolean killUid) {
|
||||
if (isExemptFromExactAlarmPermission(uid)
|
||||
|| !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) {
|
||||
return;
|
||||
@@ -3805,7 +3827,7 @@ public class AlarmManagerService extends SystemService {
|
||||
&& a.windowLength == 0);
|
||||
removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
|
||||
|
||||
if (mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) {
|
||||
if (killUid && mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) {
|
||||
PermissionManagerService.killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
|
||||
"schedule_exact_alarm revoked");
|
||||
}
|
||||
@@ -4617,6 +4639,7 @@ public class AlarmManagerService extends SystemService {
|
||||
public static final int EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED = 10;
|
||||
public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11;
|
||||
public static final int TARE_AFFORDABILITY_CHANGED = 12;
|
||||
public static final int CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE = 13;
|
||||
|
||||
AlarmHandler() {
|
||||
super(Looper.myLooper());
|
||||
@@ -4715,10 +4738,11 @@ public class AlarmManagerService extends SystemService {
|
||||
break;
|
||||
|
||||
case REMOVE_EXACT_ALARMS:
|
||||
final int uid = msg.arg1;
|
||||
final String packageName = (String) msg.obj;
|
||||
int uid = msg.arg1;
|
||||
String packageName = (String) msg.obj;
|
||||
synchronized (mLock) {
|
||||
removeExactAlarmsOnPermissionRevokedLocked(uid, packageName);
|
||||
removeExactAlarmsOnPermissionRevokedLocked(uid, packageName, /*killUid = */
|
||||
true);
|
||||
}
|
||||
break;
|
||||
case EXACT_ALARM_DENY_LIST_PACKAGES_ADDED:
|
||||
@@ -4730,6 +4754,16 @@ public class AlarmManagerService extends SystemService {
|
||||
case REFRESH_EXACT_ALARM_CANDIDATES:
|
||||
refreshExactAlarmCandidates();
|
||||
break;
|
||||
case CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE:
|
||||
packageName = (String) msg.obj;
|
||||
uid = msg.arg1;
|
||||
if (!hasScheduleExactAlarmInternal(packageName, uid)) {
|
||||
synchronized (mLock) {
|
||||
removeExactAlarmsOnPermissionRevokedLocked(uid,
|
||||
packageName, /*killUid = */false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// nope, just ignore it
|
||||
break;
|
||||
@@ -4914,6 +4948,12 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
break;
|
||||
case Intent.ACTION_PACKAGE_ADDED:
|
||||
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
|
||||
final String packageUpdated = intent.getData().getSchemeSpecificPart();
|
||||
mHandler.obtainMessage(
|
||||
AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE, uid, -1,
|
||||
packageUpdated).sendToTarget();
|
||||
}
|
||||
mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,6 +194,7 @@ package android {
|
||||
field public static final String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
|
||||
field public static final String UPDATE_PACKAGES_WITHOUT_USER_ACTION = "android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION";
|
||||
field public static final String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC";
|
||||
field public static final String USE_EXACT_ALARM = "android.permission.USE_EXACT_ALARM";
|
||||
field @Deprecated public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
|
||||
field public static final String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT";
|
||||
field public static final String USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER = "android.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER";
|
||||
|
||||
@@ -4403,6 +4403,16 @@
|
||||
<permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
|
||||
android:protectionLevel="normal|appop"/>
|
||||
|
||||
<!-- Allows apps to use exact alarms just like with SCHEDULE_EXACT_ALARM but without needing
|
||||
to request this permission from the user.
|
||||
<p><b>This is only for apps that rely on exact alarms for their core functionality.</b>
|
||||
App stores may enforce policies to audit and review the use of this permission. Any app that
|
||||
requests this but is found to not require exact alarms for its primary function may be
|
||||
removed from the app store.
|
||||
-->
|
||||
<permission android:name="android.permission.USE_EXACT_ALARM"
|
||||
android:protectionLevel="normal"/>
|
||||
|
||||
<!-- Allows an application to query tablet mode state and monitor changes
|
||||
in it.
|
||||
<p>Not for use by third-party applications.
|
||||
|
||||
@@ -108,6 +108,7 @@ import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.ActivityOptions;
|
||||
@@ -124,6 +125,7 @@ import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.PermissionChecker;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
@@ -389,6 +391,7 @@ public class AlarmManagerServiceTest {
|
||||
.mockStatic(LocalServices.class)
|
||||
.spyStatic(Looper.class)
|
||||
.mockStatic(MetricsHelper.class)
|
||||
.mockStatic(PermissionChecker.class)
|
||||
.mockStatic(PermissionManagerService.class)
|
||||
.mockStatic(ServiceManager.class)
|
||||
.mockStatic(Settings.Global.class)
|
||||
@@ -445,6 +448,10 @@ public class AlarmManagerServiceTest {
|
||||
doReturn(true)
|
||||
.when(() -> DateFormat.is24HourFormat(eq(mMockContext), anyInt()));
|
||||
|
||||
doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
|
||||
() -> PermissionChecker.checkPermissionForPreflight(any(),
|
||||
eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), anyInt(), anyString()));
|
||||
|
||||
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
|
||||
|
||||
registerAppIds(new String[]{TEST_CALLING_PACKAGE},
|
||||
@@ -2158,6 +2165,7 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
@Test
|
||||
public void canScheduleExactAlarmsBinderCall() throws RemoteException {
|
||||
// Policy permission is denied in setUp().
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
// No permission, no exemption.
|
||||
@@ -2168,6 +2176,14 @@ public class AlarmManagerServiceTest {
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
|
||||
assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
|
||||
|
||||
// Policy permission only, no exemption.
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
|
||||
doReturn(PermissionChecker.PERMISSION_GRANTED).when(
|
||||
() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
|
||||
eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), eq(TEST_CALLING_UID),
|
||||
eq(TEST_CALLING_PACKAGE)));
|
||||
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
|
||||
|
||||
// Permission, no exemption.
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT);
|
||||
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
|
||||
@@ -2699,7 +2715,8 @@ public class AlarmManagerServiceTest {
|
||||
mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), false);
|
||||
|
||||
// No permission revoked.
|
||||
verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(anyInt(), anyString());
|
||||
verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(anyInt(), anyString(),
|
||||
anyBoolean());
|
||||
|
||||
// Permission got granted only for (appId1, userId2).
|
||||
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
@@ -2754,14 +2771,14 @@ public class AlarmManagerServiceTest {
|
||||
|
||||
// Permission got revoked only for (appId1, userId2)
|
||||
verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(
|
||||
eq(UserHandle.getUid(userId1, appId1)), eq(packages[0]));
|
||||
eq(UserHandle.getUid(userId1, appId1)), eq(packages[0]), eq(true));
|
||||
verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(
|
||||
eq(UserHandle.getUid(userId1, appId2)), eq(packages[1]));
|
||||
eq(UserHandle.getUid(userId1, appId2)), eq(packages[1]), eq(true));
|
||||
verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(
|
||||
eq(UserHandle.getUid(userId2, appId2)), eq(packages[1]));
|
||||
eq(UserHandle.getUid(userId2, appId2)), eq(packages[1]), eq(true));
|
||||
|
||||
verify(mService).removeExactAlarmsOnPermissionRevokedLocked(
|
||||
eq(UserHandle.getUid(userId2, appId1)), eq(packages[0]));
|
||||
eq(UserHandle.getUid(userId2, appId1)), eq(packages[0]), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2774,7 +2791,7 @@ public class AlarmManagerServiceTest {
|
||||
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
|
||||
assertAndHandleMessageSync(REMOVE_EXACT_ALARMS);
|
||||
verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID,
|
||||
TEST_CALLING_PACKAGE);
|
||||
TEST_CALLING_PACKAGE, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2859,7 +2876,8 @@ public class AlarmManagerServiceTest {
|
||||
null);
|
||||
assertEquals(6, mService.mAlarmStore.size());
|
||||
|
||||
mService.removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE);
|
||||
mService.removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE,
|
||||
true);
|
||||
|
||||
final ArrayList<Alarm> remaining = mService.mAlarmStore.asList();
|
||||
assertEquals(3, remaining.size());
|
||||
@@ -3080,7 +3098,7 @@ public class AlarmManagerServiceTest {
|
||||
SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters);
|
||||
|
||||
final Intent packageAdded = new Intent(Intent.ACTION_PACKAGE_ADDED)
|
||||
.setPackage(TEST_CALLING_PACKAGE)
|
||||
.setData(Uri.fromParts("package", TEST_CALLING_PACKAGE, null))
|
||||
.putExtra(Intent.EXTRA_REPLACING, true);
|
||||
mPackageChangesReceiver.onReceive(mMockContext, packageAdded);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user