diff options
12 files changed, 409 insertions, 134 deletions
diff --git a/data/keyboards/Vendor_054c_Product_05c4.idc b/data/keyboards/Vendor_054c_Product_05c4.idc index 2cb3f7b90fed..2da622745baf 100644 --- a/data/keyboards/Vendor_054c_Product_05c4.idc +++ b/data/keyboards/Vendor_054c_Product_05c4.idc @@ -13,9 +13,11 @@ # limitations under the License. # -# Sony DS4 motion sensor configuration file. +# Sony Playstation(R) DualShock 4 Controller # +## Motion sensor ## + # reporting mode 0 - continuous sensor.accelerometer.reportingMode = 0 # The delay between sensor events corresponding to the lowest frequency in microsecond @@ -33,3 +35,28 @@ sensor.gyroscope.maxDelay = 100000 sensor.gyroscope.minDelay = 5000 # The power in mA used by this sensor while in use sensor.gyroscope.power = 0.8 + +## Touchpad ## + +# After the DualShock 4 has been connected over Bluetooth for a minute or so, +# its reports start bunching up in time, meaning that we receive 2–4 reports +# within a millisecond followed by a >10ms wait until the next batch. +# +# This uneven timing causes the apparent speed of a finger (calculated using +# time deltas between received reports) to vary dramatically even if it's +# actually moving smoothly across the touchpad, triggering the touchpad stack's +# drumroll detection logic. For moving fingers, the drumroll detection logic +# splits the finger's single movement into many small movements of consecutive +# touches, which are then inhibited by the click wiggle filter. For tapping +# fingers, it prevents tapping to click because it thinks the finger's moving +# too fast. +# +# Since this touchpad doesn't seem to have to drumroll issues, we can safely +# disable drumroll detection. +gestureProp.Drumroll_Suppression_Enable = 0 + +# Because of the way this touchpad is positioned, touches around the edges are +# no more likely to be palms than ones in the middle, so remove the edge zones +# from the palm classifier to increase the usable area of the pad. +gestureProp.Palm_Edge_Zone_Width = 0 +gestureProp.Tap_Exclusion_Border_Width = 0 diff --git a/data/keyboards/Vendor_054c_Product_09cc.idc b/data/keyboards/Vendor_054c_Product_09cc.idc index 2cb3f7b90fed..2a1a4fc62b24 100644 --- a/data/keyboards/Vendor_054c_Product_09cc.idc +++ b/data/keyboards/Vendor_054c_Product_09cc.idc @@ -13,9 +13,11 @@ # limitations under the License. # -# Sony DS4 motion sensor configuration file. +# Sony Playstation(R) DualShock 4 Controller # +## Motion sensor ## + # reporting mode 0 - continuous sensor.accelerometer.reportingMode = 0 # The delay between sensor events corresponding to the lowest frequency in microsecond @@ -33,3 +35,28 @@ sensor.gyroscope.maxDelay = 100000 sensor.gyroscope.minDelay = 5000 # The power in mA used by this sensor while in use sensor.gyroscope.power = 0.8 + +## Touchpad ## + +# After the DualShock 4 has been connected over Bluetooth for a minute or so, +# its reports start bunching up in time, meaning that we receive 2–4 reports +# within a millisecond followed by a >10ms wait until the next batch. +# +# This uneven timing causes the apparent speed of a finger (calculated using +# time deltas between received reports) to vary dramatically even if it's +# actually moving smoothly across the touchpad, triggering the touchpad stack's +# drumroll detection logic. For moving fingers, the drumroll detection logic +# splits the finger's single movement into many small movements of consecutive +# touches, which are then inhibited by the click wiggle filter. For tapping +# fingers, it prevents tapping to click because it thinks the finger's moving +# too fast. +# +# Since this touchpad doesn't seem to have drumroll issues, we can safely +# disable drumroll detection. +gestureProp.Drumroll_Suppression_Enable = 0 + +# Because of the way this touchpad is positioned, touches around the edges are +# no more likely to be palms than ones in the middle, so remove the edge zones +# from the palm classifier to increase the usable area of the pad. +gestureProp.Palm_Edge_Zone_Width = 0 +gestureProp.Tap_Exclusion_Border_Width = 0 diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 8baaf2f155af..38ee6e2fb7de 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -45,9 +45,6 @@ <!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu --> <bool name="config_pipEnableResizeForMenu">true</bool> - <!-- PiP minimum size, which is a % based off the shorter side of display width and height --> - <fraction name="config_pipShortestEdgePercent">40%</fraction> - <!-- Time (duration in milliseconds) that the shell waits for an app to close the PiP by itself if a custom action is present before closing it. --> <integer name="config_pipForceCloseDelay">1000</integer> @@ -91,11 +88,45 @@ 16x16 </string> + <!-- Default percentages for the PIP size logic. + 1. Determine max widths + Subtract width of system UI and default padding from the shortest edge of the device. + This is the max width. + 2. Calculate Default and Mins + Default is config_pipSystemPreferredDefaultSizePercent of max-width/height. + Min is config_pipSystemPreferredMinimumSizePercent of it. --> + <item name="config_pipSystemPreferredDefaultSizePercent" format="float" type="dimen">0.6</item> + <item name="config_pipSystemPreferredMinimumSizePercent" format="float" type="dimen">0.5</item> + <!-- Default percentages for the PIP size logic when the Display is close to square. + This is used instead when the display is square-ish, like fold-ables when unfolded, + to make sure that default PiP does not cover the hinge (halfway of the display). + 0. Determine if the display is square-ish + If min(displayWidth, displayHeight) / max(displayWidth, displayHeight) is greater than + config_pipSquareDisplayThresholdForSystemPreferredSize, we use the percent for + square display listed below. + 1. Determine max widths + Subtract width of system UI and default padding from the shortest edge of the device. + This is the max width. + 2. Calculate Default and Mins + Default is config_pipSystemPreferredDefaultSizePercentForSquareDisplay of max-width/height. + Min is config_pipSystemPreferredMinimumSizePercentForSquareDisplay of it. --> + <item name="config_pipSquareDisplayThresholdForSystemPreferredSize" + format="float" type="dimen">0.95</item> + <item name="config_pipSystemPreferredDefaultSizePercentForSquareDisplay" + format="float" type="dimen">0.5</item> + <item name="config_pipSystemPreferredMinimumSizePercentForSquareDisplay" + format="float" type="dimen">0.4</item> + <!-- The percentage of the screen width to use for the default width or height of picture-in-picture windows. Regardless of the percent set here, calculated size will never - be smaller than @dimen/default_minimal_size_pip_resizable_task. --> + be smaller than @dimen/default_minimal_size_pip_resizable_task. + This is used in legacy spec, use config_pipSystemPreferredDefaultSizePercent instead. --> <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.23</item> + <!-- PiP minimum size, which is a % based off the shorter side of display width and height. + This is used in legacy spec, use config_pipSystemPreferredMinimumSizePercent instead. --> + <fraction name="config_pipShortestEdgePercent">40%</fraction> + <!-- The default aspect ratio for picture-in-picture windows. --> <item name="config_pictureInPictureDefaultAspectRatio" format="float" type="dimen"> 1.777778 diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt index 18c7bdd6d5ba..7eb0f267b312 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt @@ -18,7 +18,6 @@ package com.android.wm.shell.common.pip import android.content.Context import android.content.res.Resources -import android.os.SystemProperties import android.util.Size import com.android.wm.shell.R import java.io.PrintWriter @@ -36,30 +35,81 @@ class PhoneSizeSpecSource( private var mOverrideMinSize: Size? = null - /** Default and minimum percentages for the PIP size logic. */ - private val mDefaultSizePercent: Float - private val mMinimumSizePercent: Float + /** + * Default percentages for the PIP size logic. + * 1. Determine max widths + * Subtract width of system UI and default padding from the shortest edge of the device. + * This is the max width. + * 2. Calculate Default and Mins + * Default is mSystemPreferredDefaultSizePercent of max-width/height. + * Min is mSystemPreferredMinimumSizePercent of it. + * + * NOTE: Do not use this directly, use the mPreferredDefaultSizePercent getter instead. + */ + private var mSystemPreferredDefaultSizePercent = 0.6f + /** Minimum percentages for the PIP size logic. */ + private var mSystemPreferredMinimumSizePercent = 0.5f + + /** Threshold to categorize the Display as square, calculated as min(w, h) / max(w, h). */ + private var mSquareDisplayThresholdForSystemPreferredSize = 0.95f + /** + * Default percentages for the PIP size logic when the Display is square-ish. + * This is used instead when the display is square-ish, like fold-ables when unfolded, + * to make sure that default PiP does not cover the hinge (halfway of the display). + * 1. Determine max widths + * Subtract width of system UI and default padding from the shortest edge of the device. + * This is the max width. + * 2. Calculate Default and Mins + * Default is mSystemPreferredDefaultSizePercent of max-width/height. + * Min is mSystemPreferredMinimumSizePercent of it. + * + * NOTE: Do not use this directly, use the mPreferredDefaultSizePercent getter instead. + */ + private var mSystemPreferredDefaultSizePercentForSquareDisplay = 0.5f + /** Minimum percentages for the PIP size logic. */ + private var mSystemPreferredMinimumSizePercentForSquareDisplay = 0.4f + + private val mIsSquareDisplay + get() = minOf(pipDisplayLayoutState.displayLayout.width(), + pipDisplayLayoutState.displayLayout.height()).toFloat() / + maxOf(pipDisplayLayoutState.displayLayout.width(), + pipDisplayLayoutState.displayLayout.height()) > + mSquareDisplayThresholdForSystemPreferredSize + private val mPreferredDefaultSizePercent + get() = if (mIsSquareDisplay) mSystemPreferredDefaultSizePercentForSquareDisplay else + mSystemPreferredDefaultSizePercent + + private val mPreferredMinimumSizePercent + get() = if (mIsSquareDisplay) mSystemPreferredMinimumSizePercentForSquareDisplay else + mSystemPreferredMinimumSizePercent /** Aspect ratio that the PIP size spec logic optimizes for. */ private var mOptimizedAspectRatio = 0f init { - mDefaultSizePercent = SystemProperties - .get("com.android.wm.shell.pip.phone.def_percentage", "0.6").toFloat() - mMinimumSizePercent = SystemProperties - .get("com.android.wm.shell.pip.phone.min_percentage", "0.5").toFloat() - reloadResources() } private fun reloadResources() { - val res: Resources = context.getResources() + val res: Resources = context.resources mDefaultMinSize = res.getDimensionPixelSize( R.dimen.default_minimal_size_pip_resizable_task) mOverridableMinSize = res.getDimensionPixelSize( R.dimen.overridable_minimal_size_pip_resizable_task) + mSystemPreferredDefaultSizePercent = res.getFloat( + R.dimen.config_pipSystemPreferredDefaultSizePercent) + mSystemPreferredMinimumSizePercent = res.getFloat( + R.dimen.config_pipSystemPreferredMinimumSizePercent) + + mSquareDisplayThresholdForSystemPreferredSize = res.getFloat( + R.dimen.config_pipSquareDisplayThresholdForSystemPreferredSize) + mSystemPreferredDefaultSizePercentForSquareDisplay = res.getFloat( + R.dimen.config_pipSystemPreferredDefaultSizePercentForSquareDisplay) + mSystemPreferredMinimumSizePercentForSquareDisplay = res.getFloat( + R.dimen.config_pipSystemPreferredMinimumSizePercentForSquareDisplay) + val requestedOptAspRatio = res.getFloat(R.dimen.config_pipLargeScreenOptimizedAspectRatio) // make sure the optimized aspect ratio is valid with a default value to fall back to mOptimizedAspectRatio = if (requestedOptAspRatio > 1) { @@ -128,7 +178,7 @@ class PhoneSizeSpecSource( return minSize } val maxSize = getMaxSize(aspectRatio) - val defaultWidth = Math.max(Math.round(maxSize.width * mDefaultSizePercent), + val defaultWidth = Math.max(Math.round(maxSize.width * mPreferredDefaultSizePercent), minSize.width) val defaultHeight = Math.round(defaultWidth / aspectRatio) return Size(defaultWidth, defaultHeight) @@ -146,8 +196,8 @@ class PhoneSizeSpecSource( return adjustOverrideMinSizeToAspectRatio(aspectRatio)!! } val maxSize = getMaxSize(aspectRatio) - var minWidth = Math.round(maxSize.width * mMinimumSizePercent) - var minHeight = Math.round(maxSize.height * mMinimumSizePercent) + var minWidth = Math.round(maxSize.width * mPreferredMinimumSizePercent) + var minHeight = Math.round(maxSize.height * mPreferredMinimumSizePercent) // make sure the calculated min size is not smaller than the allowed default min size if (aspectRatio > 1f) { @@ -244,8 +294,8 @@ class PhoneSizeSpecSource( pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize) pw.println(innerPrefix + "mOverridableMinSize=" + mOverridableMinSize) pw.println(innerPrefix + "mDefaultMinSize=" + mDefaultMinSize) - pw.println(innerPrefix + "mDefaultSizePercent=" + mDefaultSizePercent) - pw.println(innerPrefix + "mMinimumSizePercent=" + mMinimumSizePercent) + pw.println(innerPrefix + "mDefaultSizePercent=" + mPreferredDefaultSizePercent) + pw.println(innerPrefix + "mMinimumSizePercent=" + mPreferredMinimumSizePercent) pw.println(innerPrefix + "mOptimizedAspectRatio=" + mOptimizedAspectRatio) } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java index 3d5cd6939d1b..85f1da5322ea 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java @@ -16,33 +16,26 @@ package com.android.wm.shell.pip.phone; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; -import android.os.SystemProperties; import android.testing.AndroidTestingRunner; import android.util.Size; import android.view.DisplayInfo; -import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.pip.PhoneSizeSpecSource; import com.android.wm.shell.common.pip.PipDisplayLayoutState; import com.android.wm.shell.common.pip.SizeSpecSource; -import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.exceptions.misusing.InvalidUseOfMatchersException; import java.util.HashMap; import java.util.Map; @@ -63,15 +56,24 @@ public class PhoneSizeSpecSourceTest extends ShellTestCase { private static final float DEFAULT_PERCENT = 0.6f; /** Minimum sizing percentage */ private static final float MIN_PERCENT = 0.5f; + /** Threshold to determine if a Display is square-ish. */ + private static final float SQUARE_DISPLAY_THRESHOLD = 0.95f; + /** Default sizing percentage for square-ish Display. */ + private static final float SQUARE_DISPLAY_DEFAULT_PERCENT = 0.5f; + /** Minimum sizing percentage for square-ish Display. */ + private static final float SQUARE_DISPLAY_MIN_PERCENT = 0.4f; /** Aspect ratio that the new PIP size spec logic optimizes for. */ private static final float OPTIMIZED_ASPECT_RATIO = 9f / 16; - /** A map of aspect ratios to be tested to expected sizes */ - private static Map<Float, Size> sExpectedMaxSizes; - private static Map<Float, Size> sExpectedDefaultSizes; - private static Map<Float, Size> sExpectedMinSizes; - /** A static mockito session object to mock {@link SystemProperties} */ - private static StaticMockitoSession sStaticMockitoSession; + /** Maps of aspect ratios to be tested to expected sizes on non-square Display. */ + private static Map<Float, Size> sNonSquareDisplayExpectedMaxSizes; + private static Map<Float, Size> sNonSquareDisplayExpectedDefaultSizes; + private static Map<Float, Size> sNonSquareDisplayExpectedMinSizes; + + /** Maps of aspect ratios to be tested to expected sizes on square Display. */ + private static Map<Float, Size> sSquareDisplayExpectedMaxSizes; + private static Map<Float, Size> sSquareDisplayExpectedDefaultSizes; + private static Map<Float, Size> sSquareDisplayExpectedMinSizes; @Mock private Context mContext; @Mock private Resources mResources; @@ -80,49 +82,55 @@ public class PhoneSizeSpecSourceTest extends ShellTestCase { private SizeSpecSource mSizeSpecSource; /** - * Sets up static Mockito session for SystemProperties and mocks necessary static methods. + * Initializes the map with the aspect ratios to be tested and corresponding expected max sizes. + * This is to initialize the expectations on non-square Display only. */ - private static void setUpStaticSystemPropertiesSession() { - sStaticMockitoSession = mockitoSession() - .mockStatic(SystemProperties.class).startMocking(); - when(SystemProperties.get(anyString(), anyString())).thenAnswer(invocation -> { - String property = invocation.getArgument(0); - if (property.equals("com.android.wm.shell.pip.phone.def_percentage")) { - return Float.toString(DEFAULT_PERCENT); - } else if (property.equals("com.android.wm.shell.pip.phone.min_percentage")) { - return Float.toString(MIN_PERCENT); - } - - // throw an exception if illegal arguments are used for these tests - throw new InvalidUseOfMatchersException( - String.format("Argument %s does not match", property) - ); - }); + private static void initNonSquareDisplayExpectedSizes() { + sNonSquareDisplayExpectedMaxSizes = new HashMap<>(); + sNonSquareDisplayExpectedDefaultSizes = new HashMap<>(); + sNonSquareDisplayExpectedMinSizes = new HashMap<>(); + + sNonSquareDisplayExpectedMaxSizes.put(16f / 9, new Size(1000, 563)); + sNonSquareDisplayExpectedDefaultSizes.put(16f / 9, new Size(600, 338)); + sNonSquareDisplayExpectedMinSizes.put(16f / 9, new Size(501, 282)); + + sNonSquareDisplayExpectedMaxSizes.put(4f / 3, new Size(893, 670)); + sNonSquareDisplayExpectedDefaultSizes.put(4f / 3, new Size(536, 402)); + sNonSquareDisplayExpectedMinSizes.put(4f / 3, new Size(447, 335)); + + sNonSquareDisplayExpectedMaxSizes.put(3f / 4, new Size(670, 893)); + sNonSquareDisplayExpectedDefaultSizes.put(3f / 4, new Size(402, 536)); + sNonSquareDisplayExpectedMinSizes.put(3f / 4, new Size(335, 447)); + + sNonSquareDisplayExpectedMaxSizes.put(9f / 16, new Size(563, 1001)); + sNonSquareDisplayExpectedDefaultSizes.put(9f / 16, new Size(338, 601)); + sNonSquareDisplayExpectedMinSizes.put(9f / 16, new Size(282, 501)); } /** * Initializes the map with the aspect ratios to be tested and corresponding expected max sizes. + * This is to initialize the expectations on square Display only. */ - private static void initExpectedSizes() { - sExpectedMaxSizes = new HashMap<>(); - sExpectedDefaultSizes = new HashMap<>(); - sExpectedMinSizes = new HashMap<>(); - - sExpectedMaxSizes.put(16f / 9, new Size(1000, 563)); - sExpectedDefaultSizes.put(16f / 9, new Size(600, 338)); - sExpectedMinSizes.put(16f / 9, new Size(501, 282)); - - sExpectedMaxSizes.put(4f / 3, new Size(893, 670)); - sExpectedDefaultSizes.put(4f / 3, new Size(536, 402)); - sExpectedMinSizes.put(4f / 3, new Size(447, 335)); - - sExpectedMaxSizes.put(3f / 4, new Size(670, 893)); - sExpectedDefaultSizes.put(3f / 4, new Size(402, 536)); - sExpectedMinSizes.put(3f / 4, new Size(335, 447)); - - sExpectedMaxSizes.put(9f / 16, new Size(563, 1001)); - sExpectedDefaultSizes.put(9f / 16, new Size(338, 601)); - sExpectedMinSizes.put(9f / 16, new Size(282, 501)); + private static void initSquareDisplayExpectedSizes() { + sSquareDisplayExpectedMaxSizes = new HashMap<>(); + sSquareDisplayExpectedDefaultSizes = new HashMap<>(); + sSquareDisplayExpectedMinSizes = new HashMap<>(); + + sSquareDisplayExpectedMaxSizes.put(16f / 9, new Size(1000, 563)); + sSquareDisplayExpectedDefaultSizes.put(16f / 9, new Size(500, 281)); + sSquareDisplayExpectedMinSizes.put(16f / 9, new Size(400, 225)); + + sSquareDisplayExpectedMaxSizes.put(4f / 3, new Size(893, 670)); + sSquareDisplayExpectedDefaultSizes.put(4f / 3, new Size(447, 335)); + sSquareDisplayExpectedMinSizes.put(4f / 3, new Size(357, 268)); + + sSquareDisplayExpectedMaxSizes.put(3f / 4, new Size(670, 893)); + sSquareDisplayExpectedDefaultSizes.put(3f / 4, new Size(335, 447)); + sSquareDisplayExpectedMinSizes.put(3f / 4, new Size(268, 357)); + + sSquareDisplayExpectedMaxSizes.put(9f / 16, new Size(563, 1001)); + sSquareDisplayExpectedDefaultSizes.put(9f / 16, new Size(282, 501)); + sSquareDisplayExpectedMinSizes.put(9f / 16, new Size(225, 400)); } private void forEveryTestCaseCheck(Map<Float, Size> expectedSizes, @@ -137,20 +145,38 @@ public class PhoneSizeSpecSourceTest extends ShellTestCase { @Before public void setUp() { - initExpectedSizes(); - - when(mResources.getDimensionPixelSize(anyInt())).thenReturn(DEFAULT_MIN_EDGE_SIZE); - when(mResources.getFloat(anyInt())).thenReturn(OPTIMIZED_ASPECT_RATIO); - when(mResources.getString(anyInt())).thenReturn("0x0"); + initNonSquareDisplayExpectedSizes(); + initSquareDisplayExpectedSizes(); + + when(mResources.getFloat(R.dimen.config_pipSystemPreferredDefaultSizePercent)) + .thenReturn(DEFAULT_PERCENT); + when(mResources.getFloat(R.dimen.config_pipSystemPreferredMinimumSizePercent)) + .thenReturn(MIN_PERCENT); + when(mResources.getDimensionPixelSize(R.dimen.default_minimal_size_pip_resizable_task)) + .thenReturn(DEFAULT_MIN_EDGE_SIZE); + when(mResources.getFloat(R.dimen.config_pipLargeScreenOptimizedAspectRatio)) + .thenReturn(OPTIMIZED_ASPECT_RATIO); + when(mResources.getString(R.string.config_defaultPictureInPictureScreenEdgeInsets)) + .thenReturn("0x0"); when(mResources.getDisplayMetrics()) .thenReturn(getContext().getResources().getDisplayMetrics()); + when(mResources.getFloat(R.dimen.config_pipSquareDisplayThresholdForSystemPreferredSize)) + .thenReturn(SQUARE_DISPLAY_THRESHOLD); + when(mResources.getFloat( + R.dimen.config_pipSystemPreferredDefaultSizePercentForSquareDisplay)) + .thenReturn(SQUARE_DISPLAY_DEFAULT_PERCENT); + when(mResources.getFloat( + R.dimen.config_pipSystemPreferredMinimumSizePercentForSquareDisplay)) + .thenReturn(SQUARE_DISPLAY_MIN_PERCENT); // set up the mock context for spec handler specifically when(mContext.getResources()).thenReturn(mResources); + } + private void setupSizeSpecWithDisplayDimension(int width, int height) { DisplayInfo displayInfo = new DisplayInfo(); - displayInfo.logicalWidth = DISPLAY_EDGE_SIZE; - displayInfo.logicalHeight = DISPLAY_EDGE_SIZE; + displayInfo.logicalWidth = width; + displayInfo.logicalHeight = height; // use the parent context (not the mocked one) to obtain the display layout // this is done to avoid unnecessary mocking while allowing for custom display dimensions @@ -159,38 +185,57 @@ public class PhoneSizeSpecSourceTest extends ShellTestCase { mPipDisplayLayoutState = new PipDisplayLayoutState(mContext); mPipDisplayLayoutState.setDisplayLayout(displayLayout); - setUpStaticSystemPropertiesSession(); mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState); // no overridden min edge size by default mSizeSpecSource.setOverrideMinSize(null); } - @After - public void cleanUp() { - sStaticMockitoSession.finishMocking(); + @Test + public void testGetMaxSize_nonSquareDisplay() { + setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE * 2, DISPLAY_EDGE_SIZE); + forEveryTestCaseCheck(sNonSquareDisplayExpectedMaxSizes, + (aspectRatio) -> mSizeSpecSource.getMaxSize(aspectRatio)); + } + + @Test + public void testGetDefaultSize_nonSquareDisplay() { + setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE * 2, DISPLAY_EDGE_SIZE); + forEveryTestCaseCheck(sNonSquareDisplayExpectedDefaultSizes, + (aspectRatio) -> mSizeSpecSource.getDefaultSize(aspectRatio)); + } + + @Test + public void testGetMinSize_nonSquareDisplay() { + setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE * 2, DISPLAY_EDGE_SIZE); + forEveryTestCaseCheck(sNonSquareDisplayExpectedMinSizes, + (aspectRatio) -> mSizeSpecSource.getMinSize(aspectRatio)); } @Test - public void testGetMaxSize() { - forEveryTestCaseCheck(sExpectedMaxSizes, + public void testGetMaxSize_squareDisplay() { + setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE, DISPLAY_EDGE_SIZE); + forEveryTestCaseCheck(sSquareDisplayExpectedMaxSizes, (aspectRatio) -> mSizeSpecSource.getMaxSize(aspectRatio)); } @Test - public void testGetDefaultSize() { - forEveryTestCaseCheck(sExpectedDefaultSizes, + public void testGetDefaultSize_squareDisplay() { + setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE, DISPLAY_EDGE_SIZE); + forEveryTestCaseCheck(sSquareDisplayExpectedDefaultSizes, (aspectRatio) -> mSizeSpecSource.getDefaultSize(aspectRatio)); } @Test - public void testGetMinSize() { - forEveryTestCaseCheck(sExpectedMinSizes, + public void testGetMinSize_squareDisplay() { + setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE, DISPLAY_EDGE_SIZE); + forEveryTestCaseCheck(sSquareDisplayExpectedMinSizes, (aspectRatio) -> mSizeSpecSource.getMinSize(aspectRatio)); } @Test public void testGetSizeForAspectRatio_noOverrideMinSize() { + setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE * 2, DISPLAY_EDGE_SIZE); // an initial size with 16:9 aspect ratio Size initSize = new Size(600, 337); @@ -202,6 +247,7 @@ public class PhoneSizeSpecSourceTest extends ShellTestCase { @Test public void testGetSizeForAspectRatio_withOverrideMinSize() { + setupSizeSpecWithDisplayDimension(DISPLAY_EDGE_SIZE * 2, DISPLAY_EDGE_SIZE); // an initial size with a 1:1 aspect ratio Size initSize = new Size(OVERRIDE_MIN_EDGE_SIZE, OVERRIDE_MIN_EDGE_SIZE); mSizeSpecSource.setOverrideMinSize(initSize); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index f1eea728bedd..df84653135fa 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1839,18 +1839,19 @@ public class AudioDeviceBroker { && !mBtHelper.isProfilePoxyConnected(btInfo.mProfile)) { AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( "msg: MSG_L_SET_BT_ACTIVE_DEVICE " - + "received with null profile proxy: " - + btInfo)).printLog(TAG)); + + "received with null profile proxy: " + + btInfo)).printLog(TAG)); } else { - @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec = + final Pair<Integer, Boolean> codecAndChanged = mBtHelper.getCodecWithFallback(btInfo.mDevice, btInfo.mProfile, btInfo.mIsLeOutput, "MSG_L_SET_BT_ACTIVE_DEVICE"); - mDeviceInventory.onSetBtActiveDevice(btInfo, codec, - (btInfo.mProfile - != BluetoothProfile.LE_AUDIO || btInfo.mIsLeOutput) - ? mAudioService.getBluetoothContextualVolumeStream() - : AudioSystem.STREAM_DEFAULT); + mDeviceInventory.onSetBtActiveDevice(btInfo, + codecAndChanged.first, + (btInfo.mProfile != BluetoothProfile.LE_AUDIO + || btInfo.mIsLeOutput) + ? mAudioService.getBluetoothContextualVolumeStream() + : AudioSystem.STREAM_DEFAULT); if (btInfo.mProfile == BluetoothProfile.LE_AUDIO || btInfo.mProfile == BluetoothProfile.HEARING_AID) { onUpdateCommunicationRouteClient(isBluetoothScoRequested(), @@ -1884,12 +1885,13 @@ public class AudioDeviceBroker { case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: { final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj; synchronized (mDeviceStateLock) { - @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec = - mBtHelper.getCodecWithFallback(btInfo.mDevice, - btInfo.mProfile, btInfo.mIsLeOutput, - "MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE"); - mDeviceInventory.onBluetoothDeviceConfigChange( - btInfo, codec, BtHelper.EVENT_DEVICE_CONFIG_CHANGE); + final Pair<Integer, Boolean> codecAndChanged = + mBtHelper.getCodecWithFallback( + btInfo.mDevice, btInfo.mProfile, btInfo.mIsLeOutput, + "MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE"); + mDeviceInventory.onBluetoothDeviceConfigChange(btInfo, + codecAndChanged.first, codecAndChanged.second, + BtHelper.EVENT_DEVICE_CONFIG_CHANGE); } } break; case MSG_BROADCAST_AUDIO_BECOMING_NOISY: diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index e0c24256f5b1..8b64d93e4b30 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -864,7 +864,8 @@ public class AudioDeviceInventory { @GuardedBy("mDeviceBroker.mDeviceStateLock") /*package*/ void onBluetoothDeviceConfigChange( @NonNull AudioDeviceBroker.BtDeviceInfo btInfo, - @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, int event) { + @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, + boolean codecChanged, int event) { MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onBluetoothDeviceConfigChange") .set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event)); @@ -912,14 +913,12 @@ public class AudioDeviceInventory { if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { - boolean codecChange = false; if (btInfo.mProfile == BluetoothProfile.A2DP || btInfo.mProfile == BluetoothProfile.LE_AUDIO || btInfo.mProfile == BluetoothProfile.LE_AUDIO_BROADCAST) { - if (di.mDeviceCodecFormat != codec) { + if (codecChanged) { di.mDeviceCodecFormat = codec; mConnectedDevices.replace(key, di); - codecChange = true; final int res = mAudioSystem.handleDeviceConfigChange( btInfo.mAudioSystemDevice, address, BtHelper.getName(btDevice), codec); @@ -943,7 +942,7 @@ public class AudioDeviceInventory { } } } - if (!codecChange) { + if (!codecChanged) { updateBluetoothPreferredModes_l(btDevice /*connectedDevice*/); } } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 0f3f8073edcc..69f7399e60f8 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -98,9 +98,16 @@ public class BtHelper { private @Nullable BluetoothLeAudio mLeAudio; + private @Nullable BluetoothLeAudioCodecConfig mLeAudioCodecConfig; + // Reference to BluetoothA2dp to query for AbsoluteVolume. private @Nullable BluetoothA2dp mA2dp; + private @Nullable BluetoothCodecConfig mA2dpCodecConfig; + + private @AudioSystem.AudioFormatNativeEnumForBtCodec + int mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_DEFAULT; + // If absolute volume is supported in AVRCP device private boolean mAvrcpAbsVolSupported = false; @@ -269,12 +276,15 @@ public class BtHelper { } } - /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getCodec( + private synchronized Pair<Integer, Boolean> getCodec( @NonNull BluetoothDevice device, @AudioService.BtProfile int profile) { + switch (profile) { case BluetoothProfile.A2DP: { + boolean changed = mA2dpCodecConfig != null; if (mA2dp == null) { - return AudioSystem.AUDIO_FORMAT_DEFAULT; + mA2dpCodecConfig = null; + return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); } BluetoothCodecStatus btCodecStatus = null; try { @@ -283,17 +293,24 @@ public class BtHelper { Log.e(TAG, "Exception while getting status of " + device, e); } if (btCodecStatus == null) { - return AudioSystem.AUDIO_FORMAT_DEFAULT; + mA2dpCodecConfig = null; + return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); } final BluetoothCodecConfig btCodecConfig = btCodecStatus.getCodecConfig(); if (btCodecConfig == null) { - return AudioSystem.AUDIO_FORMAT_DEFAULT; + mA2dpCodecConfig = null; + return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); } - return AudioSystem.bluetoothA2dpCodecToAudioFormat(btCodecConfig.getCodecType()); + changed = !btCodecConfig.equals(mA2dpCodecConfig); + mA2dpCodecConfig = btCodecConfig; + return new Pair<>(AudioSystem.bluetoothA2dpCodecToAudioFormat( + btCodecConfig.getCodecType()), changed); } case BluetoothProfile.LE_AUDIO: { + boolean changed = mLeAudioCodecConfig != null; if (mLeAudio == null) { - return AudioSystem.AUDIO_FORMAT_DEFAULT; + mLeAudioCodecConfig = null; + return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); } BluetoothLeAudioCodecStatus btLeCodecStatus = null; int groupId = mLeAudio.getGroupId(device); @@ -303,42 +320,54 @@ public class BtHelper { Log.e(TAG, "Exception while getting status of " + device, e); } if (btLeCodecStatus == null) { - return AudioSystem.AUDIO_FORMAT_DEFAULT; + mLeAudioCodecConfig = null; + return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); } BluetoothLeAudioCodecConfig btLeCodecConfig = btLeCodecStatus.getOutputCodecConfig(); if (btLeCodecConfig == null) { - return AudioSystem.AUDIO_FORMAT_DEFAULT; + mLeAudioCodecConfig = null; + return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); } - return AudioSystem.bluetoothLeCodecToAudioFormat(btLeCodecConfig.getCodecType()); + changed = !btLeCodecConfig.equals(mLeAudioCodecConfig); + mLeAudioCodecConfig = btLeCodecConfig; + return new Pair<>(AudioSystem.bluetoothLeCodecToAudioFormat( + btLeCodecConfig.getCodecType()), changed); + } + case BluetoothProfile.LE_AUDIO_BROADCAST: { + // We assume LC3 for LE Audio broadcast codec as there is no API to get the codec + // config on LE Broadcast profile proxy. + boolean changed = mLeAudioBroadcastCodec != AudioSystem.AUDIO_FORMAT_LC3; + mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_LC3; + return new Pair<>(mLeAudioBroadcastCodec, changed); } default: - return AudioSystem.AUDIO_FORMAT_DEFAULT; + return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, false); } } - /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec - int getCodecWithFallback( - @NonNull BluetoothDevice device, @AudioService.BtProfile int profile, - boolean isLeOutput, @NonNull String source) { + /*package*/ synchronized Pair<Integer, Boolean> + getCodecWithFallback(@NonNull BluetoothDevice device, + @AudioService.BtProfile int profile, + boolean isLeOutput, @NonNull String source) { // For profiles other than A2DP and LE Audio output, the audio codec format must be // AUDIO_FORMAT_DEFAULT as native audio policy manager expects a specific audio format // only if audio HW module selection based on format is supported for the device type. if (!(profile == BluetoothProfile.A2DP || (isLeOutput && ((profile == BluetoothProfile.LE_AUDIO) || (profile == BluetoothProfile.LE_AUDIO_BROADCAST))))) { - return AudioSystem.AUDIO_FORMAT_DEFAULT; + return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, false); } - @AudioSystem.AudioFormatNativeEnumForBtCodec int codec = + Pair<Integer, Boolean> codecAndChanged = getCodec(device, profile); - if (codec == AudioSystem.AUDIO_FORMAT_DEFAULT) { + if (codecAndChanged.first == AudioSystem.AUDIO_FORMAT_DEFAULT) { AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( "getCodec DEFAULT from " + source + " fallback to " + (profile == BluetoothProfile.A2DP ? "SBC" : "LC3"))); - return profile == BluetoothProfile.A2DP - ? AudioSystem.AUDIO_FORMAT_SBC : AudioSystem.AUDIO_FORMAT_LC3; + return new Pair<>(profile == BluetoothProfile.A2DP + ? AudioSystem.AUDIO_FORMAT_SBC : AudioSystem.AUDIO_FORMAT_LC3, true); } - return codec; + return codecAndChanged; } // @GuardedBy("mDeviceBroker.mSetModeLock") @@ -543,15 +572,19 @@ public class BtHelper { break; case BluetoothProfile.A2DP: mA2dp = null; + mA2dpCodecConfig = null; break; case BluetoothProfile.HEARING_AID: mHearingAid = null; break; case BluetoothProfile.LE_AUDIO: mLeAudio = null; + mLeAudioCodecConfig = null; break; - case BluetoothProfile.A2DP_SINK: case BluetoothProfile.LE_AUDIO_BROADCAST: + mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_DEFAULT; + break; + case BluetoothProfile.A2DP_SINK: // nothing to do in BtHelper break; default: diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java index a63db88cb614..7b5b07c0fbf6 100644 --- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java +++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java @@ -16,6 +16,8 @@ package com.android.internal.telephony.util; import static android.telephony.Annotation.DataState; +import static android.telephony.NetworkRegistrationInfo.FIRST_SERVICE_TYPE; +import static android.telephony.NetworkRegistrationInfo.LAST_SERVICE_TYPE; import android.annotation.NonNull; import android.annotation.Nullable; @@ -37,6 +39,7 @@ import android.provider.Telephony.Carriers.EditStatus; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.Log; import com.android.internal.telephony.ITelephony; @@ -48,6 +51,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * This class provides various util functions @@ -342,4 +347,31 @@ public final class TelephonyUtils { return false; } + + /** + * @param plmn target plmn for validation. + * @return {@code true} if the target plmn is valid {@code false} otherwise. + */ + public static boolean isValidPlmn(@Nullable String plmn) { + if (TextUtils.isEmpty(plmn)) { + return false; + } + Pattern pattern = Pattern.compile("^(?:[0-9]{3})(?:[0-9]{2}|[0-9]{3})$"); + Matcher matcher = pattern.matcher(plmn); + if (!matcher.matches()) { + return false; + } + return true; + } + + /** + * @param serviceType target serviceType for validation. + * @return {@code true} if the target serviceType is valid {@code false} otherwise. + */ + public static boolean isValidService(int serviceType) { + if (serviceType < FIRST_SERVICE_TYPE || serviceType > LAST_SERVICE_TYPE) { + return false; + } + return true; + } } diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl index 36485c6b6fb5..16983a0dbca1 100644 --- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl +++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl @@ -83,6 +83,9 @@ oneway interface ISatellite { * * @param enableSatellite True to enable the satellite modem and false to disable. * @param enableDemoMode True to enable demo mode and false to disable. + * @param isEmergency To specify the satellite is enabled for emergency session and false for + * non emergency session. Note: it is possible that a emergency session started get converted + * to a non emergency session and vice versa. * @param resultCallback The callback to receive the error code result of the operation. * * Valid result codes returned: @@ -96,7 +99,7 @@ oneway interface ISatellite { * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void requestSatelliteEnabled(in boolean enableSatellite, in boolean enableDemoMode, - in IIntegerConsumer resultCallback); + in boolean isEmergency, in IIntegerConsumer resultCallback); /** * Request to get whether the satellite modem is enabled. diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java index b7dc79ff7283..a62363335fb9 100644 --- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java +++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java @@ -90,11 +90,11 @@ public class SatelliteImplBase extends SatelliteService { @Override public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode, - IIntegerConsumer resultCallback) throws RemoteException { + boolean isEmergency, IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this .requestSatelliteEnabled( - enableSatellite, enableDemoMode, resultCallback), + enableSatellite, enableDemoMode, isEmergency, resultCallback), "requestSatelliteEnabled"); } @@ -337,6 +337,9 @@ public class SatelliteImplBase extends SatelliteService { * * @param enableSatellite True to enable the satellite modem and false to disable. * @param enableDemoMode True to enable demo mode and false to disable. + * @param isEmergency To specify the satellite is enabled for emergency session and false for + * non emergency session. Note: it is possible that a emergency session started get converted + * to a non emergency session and vice versa. * @param resultCallback The callback to receive the error code result of the operation. * * Valid result codes returned: @@ -350,7 +353,7 @@ public class SatelliteImplBase extends SatelliteService { * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode, - @NonNull IIntegerConsumer resultCallback) { + boolean isEmergency, @NonNull IIntegerConsumer resultCallback) { // stub implementation } diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java index a62103e0030b..755833234e02 100644 --- a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java +++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java @@ -16,10 +16,16 @@ package com.android.internal.telephony.tests; +import static android.telephony.NetworkRegistrationInfo.FIRST_SERVICE_TYPE; +import static android.telephony.NetworkRegistrationInfo.LAST_SERVICE_TYPE; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Context; @@ -72,6 +78,22 @@ public class TelephonyUtilsTest { // getSubscriptionUserHandle should be called if subID is active. verify(mSubscriptionManager, times(1)).getSubscriptionUserHandle(eq(activeSubId)); } + + @Test + public void testIsValidPlmn() { + assertTrue(TelephonyUtils.isValidPlmn("310260")); + assertTrue(TelephonyUtils.isValidPlmn("45006")); + assertFalse(TelephonyUtils.isValidPlmn("1234567")); + assertFalse(TelephonyUtils.isValidPlmn("1234")); + } + + @Test + public void testIsValidService() { + assertTrue(TelephonyUtils.isValidService(FIRST_SERVICE_TYPE)); + assertTrue(TelephonyUtils.isValidService(LAST_SERVICE_TYPE)); + assertFalse(TelephonyUtils.isValidService(FIRST_SERVICE_TYPE - 1)); + assertFalse(TelephonyUtils.isValidService(LAST_SERVICE_TYPE + 1)); + } } |