Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 79a0faf8 authored by Kweku Adams's avatar Kweku Adams
Browse files

Avoid lock inversion.

The PowerManager lock (which is used in the Battery Saver code) sits
low in the lock hierarchy. NotificationManagerService is higher, so
nothing should call into NotificationManagerService with the
PowerManager lock held. This change calls into NMS on a background
thread so that the call occurs without the PM lock held.

Also fixing the cancel notification bug. We create the notifications
using notifyAsUser(... UserHandle.ALL). The cancel() call wasn't
properly dismissing the notification. Switching to
cancelAsUser(UserHandle.ALL) fixes the issue.

Bug: 145886051
Test: atest BatterySaverStateMachineTest
Test: Manually ensure notification disappears when BS turns on
Change-Id: I3315357e810d92938e4a34929235d233b07deebb
parent 4324208b
Loading
Loading
Loading
Loading
+40 −27
Original line number Diff line number Diff line
@@ -773,9 +773,9 @@ public class BatterySaverStateMachine {

        // Handle triggering the notification to show/hide when appropriate
        if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
            runOnBgThread(this::triggerDynamicModeNotification);
            triggerDynamicModeNotification();
        } else if (!enable) {
            runOnBgThread(this::hideDynamicModeNotification);
            hideDynamicModeNotification();
        }

        if (DEBUG) {
@@ -787,20 +787,28 @@ public class BatterySaverStateMachine {

    @VisibleForTesting
    void triggerDynamicModeNotification() {
        // The current lock is the PowerManager lock, which sits very low in the service lock
        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
        runOnBgThread(() -> {
            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
            ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID,
                    R.string.dynamic_mode_notification_channel_name);

            manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID,
                    buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID,
                        mContext.getResources().getString(R.string.dynamic_mode_notification_title),
                            mContext.getResources().getString(
                                    R.string.dynamic_mode_notification_title),
                            R.string.dynamic_mode_notification_summary,
                            Intent.ACTION_POWER_USAGE_SUMMARY),
                    UserHandle.ALL);
        });
    }

    @VisibleForTesting
    void triggerStickyDisabledNotification() {
        // The current lock is the PowerManager lock, which sits very low in the service lock
        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
        runOnBgThread(() -> {
            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
            ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID,
                    R.string.battery_saver_notification_channel_name);
@@ -814,6 +822,7 @@ public class BatterySaverStateMachine {
                            R.string.battery_saver_off_notification_summary,
                            Settings.ACTION_BATTERY_SAVER_SETTINGS),
                    UserHandle.ALL);
        });
    }

    private void ensureNotificationChannelExists(NotificationManager manager,
@@ -854,8 +863,12 @@ public class BatterySaverStateMachine {
    }

    private void hideNotification(int notificationId) {
        // The current lock is the PowerManager lock, which sits very low in the service lock
        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
        runOnBgThread(() -> {
            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
        manager.cancel(notificationId);
            manager.cancelAsUser(TAG, notificationId, UserHandle.ALL);
        });
    }

    private void setStickyActive(boolean active) {