Send a broadcast when an app is granted SCHEDULE_EXACT_ALARM
Bug: 187206399
Test: atest AlarmManagerServiceTest
Test: Manual test with a test app.
- Grant the permission to test app and make sure it can start FGS.
Log:
```
05-07 10:12:08.671 1000 1579 9475 I ActivityManager: Background started FGS: Allowed [callingPackage: com.google.omakoto.testapp; callingUid: 10294; uidState: RCVR; intent: Intent { act=fgs cmp=com.google.omakoto.testapp/.MyFgs }; code:(unknown:207); tempAllowListReason:<broadcast:1000:android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,reason:,reasonCode:(unknown:207),duration:10000,callingUid:1000>; targetSdkVersion:30; callerTargetSdkVersion:30; startForegroundCount:0]
```
- Grant the permission to a different app and make sure the test app
won't receive the broadcast.
Test: CTS -- Incoming
Change-Id: Iefe3e12dcf51318d8433532ba3048caa69b1edcd
This commit is contained in:
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.SystemService;
|
||||
import android.annotation.TestApi;
|
||||
@@ -138,6 +139,36 @@ public class AlarmManager {
|
||||
public static final String ACTION_NEXT_ALARM_CLOCK_CHANGED =
|
||||
"android.app.action.NEXT_ALARM_CLOCK_CHANGED";
|
||||
|
||||
/**
|
||||
* Broadcast Action: An app is granted the
|
||||
* {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM} permission.
|
||||
*
|
||||
* <p>When the user revokes the {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM}
|
||||
* permission, all alarms scheduled with
|
||||
* {@link #setExact}, {@link #setExactAndAllowWhileIdle} and
|
||||
* {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will be deleted.
|
||||
*
|
||||
* <p>When the user grants the {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM},
|
||||
* this broadcast will be sent. Applications can reschedule all the necessary alarms when
|
||||
* receiving it.
|
||||
*
|
||||
* <p><em>Note:</em>
|
||||
* Applications are still required to check {@link #canScheduleExactAlarms()}
|
||||
* before using the above APIs after receiving this broadcast,
|
||||
* because it's possible that the permission is already revoked again by the time
|
||||
* applications receive this broadcast.
|
||||
*
|
||||
* <p>This broadcast will be sent to both runtime receivers and manifest receivers.
|
||||
*
|
||||
* <p>This broadcast is sent as a foreground broadcast.
|
||||
* See {@link android.content.Intent#FLAG_RECEIVER_FOREGROUND}.
|
||||
*
|
||||
* <p>When an application receives this broadcast, it's allowed to start a foreground service.
|
||||
*/
|
||||
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
|
||||
public static final String ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED =
|
||||
"android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED";
|
||||
|
||||
/** @hide */
|
||||
@UnsupportedAppUsage
|
||||
public static final long WINDOW_EXACT = 0;
|
||||
|
||||
@@ -251,6 +251,12 @@ public class PowerExemptionManager {
|
||||
* @hide
|
||||
*/
|
||||
public static final int REASON_LOCALE_CHANGED = 206;
|
||||
/**
|
||||
* Broadcast
|
||||
* {@link android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED}
|
||||
* @hide
|
||||
*/
|
||||
public static final int REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED = 207;
|
||||
/* Reason code range 300-399 are reserved for other internal reasons */
|
||||
/**
|
||||
* Device idle system allow list, including EXCEPT-IDLE
|
||||
@@ -386,6 +392,7 @@ public class PowerExemptionManager {
|
||||
REASON_TIMEZONE_CHANGED,
|
||||
REASON_TIME_CHANGED,
|
||||
REASON_LOCALE_CHANGED,
|
||||
REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
|
||||
REASON_SYSTEM_ALLOW_LISTED,
|
||||
REASON_ALARM_MANAGER_ALARM_CLOCK,
|
||||
REASON_ALARM_MANAGER_WHILE_IDLE,
|
||||
@@ -664,6 +671,8 @@ public class PowerExemptionManager {
|
||||
return "TIME_CHANGED";
|
||||
case REASON_LOCALE_CHANGED:
|
||||
return "LOCALE_CHANGED";
|
||||
case REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
|
||||
return "REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED";
|
||||
case REASON_SYSTEM_ALLOW_LISTED:
|
||||
return "SYSTEM_ALLOW_LISTED";
|
||||
case REASON_ALARM_MANAGER_ALARM_CLOCK:
|
||||
|
||||
@@ -30,6 +30,7 @@ import static android.app.AlarmManager.INTERVAL_HOUR;
|
||||
import static android.app.AlarmManager.RTC;
|
||||
import static android.app.AlarmManager.RTC_WAKEUP;
|
||||
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
|
||||
import static android.os.PowerExemptionManager.REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED;
|
||||
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
|
||||
import static android.os.PowerWhitelistManager.REASON_ALARM_MANAGER_WHILE_IDLE;
|
||||
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
|
||||
@@ -1704,6 +1705,11 @@ public class AlarmManagerService extends SystemService {
|
||||
if (!hasScheduleExactAlarmInternal(packageName, uid)) {
|
||||
mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS,
|
||||
uid, 0, packageName).sendToTarget();
|
||||
} else {
|
||||
// TODO(b/187206399) Make sure this won't be sent, if the app
|
||||
// already had the appop previously.
|
||||
sendScheduleExactAlarmPermissionStateChangedBroadcast(
|
||||
packageName, UserHandle.getUserId(uid));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -4816,6 +4822,30 @@ public class AlarmManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send {@link AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED} to
|
||||
* the app that is just granted the permission.
|
||||
*/
|
||||
private void sendScheduleExactAlarmPermissionStateChangedBroadcast(
|
||||
String packageName, int userId) {
|
||||
final Intent i = new Intent(
|
||||
AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED);
|
||||
i.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
|
||||
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
|
||||
| Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
i.setPackage(packageName);
|
||||
|
||||
// We need to allow the app to start a foreground service.
|
||||
// This broadcast is very rare, so we do not cache the BroadcastOptions.
|
||||
final BroadcastOptions opts = BroadcastOptions.makeBasic();
|
||||
opts.setTemporaryAppAllowlist(
|
||||
mActivityManagerInternal.getBootTimeTempAllowListDuration(),
|
||||
TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
|
||||
REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED, "");
|
||||
getContext().sendBroadcastAsUser(i, UserHandle.of(userId), /*permission*/ null,
|
||||
opts.toBundle());
|
||||
}
|
||||
|
||||
private void decrementAlarmCount(int uid, int decrement) {
|
||||
int oldCount = 0;
|
||||
final int uidIndex = mAlarmsPerUid.indexOfKey(uid);
|
||||
|
||||
@@ -4404,6 +4404,7 @@ package android.app {
|
||||
method public void setWindow(int, long, long, android.app.PendingIntent);
|
||||
method public void setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
|
||||
field public static final String ACTION_NEXT_ALARM_CLOCK_CHANGED = "android.app.action.NEXT_ALARM_CLOCK_CHANGED";
|
||||
field public static final String ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED = "android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED";
|
||||
field public static final int ELAPSED_REALTIME = 3; // 0x3
|
||||
field public static final int ELAPSED_REALTIME_WAKEUP = 2; // 0x2
|
||||
field public static final long INTERVAL_DAY = 86400000L; // 0x5265c00L
|
||||
|
||||
@@ -696,6 +696,8 @@
|
||||
<protected-broadcast android:name="android.scheduling.action.REBOOT_READY" />
|
||||
<protected-broadcast android:name="android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED" />
|
||||
|
||||
<protected-broadcast android:name="android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED" />
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- RUNTIME PERMISSIONS -->
|
||||
<!-- ====================================================================== -->
|
||||
|
||||
@@ -2402,6 +2402,34 @@ public class AlarmManagerServiceTest {
|
||||
TEST_CALLING_PACKAGE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void opScheduleExactAlarmGranted() throws Exception {
|
||||
final long durationMs = 20000L;
|
||||
when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs);
|
||||
|
||||
mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED);
|
||||
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
|
||||
|
||||
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
|
||||
verify(mMockContext).sendBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.SYSTEM),
|
||||
isNull(), bundleCaptor.capture());
|
||||
|
||||
// Validate the intent.
|
||||
assertEquals(AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
|
||||
intentCaptor.getValue().getAction());
|
||||
assertEquals(TEST_CALLING_PACKAGE, intentCaptor.getValue().getPackage());
|
||||
|
||||
// Validate the options.
|
||||
final BroadcastOptions bOptions = new BroadcastOptions(bundleCaptor.getValue());
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
|
||||
bOptions.getTemporaryAppAllowlistType());
|
||||
assertEquals(durationMs, bOptions.getTemporaryAppAllowlistDuration());
|
||||
assertEquals(PowerExemptionManager.REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
|
||||
bOptions.getTemporaryAppAllowlistReasonCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeExactAlarmsOnPermissionRevoked() {
|
||||
doReturn(true).when(
|
||||
|
||||
Reference in New Issue
Block a user