Play charging feedback on a background thread
MediaPlayer can block for a while sometimes, so we don't want to block the main thread on this. Fixes: 225070450 Test: atest NotifierTest Test: manually plug/unplug device Change-Id: Ic792e35118109aa3357376b5950d56e12c1d51bb
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user