Merge "Play charging feedback on a background thread" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-04-13 22:55:36 +00:00
committed by Android (Google) Code Review
5 changed files with 89 additions and 24 deletions

View File

@@ -64,6 +64,8 @@ import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Sends broadcasts about important power state changes.
@@ -133,6 +135,7 @@ public class Notifier {
private final DisplayManagerInternal mDisplayManagerInternal;
private final NotifierHandler mHandler;
private final Executor mBackgroundExecutor;
private final Intent mScreenOnIntent;
private final Intent mScreenOffIntent;
@@ -169,9 +172,12 @@ public class Notifier {
// True if a user activity message should be sent.
private boolean mUserActivityPending;
private final AtomicBoolean mIsPlayingChargingStartedFeedback = new AtomicBoolean(false);
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
Executor backgroundExecutor) {
mContext = context;
mBatteryStats = batteryStats;
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -188,6 +194,7 @@ public class Notifier {
mVibrator = mContext.getSystemService(Vibrator.class);
mHandler = new NotifierHandler(looper);
mBackgroundExecutor = backgroundExecutor;
mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
mScreenOnIntent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND
@@ -824,25 +831,36 @@ public class Notifier {
return;
}
// vibrate
final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
if (vibrate) {
mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
if (!mIsPlayingChargingStartedFeedback.compareAndSet(false, true)) {
// there's already a charging started feedback Runnable scheduled to run on the
// background thread, so let's not execute another
return;
}
// play sound
final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND
: Settings.Global.CHARGING_STARTED_SOUND);
final Uri soundUri = Uri.parse("file://" + soundPath);
if (soundUri != null) {
final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
if (sfx != null) {
sfx.setStreamType(AudioManager.STREAM_SYSTEM);
sfx.play();
// vibrate & play sound on a background thread
mBackgroundExecutor.execute(() -> {
// vibrate
final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
if (vibrate) {
mVibrator.vibrate(CHARGING_VIBRATION_EFFECT,
HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
}
}
// play sound
final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND
: Settings.Global.CHARGING_STARTED_SOUND);
final Uri soundUri = Uri.parse("file://" + soundPath);
if (soundUri != null) {
final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
if (sfx != null) {
sfx.setStreamType(AudioManager.STREAM_SYSTEM);
sfx.play();
}
}
mIsPlayingChargingStartedFeedback.set(false);
});
}
private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {

View File

@@ -140,6 +140,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* The power manager service is responsible for coordinating power management
@@ -905,10 +906,11 @@ public final class PowerManagerService extends SystemService
static class Injector {
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
Executor backgroundExecutor) {
return new Notifier(
looper, context, batteryStats, suspendBlocker, policy, faceDownDetector,
screenUndimDetector);
screenUndimDetector, backgroundExecutor);
}
SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
@@ -1227,7 +1229,8 @@ public final class PowerManagerService extends SystemService
mBatteryStats = BatteryStatsService.getService();
mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
mPolicy, mFaceDownDetector, mScreenUndimDetector);
mPolicy, mFaceDownDetector, mScreenUndimDetector,
BackgroundThread.getExecutor());
mPowerGroups.append(Display.DEFAULT_DISPLAY_GROUP,
new PowerGroup(WAKEFULNESS_AWAKE, mPowerGroupWakefulnessChangeListener,

View File

@@ -67,6 +67,8 @@ import com.android.server.power.batterysaver.BatterySaverStateMachine;
import com.android.server.power.batterysaver.BatterySavingStats;
import com.android.server.testutils.OffsettableClock;
import java.util.concurrent.Executor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -164,7 +166,8 @@ public class PowerManagerServiceMockingTest {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
Executor executor) {
return mNotifierMock;
}

View File

@@ -16,6 +16,8 @@
package com.android.server.power;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -56,6 +58,8 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.Executor;
/**
* Tests for {@link com.android.server.power.Notifier}
*/
@@ -79,6 +83,7 @@ public class NotifierTest {
private Context mContextSpy;
private Resources mResourcesSpy;
private TestLooper mTestLooper = new TestLooper();
private FakeExecutor mTestExecutor = new FakeExecutor();
private Notifier mNotifier;
@Before
@@ -107,6 +112,7 @@ public class NotifierTest {
// WHEN wired charging starts
mNotifier.onWiredChargingStarted(USER_ID);
mTestLooper.dispatchAll();
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device vibrates once
verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class));
@@ -122,6 +128,7 @@ public class NotifierTest {
// WHEN wired charging starts
mNotifier.onWiredChargingStarted(USER_ID);
mTestLooper.dispatchAll();
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
@@ -137,6 +144,7 @@ public class NotifierTest {
// WHEN wireless charging starts
mNotifier.onWirelessChargingStarted(5, USER_ID);
mTestLooper.dispatchAll();
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device vibrates once
verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class));
@@ -152,6 +160,7 @@ public class NotifierTest {
// WHEN wireless charging starts
mNotifier.onWirelessChargingStarted(5, USER_ID);
mTestLooper.dispatchAll();
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
@@ -170,6 +179,7 @@ public class NotifierTest {
// WHEN wired charging starts
mNotifier.onWiredChargingStarted(USER_ID);
mTestLooper.dispatchAll();
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
@@ -186,6 +196,7 @@ public class NotifierTest {
// WHEN wireless charging starts
mNotifier.onWirelessChargingStarted(5, USER_ID);
mTestLooper.dispatchAll();
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the charging animation is triggered
verify(mStatusBarManagerInternal, times(1)).showChargingAnimation(5);
@@ -202,6 +213,7 @@ public class NotifierTest {
// WHEN wireless charging starts
mNotifier.onWirelessChargingStarted(5, USER_ID);
mTestLooper.dispatchAll();
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the charging animation never gets called
verify(mStatusBarManagerInternal, never()).showChargingAnimation(anyInt());
@@ -211,7 +223,8 @@ public class NotifierTest {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
Executor backgroundExecutor) {
return mNotifierMock;
}
@@ -300,6 +313,32 @@ public class NotifierTest {
mInjector.createSuspendBlocker(mService, "testBlocker"),
null,
null,
null);
null,
mTestExecutor);
}
private static class FakeExecutor implements Executor {
private Runnable mLastCommand;
@Override
public void execute(Runnable command) {
assertNull(mLastCommand);
assertNotNull(command);
mLastCommand = command;
}
public Runnable getAndResetLastCommand() {
Runnable toReturn = mLastCommand;
mLastCommand = null;
return toReturn;
}
public void simulateAsyncExecutionOfLastCommand() {
Runnable toRun = getAndResetLastCommand();
if (toRun != null) {
toRun.run();
}
}
}
}

View File

@@ -109,6 +109,7 @@ import org.mockito.stubbing.Answer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -220,7 +221,8 @@ public class PowerManagerServiceTest {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
Executor executor) {
return mNotifierMock;
}