Merge "Break VolumeDialogControllerImpl' dependency on StatusBar" into sc-dev

This commit is contained in:
Dave Mankoff
2021-05-26 14:59:51 +00:00
committed by Android (Google) Code Review
8 changed files with 199 additions and 125 deletions

View File

@@ -236,7 +236,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
Log.i(TAG, "ScreenDecorations is disabled"); Log.i(TAG, "ScreenDecorations is disabled");
return; return;
} }
mHandler = mThreadFactory.builderHandlerOnNewThread("ScreenDecorations"); mHandler = mThreadFactory.buildHandlerOnNewThread("ScreenDecorations");
mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler); mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler);
mExecutor.execute(this::startOnScreenDecorationsThread); mExecutor.execute(this::startOnScreenDecorationsThread);
mDotViewController.setUiExecutor(mExecutor); mDotViewController.setUiExecutor(mExecutor);

View File

@@ -46,6 +46,7 @@ import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager; import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.IAudioService;
import android.media.MediaRouter2Manager; import android.media.MediaRouter2Manager;
import android.media.session.MediaSessionManager; import android.media.session.MediaSessionManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
@@ -77,6 +78,8 @@ import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.PackageManagerWrapper; import com.android.systemui.shared.system.PackageManagerWrapper;
import java.util.Optional;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
@@ -165,6 +168,13 @@ public class FrameworkServicesModule {
return ActivityTaskManager.getService(); return ActivityTaskManager.getService();
} }
@Provides
@Singleton
static IAudioService provideIAudioService() {
return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
}
@Provides @Provides
@Singleton @Singleton
static IBatteryStats provideIBatteryStats() { static IBatteryStats provideIBatteryStats() {
@@ -360,6 +370,12 @@ public class FrameworkServicesModule {
return context.getSystemService(Vibrator.class); return context.getSystemService(Vibrator.class);
} }
@Provides
@Singleton
static Optional<Vibrator> provideOptionalVibrator(Context context) {
return Optional.ofNullable(context.getSystemService(Vibrator.class));
}
@Provides @Provides
@Singleton @Singleton
static ViewConfiguration provideViewConfiguration(Context context) { static ViewConfiguration provideViewConfiguration(Context context) {

View File

@@ -23,7 +23,6 @@ import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.Dependency;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController;
@@ -63,6 +62,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
private final SecurityController mSecurityController; private final SecurityController mSecurityController;
private final Handler mHandler = Handler.getMain(); private final Handler mHandler = Handler.getMain();
private final CarrierConfigTracker mCarrierConfigTracker; private final CarrierConfigTracker mCarrierConfigTracker;
private final TunerService mTunerService;
private boolean mHideAirplane; private boolean mHideAirplane;
private boolean mHideMobile; private boolean mHideMobile;
@@ -83,9 +83,16 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
@Inject @Inject
public StatusBarSignalPolicy(Context context, StatusBarIconController iconController, public StatusBarSignalPolicy(Context context, StatusBarIconController iconController,
CarrierConfigTracker carrierConfigTracker) { CarrierConfigTracker carrierConfigTracker, NetworkController networkController,
SecurityController securityController, TunerService tunerService) {
mContext = context; mContext = context;
mIconController = iconController;
mCarrierConfigTracker = carrierConfigTracker;
mNetworkController = networkController;
mSecurityController = securityController;
mTunerService = tunerService;
mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile);
mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi);
@@ -96,18 +103,14 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
mContext.getString(com.android.internal.R.string.status_bar_call_strength); mContext.getString(com.android.internal.R.string.status_bar_call_strength);
mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
mIconController = iconController;
mCarrierConfigTracker = carrierConfigTracker;
mNetworkController = Dependency.get(NetworkController.class);
mSecurityController = Dependency.get(SecurityController.class);
Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST); tunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
mNetworkController.addCallback(this); mNetworkController.addCallback(this);
mSecurityController.addCallback(this); mSecurityController.addCallback(this);
} }
public void destroy() { public void destroy() {
Dependency.get(TunerService.class).removeTunable(this); mTunerService.removeTunable(this);
mNetworkController.removeCallback(this); mNetworkController.removeCallback(this);
mSecurityController.removeCallback(this); mSecurityController.removeCallback(this);
} }

