Loading core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java +53 −34 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package android.hardware.devicestate; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; Loading @@ -40,7 +44,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.internal.util.ConcurrentUtils; import com.android.window.flags.Flags; import org.junit.Before; Loading @@ -52,6 +55,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; Loading Loading @@ -103,7 +107,7 @@ public final class DeviceStateManagerGlobalTest { permissionEnforcer, true /* simulatePostCallback */); final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); final DeviceStateCallback callback = mock(DeviceStateCallback.class); dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); dsmGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback, never()).onDeviceStateChanged(any()); Loading @@ -120,49 +124,68 @@ public final class DeviceStateManagerGlobalTest { final IDeviceStateManager service = new TestDeviceStateManagerService(permissionEnforcer); final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); final DeviceStateCallback callback = mock(DeviceStateCallback.class); dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); dsmGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE)); } @Test public void registerCallback() { final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); public void registerCallback_usesExecutorForCallbacks() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); final Executor executor = mock(Executor.class); doAnswer(invocation -> { Runnable runnable = (Runnable) invocation.getArguments()[0]; runnable.run(); return null; }).when(executor).execute(any(Runnable.class)); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, executor); mService.setBaseState(OTHER_DEVICE_STATE); mService.setSupportedStates(List.of(OTHER_DEVICE_STATE)); // Verify initial callbacks verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); verify(callback1).onDeviceStateChanged(eq(mService.getMergedState())); verify(callback2).onDeviceStateChanged(eq(mService.getMergedState())); // Verify that the given executor is used for both initial and subsequent callbacks. verify(executor, times(4)).execute(any(Runnable.class)); } reset(callback1); reset(callback2); @Test public void registerCallback_supportedStatesChanged() { final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR); // Change the supported states and verify callback // Change the supported states and verify callback. mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE)); verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE)); } reset(callback1); reset(callback2); @Test public void registerCallback_baseStateChanged() { final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR); mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE)); // Change the base state and verify callback // Change the base state and verify callback. mService.setBaseState(OTHER_DEVICE_STATE); verify(callback1).onDeviceStateChanged(eq(mService.getMergedState())); verify(callback2).onDeviceStateChanged(eq(mService.getMergedState())); } reset(callback1); reset(callback2); // Change the requested state and verify callback @Test public void registerCallback_requestedStateChanged() { final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); final DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR); // Change the requested state and verify callback. mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); verify(callback1).onDeviceStateChanged(eq(mService.getMergedState())); Loading @@ -173,8 +196,7 @@ public final class DeviceStateManagerGlobalTest { public void unregisterCallback() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); // Verify initial callbacks verify(callback).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); Loading @@ -191,8 +213,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitRequest() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); Loading @@ -212,8 +233,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitBaseStateOverrideRequest() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); Loading @@ -234,8 +254,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitBaseAndEmulatedStateOverride() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); Loading Loading @@ -275,7 +294,7 @@ public final class DeviceStateManagerGlobalTest { final DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.requestState(request, ConcurrentUtils.DIRECT_EXECUTOR /* executor */, DIRECT_EXECUTOR /* executor */, callback /* callback */); verify(callback).onRequestActivated(eq(request)); Loading libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java +23 −13 Original line number Diff line number Diff line Loading @@ -26,10 +26,12 @@ import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.hardware.devicestate.DeviceStateUtil; import android.os.Looper; import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; import androidx.annotation.BinderThread; import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; Loading @@ -43,6 +45,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.Executor; import java.util.function.Consumer; /** Loading @@ -68,39 +71,49 @@ public final class DeviceStateManagerFoldingFeatureProducer */ private DeviceState mCurrentDeviceState = INVALID_DEVICE_STATE; @NonNull private final Context mContext; @NonNull private final RawFoldingFeatureProducer mRawFoldSupplier; @NonNull private final DeviceStateMapper mDeviceStateMapper; @VisibleForTesting final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() { // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the getData() // implementation. See https://errorprone.info/bugpattern/GuardedBy for limitations. @SuppressWarnings("GuardedBy") @MainThread private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() { @BinderThread // Subsequent callback after registered. @MainThread // Initial callback if registration is on the main thread. @Override public void onDeviceStateChanged(@NonNull DeviceState state) { mCurrentDeviceState = state; mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer.this ::notifyFoldingFeatureChangeLocked); final boolean isMainThread = Looper.myLooper() == Looper.getMainLooper(); final Executor executor = isMainThread ? Runnable::run : mContext.getMainExecutor(); executor.execute(() -> { DeviceStateManagerFoldingFeatureProducer.this.onDeviceStateChanged(state); }); } }; public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context, @NonNull RawFoldingFeatureProducer rawFoldSupplier, @NonNull DeviceStateManager deviceStateManager) { mContext = context; mRawFoldSupplier = rawFoldSupplier; mDeviceStateMapper = new DeviceStateMapper(context, deviceStateManager.getSupportedDeviceStates()); if (!mDeviceStateMapper.isDeviceStateToPostureMapEmpty()) { Objects.requireNonNull(deviceStateManager) .registerCallback(context.getMainExecutor(), mDeviceStateCallback); .registerCallback(Runnable::run, mDeviceStateCallback); } } @MainThread @VisibleForTesting void onDeviceStateChanged(@NonNull DeviceState state) { mCurrentDeviceState = state; mRawFoldSupplier.getData(this::notifyFoldingFeatureChangeLocked); } /** * Add a callback to mCallbacks if there is no device state. This callback will be run * once a device state is set. Otherwise,run the callback immediately. Loading @@ -118,9 +131,6 @@ public final class DeviceStateManagerFoldingFeatureProducer } } // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the implementation of // addDataChangedCallback(). See https://errorprone.info/bugpattern/GuardedBy for limitations. @SuppressWarnings("GuardedBy") @Override protected void onListenersChanged() { super.onListenersChanged(); Loading libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt +34 −7 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.res.Resources import android.hardware.devicestate.DeviceState import android.hardware.devicestate.DeviceStateManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.window.common.layout.CommonFoldingFeature import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_FLAT import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_HALF_OPENED Loading @@ -33,13 +34,14 @@ import androidx.window.common.layout.DisplayFoldFeatureCommon.DISPLAY_FOLD_FEATU import com.android.internal.R import com.google.common.truth.Truth.assertThat import java.util.Optional import java.util.concurrent.Executor import java.util.function.Consumer import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.stub Loading Loading @@ -69,14 +71,39 @@ class DeviceStateManagerFoldingFeatureProducerTest { } @Test fun testRegisterCallback_usesMainExecutor() { fun testRegisterCallback_initialCallbackOnMainThread_executesDirectly() { DeviceStateManagerFoldingFeatureProducer( mMockContext, mRawFoldSupplier, mMockDeviceStateManager, ) val callbackCaptor = argumentCaptor<DeviceStateManager.DeviceStateCallback>() verify(mMockDeviceStateManager).registerCallback(any(), callbackCaptor.capture()) verify(mMockDeviceStateManager).registerCallback(eq(mMockContext.mainExecutor), any()) InstrumentationRegistry.getInstrumentation().runOnMainSync { callbackCaptor.firstValue.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) } verify(mMockContext, never()).getMainExecutor() } @Test fun testRegisterCallback_subsequentCallbacks_postsToMainThread() { val mockMainExecutor = mock<Executor>() mMockContext.stub { on { getMainExecutor() } doReturn mockMainExecutor } DeviceStateManagerFoldingFeatureProducer( mMockContext, mRawFoldSupplier, mMockDeviceStateManager, ) val callbackCaptor = argumentCaptor<DeviceStateManager.DeviceStateCallback>() verify(mMockDeviceStateManager).registerCallback(any(), callbackCaptor.capture()) callbackCaptor.firstValue.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) verify(mockMainExecutor).execute(any()) } @Test Loading @@ -86,7 +113,7 @@ class DeviceStateManagerFoldingFeatureProducerTest { mRawFoldSupplier, mMockDeviceStateManager, ) ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) val currentData = ffp.getCurrentData() Loading Loading @@ -209,7 +236,7 @@ class DeviceStateManagerFoldingFeatureProducerTest { mRawFoldSupplier, mMockDeviceStateManager, ) ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) val storeFeaturesConsumer = mock<Consumer<List<CommonFoldingFeature>>>() ffp.getData(storeFeaturesConsumer) Loading @@ -229,8 +256,8 @@ class DeviceStateManagerFoldingFeatureProducerTest { ffp.getData(storeFeaturesConsumer) verify(storeFeaturesConsumer, never()).accept(any()) ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_OPENED) ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) ffp.onDeviceStateChanged(DEVICE_STATE_OPENED) verify(storeFeaturesConsumer).accept(HALF_OPENED_FOLDING_FEATURES) } Loading Loading
core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java +53 −34 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package android.hardware.devicestate; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; Loading @@ -40,7 +44,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.internal.util.ConcurrentUtils; import com.android.window.flags.Flags; import org.junit.Before; Loading @@ -52,6 +55,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; Loading Loading @@ -103,7 +107,7 @@ public final class DeviceStateManagerGlobalTest { permissionEnforcer, true /* simulatePostCallback */); final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); final DeviceStateCallback callback = mock(DeviceStateCallback.class); dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); dsmGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback, never()).onDeviceStateChanged(any()); Loading @@ -120,49 +124,68 @@ public final class DeviceStateManagerGlobalTest { final IDeviceStateManager service = new TestDeviceStateManagerService(permissionEnforcer); final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); final DeviceStateCallback callback = mock(DeviceStateCallback.class); dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); dsmGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE)); } @Test public void registerCallback() { final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); public void registerCallback_usesExecutorForCallbacks() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); final Executor executor = mock(Executor.class); doAnswer(invocation -> { Runnable runnable = (Runnable) invocation.getArguments()[0]; runnable.run(); return null; }).when(executor).execute(any(Runnable.class)); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, executor); mService.setBaseState(OTHER_DEVICE_STATE); mService.setSupportedStates(List.of(OTHER_DEVICE_STATE)); // Verify initial callbacks verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); verify(callback1).onDeviceStateChanged(eq(mService.getMergedState())); verify(callback2).onDeviceStateChanged(eq(mService.getMergedState())); // Verify that the given executor is used for both initial and subsequent callbacks. verify(executor, times(4)).execute(any(Runnable.class)); } reset(callback1); reset(callback2); @Test public void registerCallback_supportedStatesChanged() { final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR); // Change the supported states and verify callback // Change the supported states and verify callback. mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE)); verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE)); } reset(callback1); reset(callback2); @Test public void registerCallback_baseStateChanged() { final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR); mService.setSupportedStates(List.of(DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE)); // Change the base state and verify callback // Change the base state and verify callback. mService.setBaseState(OTHER_DEVICE_STATE); verify(callback1).onDeviceStateChanged(eq(mService.getMergedState())); verify(callback2).onDeviceStateChanged(eq(mService.getMergedState())); } reset(callback1); reset(callback2); // Change the requested state and verify callback @Test public void registerCallback_requestedStateChanged() { final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); final DeviceStateCallback callback2 = mock(DeviceStateCallback.class); final DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, DIRECT_EXECUTOR); // Change the requested state and verify callback. mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); verify(callback1).onDeviceStateChanged(eq(mService.getMergedState())); Loading @@ -173,8 +196,7 @@ public final class DeviceStateManagerGlobalTest { public void unregisterCallback() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); // Verify initial callbacks verify(callback).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates())); Loading @@ -191,8 +213,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitRequest() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); Loading @@ -212,8 +233,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitBaseStateOverrideRequest() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); Loading @@ -234,8 +254,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitBaseAndEmulatedStateOverride() { final DeviceStateCallback callback = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(mService.getBaseState())); reset(callback); Loading Loading @@ -275,7 +294,7 @@ public final class DeviceStateManagerGlobalTest { final DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build(); mDeviceStateManagerGlobal.requestState(request, ConcurrentUtils.DIRECT_EXECUTOR /* executor */, DIRECT_EXECUTOR /* executor */, callback /* callback */); verify(callback).onRequestActivated(eq(request)); Loading
libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java +23 −13 Original line number Diff line number Diff line Loading @@ -26,10 +26,12 @@ import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.hardware.devicestate.DeviceStateUtil; import android.os.Looper; import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; import androidx.annotation.BinderThread; import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; Loading @@ -43,6 +45,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.Executor; import java.util.function.Consumer; /** Loading @@ -68,39 +71,49 @@ public final class DeviceStateManagerFoldingFeatureProducer */ private DeviceState mCurrentDeviceState = INVALID_DEVICE_STATE; @NonNull private final Context mContext; @NonNull private final RawFoldingFeatureProducer mRawFoldSupplier; @NonNull private final DeviceStateMapper mDeviceStateMapper; @VisibleForTesting final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() { // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the getData() // implementation. See https://errorprone.info/bugpattern/GuardedBy for limitations. @SuppressWarnings("GuardedBy") @MainThread private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() { @BinderThread // Subsequent callback after registered. @MainThread // Initial callback if registration is on the main thread. @Override public void onDeviceStateChanged(@NonNull DeviceState state) { mCurrentDeviceState = state; mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer.this ::notifyFoldingFeatureChangeLocked); final boolean isMainThread = Looper.myLooper() == Looper.getMainLooper(); final Executor executor = isMainThread ? Runnable::run : mContext.getMainExecutor(); executor.execute(() -> { DeviceStateManagerFoldingFeatureProducer.this.onDeviceStateChanged(state); }); } }; public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context, @NonNull RawFoldingFeatureProducer rawFoldSupplier, @NonNull DeviceStateManager deviceStateManager) { mContext = context; mRawFoldSupplier = rawFoldSupplier; mDeviceStateMapper = new DeviceStateMapper(context, deviceStateManager.getSupportedDeviceStates()); if (!mDeviceStateMapper.isDeviceStateToPostureMapEmpty()) { Objects.requireNonNull(deviceStateManager) .registerCallback(context.getMainExecutor(), mDeviceStateCallback); .registerCallback(Runnable::run, mDeviceStateCallback); } } @MainThread @VisibleForTesting void onDeviceStateChanged(@NonNull DeviceState state) { mCurrentDeviceState = state; mRawFoldSupplier.getData(this::notifyFoldingFeatureChangeLocked); } /** * Add a callback to mCallbacks if there is no device state. This callback will be run * once a device state is set. Otherwise,run the callback immediately. Loading @@ -118,9 +131,6 @@ public final class DeviceStateManagerFoldingFeatureProducer } } // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the implementation of // addDataChangedCallback(). See https://errorprone.info/bugpattern/GuardedBy for limitations. @SuppressWarnings("GuardedBy") @Override protected void onListenersChanged() { super.onListenersChanged(); Loading
libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt +34 −7 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.res.Resources import android.hardware.devicestate.DeviceState import android.hardware.devicestate.DeviceStateManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.window.common.layout.CommonFoldingFeature import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_FLAT import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_HALF_OPENED Loading @@ -33,13 +34,14 @@ import androidx.window.common.layout.DisplayFoldFeatureCommon.DISPLAY_FOLD_FEATU import com.android.internal.R import com.google.common.truth.Truth.assertThat import java.util.Optional import java.util.concurrent.Executor import java.util.function.Consumer import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.stub Loading Loading @@ -69,14 +71,39 @@ class DeviceStateManagerFoldingFeatureProducerTest { } @Test fun testRegisterCallback_usesMainExecutor() { fun testRegisterCallback_initialCallbackOnMainThread_executesDirectly() { DeviceStateManagerFoldingFeatureProducer( mMockContext, mRawFoldSupplier, mMockDeviceStateManager, ) val callbackCaptor = argumentCaptor<DeviceStateManager.DeviceStateCallback>() verify(mMockDeviceStateManager).registerCallback(any(), callbackCaptor.capture()) verify(mMockDeviceStateManager).registerCallback(eq(mMockContext.mainExecutor), any()) InstrumentationRegistry.getInstrumentation().runOnMainSync { callbackCaptor.firstValue.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) } verify(mMockContext, never()).getMainExecutor() } @Test fun testRegisterCallback_subsequentCallbacks_postsToMainThread() { val mockMainExecutor = mock<Executor>() mMockContext.stub { on { getMainExecutor() } doReturn mockMainExecutor } DeviceStateManagerFoldingFeatureProducer( mMockContext, mRawFoldSupplier, mMockDeviceStateManager, ) val callbackCaptor = argumentCaptor<DeviceStateManager.DeviceStateCallback>() verify(mMockDeviceStateManager).registerCallback(any(), callbackCaptor.capture()) callbackCaptor.firstValue.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) verify(mockMainExecutor).execute(any()) } @Test Loading @@ -86,7 +113,7 @@ class DeviceStateManagerFoldingFeatureProducerTest { mRawFoldSupplier, mMockDeviceStateManager, ) ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) val currentData = ffp.getCurrentData() Loading Loading @@ -209,7 +236,7 @@ class DeviceStateManagerFoldingFeatureProducerTest { mRawFoldSupplier, mMockDeviceStateManager, ) ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) val storeFeaturesConsumer = mock<Consumer<List<CommonFoldingFeature>>>() ffp.getData(storeFeaturesConsumer) Loading @@ -229,8 +256,8 @@ class DeviceStateManagerFoldingFeatureProducerTest { ffp.getData(storeFeaturesConsumer) verify(storeFeaturesConsumer, never()).accept(any()) ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_OPENED) ffp.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED) ffp.onDeviceStateChanged(DEVICE_STATE_OPENED) verify(storeFeaturesConsumer).accept(HALF_OPENED_FOLDING_FEATURES) } Loading