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

Commit 51802b36 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
(cherry picked from commit 79a0faf8)
parent cb0b5e20
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) {