View File

@@ -29,13 +29,21 @@ import java.util.concurrent.Executor;
* threads; there are no singletons here. Use responsibly. * threads; there are no singletons here. Use responsibly.
*/ */
public interface ThreadFactory { public interface ThreadFactory {
/**
* Returns a {@link Looper} running on a named thread.
*
* The thread is implicitly started and may be left running indefinitely, depending on the
* implementation. Assume this is the case and use responsibly.
*/
Looper buildLooperOnNewThread(String threadName);
/** /**
* Returns a {@link Handler} running on a named thread. * Returns a {@link Handler} running on a named thread.
* *
* The thread is implicitly started and may be left running indefinitely, depending on the * The thread is implicitly started and may be left running indefinitely, depending on the
* implementation. Assume this is the case and use responsibly. * implementation. Assume this is the case and use responsibly.
*/ */
Handler builderHandlerOnNewThread(String threadName); Handler buildHandlerOnNewThread(String threadName);
/** /**
* Return an {@link java.util.concurrent.Executor} running on a named thread. * Return an {@link java.util.concurrent.Executor} running on a named thread.

View File

@@ -29,10 +29,15 @@ class ThreadFactoryImpl implements ThreadFactory {
ThreadFactoryImpl() {} ThreadFactoryImpl() {}
@Override @Override
public Handler builderHandlerOnNewThread(String threadName) { public Looper buildLooperOnNewThread(String threadName) {
HandlerThread handlerThread = new HandlerThread(threadName); HandlerThread handlerThread = new HandlerThread(threadName);
handlerThread.start(); handlerThread.start();
return new Handler(handlerThread.getLooper()); return handlerThread.getLooper();
}
@Override
public Handler buildHandlerOnNewThread(String threadName) {
return new Handler(buildLooperOnNewThread(threadName));
} }
@Override @Override

View File

@@ -38,11 +38,9 @@ import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession.Token; import android.media.session.MediaSession.Token;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.VibrationEffect; import android.os.VibrationEffect;
import android.os.Vibrator; import android.os.Vibrator;
@@ -66,9 +64,9 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.RingerModeLiveData; import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.concurrency.ThreadFactory;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
@@ -80,8 +78,6 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.Lazy;
/** /**
* Source of truth for all state / events related to the volume dialog. No presentation. * Source of truth for all state / events related to the volume dialog. No presentation.
* *
@@ -118,12 +114,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call); STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call);
} }
private final HandlerThread mWorkerThread;
private final W mWorker; private final W mWorker;
private final Context mContext; private final Context mContext;
private final Looper mWorkerLooper;
private final PackageManager mPackageManager;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private AudioManager mAudio; private AudioManager mAudio;
private IAudioService mAudioService; private IAudioService mAudioService;
private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
private final NotificationManager mNoMan; private final NotificationManager mNoMan;
private final SettingObserver mObserver; private final SettingObserver mObserver;
private final Receiver mReceiver = new Receiver(); private final Receiver mReceiver = new Receiver();
@@ -132,13 +129,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
protected C mCallbacks = new C(); protected C mCallbacks = new C();
private final State mState = new State(); private final State mState = new State();
protected final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks(); protected final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
private final Vibrator mVibrator; private final Optional<Vibrator> mVibrator;
private final boolean mHasVibrator; private final boolean mHasVibrator;
private boolean mShowA11yStream; private boolean mShowA11yStream;
private boolean mShowVolumeDialog; private boolean mShowVolumeDialog;
private boolean mShowSafetyWarning; private boolean mShowSafetyWarning;
private long mLastToggledRingerOn; private long mLastToggledRingerOn;
private final NotificationManager mNotificationManager; private boolean mDeviceInteractive;
private boolean mDestroyed; private boolean mDestroyed;
private VolumePolicy mVolumePolicy; private VolumePolicy mVolumePolicy;
@@ -149,26 +146,42 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
protected final VC mVolumeController = new VC(); protected final VC mVolumeController = new VC();
protected final BroadcastDispatcher mBroadcastDispatcher; protected final BroadcastDispatcher mBroadcastDispatcher;
@Inject private final WakefulnessLifecycle.Observer mWakefullnessLifecycleObserver =
public VolumeDialogControllerImpl(Context context, BroadcastDispatcher broadcastDispatcher, new WakefulnessLifecycle.Observer() {
Optional<Lazy<StatusBar>> statusBarOptionalLazy, RingerModeTracker ringerModeTracker) { @Override
mContext = context.getApplicationContext(); public void onStartedWakingUp() {
// TODO(b/150663459): remove this TV workaround once StatusBar is "unbound" on TVs mDeviceInteractive = true;
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
mStatusBarOptionalLazy = Optional.empty();
} else {
mStatusBarOptionalLazy = statusBarOptionalLazy;
} }
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE); @Override
public void onFinishedGoingToSleep() {
mDeviceInteractive = false;
}
};
@Inject
public VolumeDialogControllerImpl(
Context context,
BroadcastDispatcher broadcastDispatcher,
RingerModeTracker ringerModeTracker,
ThreadFactory theadFactory,
AudioManager audioManager,
NotificationManager notificationManager,
Optional<Vibrator> optionalVibrator,
IAudioService iAudioService,
AccessibilityManager accessibilityManager,
PackageManager packageManager,
WakefulnessLifecycle wakefulnessLifecycle) {
mContext = context.getApplicationContext();
mPackageManager = packageManager;
mWakefulnessLifecycle = wakefulnessLifecycle;
Events.writeEvent(Events.EVENT_COLLECTION_STARTED); Events.writeEvent(Events.EVENT_COLLECTION_STARTED);
mWorkerThread = new HandlerThread(VolumeDialogControllerImpl.class.getSimpleName()); mWorkerLooper = theadFactory.buildLooperOnNewThread(
mWorkerThread.start(); VolumeDialogControllerImpl.class.getSimpleName());
mWorker = new W(mWorkerThread.getLooper()); mWorker = new W(mWorkerLooper);
mMediaSessions = createMediaSessions(mContext, mWorkerThread.getLooper(), mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW);
mMediaSessionsCallbacksW); mAudio = audioManager;
mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); mNoMan = notificationManager;
mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mObserver = new SettingObserver(mWorker); mObserver = new SettingObserver(mWorker);
mRingerModeObservers = new RingerModeObservers( mRingerModeObservers = new RingerModeObservers(
(RingerModeLiveData) ringerModeTracker.getRingerMode(), (RingerModeLiveData) ringerModeTracker.getRingerMode(),
@@ -178,16 +191,17 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mBroadcastDispatcher = broadcastDispatcher; mBroadcastDispatcher = broadcastDispatcher;
mObserver.init(); mObserver.init();
mReceiver.init(); mReceiver.init();
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); mVibrator = optionalVibrator;
mHasVibrator = mVibrator != null && mVibrator.hasVibrator(); mHasVibrator = mVibrator.isPresent() && mVibrator.get().hasVibrator();
mAudioService = IAudioService.Stub.asInterface( mAudioService = iAudioService;
ServiceManager.getService(Context.AUDIO_SERVICE));
boolean accessibilityVolumeStreamActive = context.getSystemService( boolean accessibilityVolumeStreamActive = accessibilityManager
AccessibilityManager.class).isAccessibilityVolumeStreamActive(); .isAccessibilityVolumeStreamActive();
mVolumeController.setA11yMode(accessibilityVolumeStreamActive ? mVolumeController.setA11yMode(accessibilityVolumeStreamActive ?
VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME : VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME); VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
mWakefulnessLifecycle.addObserver(mWakefullnessLifecycleObserver);
} }
public AudioManager getAudioManager() { public AudioManager getAudioManager() {
@@ -203,7 +217,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mAudio.setVolumeController(mVolumeController); mAudio.setVolumeController(mVolumeController);
} catch (SecurityException e) { } catch (SecurityException e) {
Log.w(TAG, "Unable to set the volume controller", e); Log.w(TAG, "Unable to set the volume controller", e);
return;
} }
} }
@@ -249,18 +262,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
return new MediaSessions(context, looper, callbacks); return new MediaSessions(context, looper, callbacks);
} }
public void destroy() {
if (D.BUG) Log.d(TAG, "destroy");
if (mDestroyed) return;
mDestroyed = true;
Events.writeEvent(Events.EVENT_COLLECTION_STOPPED);
mMediaSessions.destroy();
mObserver.destroy();
mReceiver.destroy();
mRingerModeObservers.destroy();
mWorkerThread.quitSafely();
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:"); pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:");
pw.print(" mDestroyed: "); pw.println(mDestroyed); pw.print(" mDestroyed: "); pw.println(mDestroyed);
@@ -383,9 +384,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
} }
public void vibrate(VibrationEffect effect) { public void vibrate(VibrationEffect effect) {
if (mHasVibrator) { mVibrator.ifPresent(
mVibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES); vibrator -> vibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES));
}
} }
public boolean hasVibrator() { public boolean hasVibrator() {
@@ -437,9 +437,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
return; return;
} }
PackageManager packageManager = mContext.getPackageManager();
mCallbacks.onCaptionComponentStateChanged( mCallbacks.onCaptionComponentStateChanged(
packageManager.getComponentEnabledSetting(componentName) mPackageManager.getComponentEnabledSetting(componentName)
== PackageManager.COMPONENT_ENABLED_STATE_ENABLED, fromTooltip); == PackageManager.COMPONENT_ENABLED_STATE_ENABLED, fromTooltip);
} catch (Exception ex) { } catch (Exception ex) {
Log.e(TAG, Log.e(TAG,
@@ -466,17 +465,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
} }
private boolean shouldShowUI(int flags) { private boolean shouldShowUI(int flags) {
// if status bar isn't null, check if phone is in AOD, else check flags int wakefulness = mWakefulnessLifecycle.getWakefulness();
// since we could be using a different status bar return wakefulness != WakefulnessLifecycle.WAKEFULNESS_ASLEEP
return mStatusBarOptionalLazy.map(statusBarLazy -> { && wakefulness != WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
StatusBar statusBar = statusBarLazy.get(); && mDeviceInteractive && (flags & AudioManager.FLAG_SHOW_UI) != 0
return statusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_ASLEEP && mShowVolumeDialog;
&& statusBar.getWakefulnessState()
!= WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
&& statusBar.isDeviceInteractive() && (flags & AudioManager.FLAG_SHOW_UI) != 0
&& mShowVolumeDialog;
}).orElse(
mShowVolumeDialog && (flags & AudioManager.FLAG_SHOW_UI) != 0);
} }
boolean onVolumeChangedW(int stream, int flags) { boolean onVolumeChangedW(int stream, int flags) {
@@ -600,15 +593,15 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) { private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) {
if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false; if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
mState.effectsSuppressor = effectsSuppressor; mState.effectsSuppressor = effectsSuppressor;
mState.effectsSuppressorName = getApplicationName(mContext, mState.effectsSuppressor); mState.effectsSuppressorName =
getApplicationName(mPackageManager, mState.effectsSuppressor);
Events.writeEvent(Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor, Events.writeEvent(Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
mState.effectsSuppressorName); mState.effectsSuppressorName);
return true; return true;
} }
private static String getApplicationName(Context context, ComponentName component) { private static String getApplicationName(PackageManager pm, ComponentName component) {
if (component == null) return null; if (component == null) return null;
final PackageManager pm = context.getPackageManager();
final String pkg = component.getPackageName(); final String pkg = component.getPackageName();
try { try {
final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0); final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
@@ -630,8 +623,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
} }
private boolean updateZenConfig() { private boolean updateZenConfig() {
final NotificationManager.Policy policy = final NotificationManager.Policy policy = mNoMan.getConsolidatedNotificationPolicy();
mNotificationManager.getConsolidatedNotificationPolicy();
boolean disallowAlarms = (policy.priorityCategories & NotificationManager.Policy boolean disallowAlarms = (policy.priorityCategories & NotificationManager.Policy
.PRIORITY_CATEGORY_ALARMS) == 0; .PRIORITY_CATEGORY_ALARMS) == 0;
boolean disallowMedia = (policy.priorityCategories & NotificationManager.Policy boolean disallowMedia = (policy.priorityCategories & NotificationManager.Policy

View File

@@ -27,6 +27,7 @@ import java.util.concurrent.Executor;
public class FakeThreadFactory implements ThreadFactory { public class FakeThreadFactory implements ThreadFactory {
private final FakeExecutor mFakeExecutor; private final FakeExecutor mFakeExecutor;
private Handler mHandler; private Handler mHandler;
private Looper mLooper;
public FakeThreadFactory(FakeExecutor fakeExecutor) { public FakeThreadFactory(FakeExecutor fakeExecutor) {
mFakeExecutor = fakeExecutor; mFakeExecutor = fakeExecutor;
@@ -36,8 +37,17 @@ public class FakeThreadFactory implements ThreadFactory {
mHandler = handler; mHandler = handler;
} }
public void setLooper(Looper looper) {
mLooper = looper;
}
@Override @Override
public Handler builderHandlerOnNewThread(String threadName) { public Looper buildLooperOnNewThread(String threadName) {
return mLooper;
}
@Override
public Handler buildHandlerOnNewThread(String threadName) {
return mHandler; return mHandler;
} }

View File

@@ -23,29 +23,37 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.NotificationManager;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.IAudioService;
import android.media.session.MediaSession; import android.media.session.MediaSession;
import android.os.Handler; import android.os.Handler;
import android.os.Process; import android.os.Process;
import android.os.Vibrator;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper; import android.testing.TestableLooper;
import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.RingerModeLiveData; import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
@@ -58,7 +66,6 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
TestableVolumeDialogControllerImpl mVolumeController; TestableVolumeDialogControllerImpl mVolumeController;
VolumeDialogControllerImpl.C mCallback; VolumeDialogControllerImpl.C mCallback;
StatusBar mStatusBar;
@Mock @Mock
private BroadcastDispatcher mBroadcastDispatcher; private BroadcastDispatcher mBroadcastDispatcher;
@Mock @Mock
@@ -67,6 +74,23 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
private RingerModeLiveData mRingerModeLiveData; private RingerModeLiveData mRingerModeLiveData;
@Mock @Mock
private RingerModeLiveData mRingerModeInternalLiveData; private RingerModeLiveData mRingerModeInternalLiveData;
private final FakeThreadFactory mThreadFactory = new FakeThreadFactory(
new FakeExecutor(new FakeSystemClock()));
@Mock
private AudioManager mAudioManager;
@Mock
private NotificationManager mNotificationManager;
@Mock
private Vibrator mVibrator;
@Mock
private IAudioService mIAudioService;
@Mock
private AccessibilityManager mAccessibilityManager;
@Mock
private PackageManager mPackageManager;
@Mock
private WakefulnessLifecycle mWakefullnessLifcycle;
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
@@ -77,19 +101,15 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
// Initial non-set value // Initial non-set value
when(mRingerModeLiveData.getValue()).thenReturn(-1); when(mRingerModeLiveData.getValue()).thenReturn(-1);
when(mRingerModeInternalLiveData.getValue()).thenReturn(-1); when(mRingerModeInternalLiveData.getValue()).thenReturn(-1);
mCallback = mock(VolumeDialogControllerImpl.C.class); mCallback = mock(VolumeDialogControllerImpl.C.class);
mStatusBar = mock(StatusBar.class); mThreadFactory.setLooper(TestableLooper.get(this).getLooper());
mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar, mVolumeController = new TestableVolumeDialogControllerImpl(mContext,
mBroadcastDispatcher, mRingerModeTracker); mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager,
mNotificationManager, Optional.of(mVibrator), mIAudioService, mAccessibilityManager,
mPackageManager, mWakefullnessLifcycle, mCallback);
mVolumeController.setEnableDialogs(true, true); mVolumeController.setEnableDialogs(true, true);
} }
@After
public void tearDown() {
mVolumeController.destroy();
}
@Test @Test
public void testRegisteredWithDispatcher() { public void testRegisteredWithDispatcher() {
verify(mBroadcastDispatcher).registerReceiverWithHandler(any(BroadcastReceiver.class), verify(mBroadcastDispatcher).registerReceiverWithHandler(any(BroadcastReceiver.class),
@@ -99,44 +119,35 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
@Test @Test
public void testVolumeChangeW_deviceNotInteractiveAOD() { public void testVolumeChangeW_deviceNotInteractiveAOD() {
when(mStatusBar.isDeviceInteractive()).thenReturn(false); mVolumeController.setDeviceInteractive(false);
when(mStatusBar.getWakefulnessState()).thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE); when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_AWAKE);
mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
verify(mCallback, never()).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED); verify(mCallback, never()).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
} }
@Test @Test
public void testVolumeChangeW_deviceInteractive() { public void testVolumeChangeW_deviceInteractive() {
when(mStatusBar.isDeviceInteractive()).thenReturn(true); mVolumeController.setDeviceInteractive(true);
when(mStatusBar.getWakefulnessState()).thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE); when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_AWAKE);
mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED); verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
} }
@Test @Test
public void testVolumeChangeW_deviceInteractive_StartedSleeping() { public void testVolumeChangeW_deviceInteractive_StartedSleeping() {
when(mStatusBar.isDeviceInteractive()).thenReturn(true); mVolumeController.setDeviceInteractive(true);
when(mStatusBar.getWakefulnessState()).thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE); when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_AWAKE);
mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
when(mStatusBar.isDeviceInteractive()).thenReturn(false); mVolumeController.setDeviceInteractive(false);
when(mStatusBar.getWakefulnessState()).thenReturn( when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP); WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP);
mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED); verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
} }
@Test
public void testVolumeChangeW_nullStatusBar() {
VolumeDialogControllerImpl.C callback = mock(VolumeDialogControllerImpl.C.class);
TestableVolumeDialogControllerImpl
nullStatusBarTestableDialog =
new TestableVolumeDialogControllerImpl(
mContext, callback, null, mBroadcastDispatcher, mRingerModeTracker);
nullStatusBarTestableDialog.setEnableDialogs(true, true);
nullStatusBarTestableDialog.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
verify(callback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
}
@Test @Test
public void testOnRemoteVolumeChanged_newStream_noNullPointer() { public void testOnRemoteVolumeChanged_newStream_noNullPointer() {
MediaSession.Token token = new MediaSession.Token(Process.myUid(), null); MediaSession.Token token = new MediaSession.Token(Process.myUid(), null);
@@ -155,22 +166,51 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
verify(mRingerModeInternalLiveData).observeForever(any()); verify(mRingerModeInternalLiveData).observeForever(any());
} }
@Test
public void testRingerModeOnDestroy_observersRemoved() {
mVolumeController.destroy();
verify(mRingerModeLiveData).removeObserver(any());
verify(mRingerModeInternalLiveData).removeObserver(any());
}
static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl { static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl {
TestableVolumeDialogControllerImpl(Context context, C callback, StatusBar s, private final WakefulnessLifecycle.Observer mWakefullessLifecycleObserver;
BroadcastDispatcher broadcastDispatcher, RingerModeTracker ringerModeTracker) {
super( TestableVolumeDialogControllerImpl(
context, broadcastDispatcher, Context context,
s == null ? Optional.empty() : Optional.of(() -> s), ringerModeTracker); BroadcastDispatcher broadcastDispatcher,
RingerModeTracker ringerModeTracker,
ThreadFactory theadFactory,
AudioManager audioManager,
NotificationManager notificationManager,
Optional<Vibrator> optionalVibrator,
IAudioService iAudioService,
AccessibilityManager accessibilityManager,
PackageManager packageManager,
WakefulnessLifecycle wakefulnessLifecycle,
C callback) {
super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager,
notificationManager, optionalVibrator, iAudioService, accessibilityManager,
packageManager, wakefulnessLifecycle);
mCallbacks = callback; mCallbacks = callback;
ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor =
ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
verify(wakefulnessLifecycle).addObserver(observerCaptor.capture());
mWakefullessLifecycleObserver = observerCaptor.getValue();
}
public void setDeviceInteractive(boolean interactive) {
if (interactive) {
mWakefullessLifecycleObserver.onStartedWakingUp();
} else {
mWakefullessLifecycleObserver.onFinishedGoingToSleep();
}
} }
} }
// static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl {
// TestableVolumeDialogControllerImpl(Context context, C callback,
// BroadcastDispatcher broadcastDispatcher, RingerModeTracker ringerModeTracker,
// ThreadFactory threadFactory) {
// super(
// context, broadcastDispatcher,
// s == null ? Optional.empty() : Optional.of(() -> s), ringerModeTracker);
// mCallbacks = callback;
// }
// }
} }