diff options
29 files changed, 530 insertions, 104 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1c31b1b76f85..d9007e50c7c6 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2679,6 +2679,16 @@ <item>350</item> </integer-array> + <!-- A vibration waveform for notifications that specify DEFAULT_VIBRATE. + This value is a float array with values grouped as + { targetAmplitude (within [0,1]), targetFrequency [-1,1], duration (in milliseconds) } + This is only applied on devices with vibration frequency control. If the device doesn't + support frequency control, then the vibration specified in + config_defaultNotificationVibePattern is used instead. + --> + <array name="config_defaultNotificationVibeWaveform"> + </array> + <!-- Vibrator pattern to be used as the default for notifications that do not specify vibration but vibrate anyway because the device is in vibrate mode. @@ -2690,6 +2700,16 @@ <item>100</item> </integer-array> + <!-- A vibration waveform for notifications that do not specify vibration but vibrate anyway, + because the device is in vibrate mode. This value is a float array with values grouped as + { targetAmplitude (within [0,1]), targetFrequency [-1,1], duration (in milliseconds) } + This is only applied on devices with vibration frequency control. If the device doesn't + support frequency control, then the vibration specified in + config_notificationFallbackVibePattern is used instead. + --> + <array name="config_notificationFallbackVibeWaveform"> + </array> + <!-- Flag indicating if the speed up audio on mt call code should be executed --> <bool name="config_speed_up_audio_on_mt_calls">false</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b017a30cb5f2..f68c26d76610 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1917,7 +1917,9 @@ <java-symbol type="array" name="config_locationExtraPackageNames" /> <java-symbol type="array" name="config_testLocationProviders" /> <java-symbol type="array" name="config_defaultNotificationVibePattern" /> + <java-symbol type="array" name="config_defaultNotificationVibeWaveform" /> <java-symbol type="array" name="config_notificationFallbackVibePattern" /> + <java-symbol type="array" name="config_notificationFallbackVibeWaveform" /> <java-symbol type="bool" name="config_enableServerNotificationEffectsForAutomotive" /> <java-symbol type="bool" name="config_useAttentionLight" /> <java-symbol type="bool" name="config_adaptive_sleep_available" /> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index df751fc9fa48..180c77250fd1 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -79,22 +79,23 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { } @Override - public void registerOrganizer() { - if (mAnimationController != null) { - throw new IllegalStateException("Must unregister the organizer before re-register."); + public void unregisterOrganizer() { + stopOverrideSplitAnimation(); + mAnimationController = null; + super.unregisterOrganizer(); + } + + void startOverrideSplitAnimation() { + if (mAnimationController == null) { + mAnimationController = new TaskFragmentAnimationController(this); } - super.registerOrganizer(); - mAnimationController = new TaskFragmentAnimationController(this); mAnimationController.registerRemoteAnimations(); } - @Override - public void unregisterOrganizer() { + void stopOverrideSplitAnimation() { if (mAnimationController != null) { mAnimationController.unregisterRemoteAnimations(); - mAnimationController = null; } - super.unregisterOrganizer(); } /** diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index b8e8b0114b47..8f368c2bee22 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -33,6 +33,7 @@ import android.app.Instrumentation; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; +import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -63,6 +64,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback; private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>(); + // We currently only support split activity embedding within the one root Task. + private final Rect mParentBounds = new Rect(); + public SplitController() { mPresenter = new SplitPresenter(new MainThreadExecutor(), this); ActivityThread activityThread = ActivityThread.currentActivityThread(); @@ -79,6 +83,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) { mSplitRules.clear(); mSplitRules.addAll(rules); + updateAnimationOverride(); } @NonNull @@ -158,6 +163,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @Override public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) { + onParentBoundsMayChange(parentConfig.windowConfiguration.getBounds()); TaskFragmentContainer container = getContainer(fragmentToken); if (container != null) { mPresenter.updateContainer(container); @@ -165,6 +171,51 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } + private void onParentBoundsMayChange(Activity activity) { + if (activity.isFinishing()) { + return; + } + + onParentBoundsMayChange(mPresenter.getParentContainerBounds(activity)); + } + + private void onParentBoundsMayChange(Rect parentBounds) { + if (!parentBounds.isEmpty() && !mParentBounds.equals(parentBounds)) { + mParentBounds.set(parentBounds); + updateAnimationOverride(); + } + } + + /** + * Updates if we should override transition animation. We only want to override if the Task + * bounds is large enough for at least one split rule. + */ + private void updateAnimationOverride() { + if (mParentBounds.isEmpty()) { + // We don't know about the parent bounds yet. + return; + } + + // Check if the parent container bounds can support any split rule. + boolean supportSplit = false; + for (EmbeddingRule rule : mSplitRules) { + if (!(rule instanceof SplitRule)) { + continue; + } + if (mPresenter.shouldShowSideBySide(mParentBounds, (SplitRule) rule)) { + supportSplit = true; + break; + } + } + + // We only want to override if it supports split. + if (supportSplit) { + mPresenter.startOverrideSplitAnimation(); + } else { + mPresenter.stopOverrideSplitAnimation(); + } + } + void onActivityCreated(@NonNull Activity launchedActivity) { handleActivityCreated(launchedActivity); updateCallbackIfNecessary(); @@ -180,6 +231,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final TaskFragmentContainer currentContainer = getContainerWithActivity( launchedActivity.getActivityToken()); + if (currentContainer == null) { + // Initial check before any TaskFragment is created. + onParentBoundsMayChange(launchedActivity); + } + // Check if the activity is configured to always be expanded. if (shouldExpand(launchedActivity, null, splitRules)) { if (shouldContainerBeExpanded(currentContainer)) { @@ -257,6 +313,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // onTaskFragmentParentInfoChanged return; } + // The bounds of the container may have been changed. + onParentBoundsMayChange(activity); // Check if activity requires a placeholder launchPlaceholderIfNecessary(activity); @@ -346,7 +404,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen TaskFragmentContainer getTopActiveContainer() { for (int i = mContainers.size() - 1; i >= 0; i--) { TaskFragmentContainer container = mContainers.get(i); - if (!container.isFinished()) { + if (!container.isFinished() && container.getTopNonFinishingActivity() != null) { return container; } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java index 3c7d2de6165f..a801dc8193fd 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java @@ -37,32 +37,42 @@ class TaskFragmentAnimationController { private final TaskFragmentOrganizer mOrganizer; private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner(); + private final RemoteAnimationDefinition mDefinition; + private boolean mIsRegister; TaskFragmentAnimationController(TaskFragmentOrganizer organizer) { mOrganizer = organizer; + mDefinition = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter animationAdapter = + new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */); + mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_OPEN, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_CLOSE, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter); } void registerRemoteAnimations() { if (DEBUG) { Log.v(TAG, "registerRemoteAnimations"); } - final RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); - final RemoteAnimationAdapter animationAdapter = - new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */); - definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_OPEN, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_CLOSE, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter); - mOrganizer.registerRemoteAnimations(definition); + if (mIsRegister) { + return; + } + mOrganizer.registerRemoteAnimations(mDefinition); + mIsRegister = true; } void unregisterRemoteAnimations() { if (DEBUG) { Log.v(TAG, "unregisterRemoteAnimations"); } + if (!mIsRegister) { + return; + } mOrganizer.unregisterRemoteAnimations(); + mIsRegister = false; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index 7decb54d16bb..338c944f7eec 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -292,7 +292,6 @@ public class RecentTasksController implements TaskStackListenerCallback, * Invalidates this instance, preventing future calls from updating the controller. */ void invalidate() { - Slog.d("b/206648922", "invalidating controller: " + mController); mController = null; } @@ -313,13 +312,16 @@ public class RecentTasksController implements TaskStackListenerCallback, @Override public GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId) throws RemoteException { + if (mController == null) { + // The controller is already invalidated -- just return an empty task list for now + return new GroupedRecentTaskInfo[0]; + } + final GroupedRecentTaskInfo[][] out = new GroupedRecentTaskInfo[][]{null}; executeRemoteCallWithTaskPermission(mController, "getRecentTasks", (controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId) .toArray(new GroupedRecentTaskInfo[0]), true /* blocking */); - Slog.d("b/206648922", "getRecentTasks(" + maxNum + "): " + out[0] - + " mController=" + mController); return out[0]; } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt index 977e46ac3b44..d2ded71487dc 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt @@ -131,6 +131,12 @@ class ControlsProviderLifecycleManager( wrapper = null bindService(false) } + + override fun onNullBinding(name: ComponentName?) { + if (DEBUG) Log.d(TAG, "onNullBinding $name") + wrapper = null + context.unbindService(this) + } } private fun handlePendingServiceMethods() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 7a53fd1152fe..e9f288d51317 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1727,6 +1727,21 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, || mUpdateMonitor.isSimPinSecure(); } + /** + * Whether any of the SIMs on the device are secured with a PIN. If so, the keyguard should not + * be dismissable until the PIN is entered, even if the device itself has no lock set. + */ + public boolean isAnySimPinSecure() { + for (int i = 0; i < mLastSimStates.size(); i++) { + final int key = mLastSimStates.keyAt(i); + if (KeyguardUpdateMonitor.isSimPinSecure(mLastSimStates.get(key))) { + return true; + } + } + + return false; + } + public void setSwitchingUser(boolean switching) { mUpdateMonitor.setSwitchingUser(switching); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index 3f5ef4806a1e..631a1ca3dcec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -335,6 +335,8 @@ public class NetworkControllerImpl extends BroadcastReceiver setUserSetupComplete(deviceProvisionedController.isCurrentUserSetup()); } }); + // Get initial user setup state + setUserSetupComplete(deviceProvisionedController.isCurrentUserSetup()); WifiManager.ScanResultsCallback scanResultsCallback = new WifiManager.ScanResultsCallback() { @@ -1000,6 +1002,11 @@ public class NetworkControllerImpl extends BroadcastReceiver } @VisibleForTesting + boolean isUserSetup() { + return mUserSetup; + } + + @VisibleForTesting boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) { if (allSubscriptions.size() != mMobileSignalControllers.size()) { return false; @@ -1144,6 +1151,7 @@ public class NetworkControllerImpl extends BroadcastReceiver /** */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NetworkController state:"); + pw.println(" mUserSetup=" + mUserSetup); pw.println(" - telephony ------"); pw.print(" hasVoiceCallingFeature()="); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt index 4de78f5d6190..868efa027f40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt @@ -34,7 +34,7 @@ class LSShadeTransitionLogger @Inject constructor( private val displayMetrics: DisplayMetrics ) { fun logUnSuccessfulDragDown(startingChild: View?) { - val entry = (startingChild as ExpandableNotificationRow?)?.entry + val entry = (startingChild as? ExpandableNotificationRow)?.entry buffer.log(TAG, LogLevel.INFO, { str1 = entry?.key ?: "no entry" }, { @@ -49,7 +49,7 @@ class LSShadeTransitionLogger @Inject constructor( } fun logDragDownStarted(startingChild: ExpandableView?) { - val entry = (startingChild as ExpandableNotificationRow?)?.entry + val entry = (startingChild as? ExpandableNotificationRow)?.entry buffer.log(TAG, LogLevel.INFO, { str1 = entry?.key ?: "no entry" }, { @@ -58,7 +58,7 @@ class LSShadeTransitionLogger @Inject constructor( } fun logDraggedDownLockDownShade(startingChild: View?) { - val entry = (startingChild as ExpandableNotificationRow?)?.entry + val entry = (startingChild as? ExpandableNotificationRow)?.entry buffer.log(TAG, LogLevel.INFO, { str1 = entry?.key ?: "no entry" }, { @@ -67,7 +67,7 @@ class LSShadeTransitionLogger @Inject constructor( } fun logDraggedDown(startingChild: View?, dragLengthY: Int) { - val entry = (startingChild as ExpandableNotificationRow?)?.entry + val entry = (startingChild as? ExpandableNotificationRow)?.entry buffer.log(TAG, LogLevel.INFO, { str1 = entry?.key ?: "no entry" }, { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 7a68b9616fa3..778a1e36392e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1354,10 +1354,14 @@ public class StatusBar extends SystemUI implements // Things that mean we're not dismissing the keyguard, and should ignore this expansion: // - Keyguard isn't even visible. // - Keyguard is visible, but can't be dismissed (swiping up will show PIN/password prompt). + // - The SIM is locked, you can't swipe to unlock. If the SIM is locked but there is no + // device lock set, canDismissLockScreen returns true even though you should not be able + // to dismiss the lock screen until entering the SIM PIN. // - QS is expanded and we're swiping - swiping up now will hide QS, not dismiss the // keyguard. if (!isKeyguardShowing() || !mKeyguardStateController.canDismissLockScreen() + || mKeyguardViewMediator.isAnySimPinSecure() || (mNotificationPanelViewController.isQsExpanded() && trackingTouch)) { return; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt index 2d3757c29ebf..12096bc06748 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt @@ -17,6 +17,9 @@ package com.android.systemui.controls.controller import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection import android.os.UserHandle import android.service.controls.IControlsActionCallback import android.service.controls.IControlsProvider @@ -43,6 +46,8 @@ import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -57,8 +62,6 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { private lateinit var subscriberService: IControlsSubscriber.Stub @Mock private lateinit var service: IControlsProvider.Stub - @Mock - private lateinit var loadCallback: ControlsBindingController.LoadCallback @Captor private lateinit var wrapperCaptor: ArgumentCaptor<ControlActionWrapper> @@ -75,7 +78,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - mContext.addMockService(componentName, service) + context.addMockService(componentName, service) executor = FakeExecutor(FakeSystemClock()) `when`(service.asBinder()).thenCallRealMethod() `when`(service.queryLocalInterface(ArgumentMatchers.anyString())).thenReturn(service) @@ -98,7 +101,36 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { fun testBindService() { manager.bindService() executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) + } + + @Test + fun testNullBinding() { + val mockContext = mock(Context::class.java) + lateinit var serviceConnection: ServiceConnection + `when`(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer { + val component = (it.arguments[0] as Intent).component + if (component == componentName) { + serviceConnection = it.arguments[1] as ServiceConnection + serviceConnection.onNullBinding(component) + true + } else { + false + } + } + + val nullManager = ControlsProviderLifecycleManager( + mockContext, + executor, + actionCallbackService, + UserHandle.of(0), + componentName + ) + + nullManager.bindService() + executor.runAllReady() + + verify(mockContext).unbindService(serviceConnection) } @Test @@ -109,7 +141,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.unbindService() executor.runAllReady() - assertFalse(mContext.isBound(componentName)) + assertFalse(context.isBound(componentName)) } @Test @@ -119,7 +151,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { verify(service).load(subscriberService) - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) } @Test @@ -129,7 +161,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.unbindService() executor.runAllReady() - assertFalse(mContext.isBound(componentName)) + assertFalse(context.isBound(componentName)) } @Test @@ -162,7 +194,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.maybeBindAndSubscribe(list, subscriberService) executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) verify(service).subscribe(list, subscriberService) } @@ -173,7 +205,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.maybeBindAndSendAction(controlId, action) executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) verify(service).action(eq(controlId), capture(wrapperCaptor), eq(actionCallbackService)) assertEquals(action, wrapperCaptor.getValue().getWrappedAction()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt new file mode 100644 index 000000000000..6971c63ed6d4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt @@ -0,0 +1,44 @@ +package com.android.systemui.statusbar + +import android.testing.AndroidTestingRunner +import android.util.DisplayMetrics +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.log.LogBuffer +import com.android.systemui.statusbar.notification.row.ExpandableView +import com.android.systemui.statusbar.phone.LSShadeTransitionLogger +import com.android.systemui.statusbar.phone.LockscreenGestureLogger +import com.android.systemui.util.mockito.mock +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class LSShadeTransitionLoggerTest : SysuiTestCase() { + lateinit var logger: LSShadeTransitionLogger + @Mock + lateinit var gestureLogger: LockscreenGestureLogger + @Mock + lateinit var displayMetrics: DisplayMetrics + @JvmField @Rule + val mockito = MockitoJUnit.rule() + + @Before + fun setup() { + logger = LSShadeTransitionLogger( + LogBuffer("Test", 10, 10, mock()), + gestureLogger, + displayMetrics) + } + + @Test + fun testLogDragDownStarted() { + val view: ExpandableView = mock() + // log a non-null, non row, ensure no crash + logger.logDragDownStarted(view) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java index 47a11fcdcee4..23f3c53bc332 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java @@ -23,7 +23,7 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; @@ -216,8 +216,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase { when(mMockProvisionController.isCurrentUserSetup()).thenReturn(true); doAnswer(invocation -> { mUserCallback = (DeviceProvisionedListener) invocation.getArguments()[0]; - mUserCallback.onUserSetupChanged(); - mUserCallback.onDeviceProvisionedChanged(); TestableLooper.get(this).processAllMessages(); return null; }).when(mMockProvisionController).addCallback(any()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java index 73eddd166f88..6262a9b628f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java @@ -35,6 +35,7 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import com.android.settingslib.graph.SignalDrawable; @@ -60,6 +61,72 @@ import java.util.List; public class NetworkControllerSignalTest extends NetworkControllerBaseTest { @Test + public void testDeviceProvisioned_userNotSetUp() { + // GIVEN - user is not setup + when(mMockProvisionController.isCurrentUserSetup()).thenReturn(false); + + // WHEN - a NetworkController is created + mNetworkController = new NetworkControllerImpl(mContext, + mMockCm, + mMockTm, + mTelephonyListenerManager, + mMockWm, + mMockNsm, + mMockSm, + mConfig, + TestableLooper.get(this).getLooper(), + mFakeExecutor, + mCallbackHandler, + mock(AccessPointControllerImpl.class), + mock(DataUsageController.class), + mMockSubDefaults, + mMockProvisionController, + mMockBd, + mDemoModeController, + mCarrierConfigTracker, + mFeatureFlags, + mock(DumpManager.class) + ); + TestableLooper.get(this).processAllMessages(); + + // THEN - NetworkController claims the user is not setup + assertFalse("User has not been set up", mNetworkController.isUserSetup()); + } + + @Test + public void testDeviceProvisioned_userSetUp() { + // GIVEN - user is not setup + when(mMockProvisionController.isCurrentUserSetup()).thenReturn(true); + + // WHEN - a NetworkController is created + mNetworkController = new NetworkControllerImpl(mContext, + mMockCm, + mMockTm, + mTelephonyListenerManager, + mMockWm, + mMockNsm, + mMockSm, + mConfig, + TestableLooper.get(this).getLooper(), + mFakeExecutor, + mCallbackHandler, + mock(AccessPointControllerImpl.class), + mock(DataUsageController.class), + mMockSubDefaults, + mMockProvisionController, + mMockBd, + mDemoModeController, + mCarrierConfigTracker, + mFeatureFlags, + mock(DumpManager.class) + ); + TestableLooper.get(this).processAllMessages(); + + // THEN - NetworkController claims the user is not setup + assertTrue("User has been set up", mNetworkController.isUserSetup()); + } + + @Test public void testNoIconWithoutMobile() { // Turn off mobile network support. when(mMockTm.isDataCapable()).thenReturn(false); diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 8199d940883a..6bb5aa148e59 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -523,6 +523,8 @@ public final class CachedAppOptimizer { */ static private native void compactProcess(int pid, int compactionFlags); + static private native void cancelCompaction(); + /** * Reads the flag value from DeviceConfig to determine whether app compaction * should be enabled, and starts the freeze/compaction thread if needed. @@ -1026,6 +1028,26 @@ public final class CachedAppOptimizer { } } + @GuardedBy({"mService", "mProcLock"}) + void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) { + // Cancel any currently executing compactions + // if the process moved out of cached state + if (DefaultProcessDependencies.mPidCompacting == app.mPid && newAdj < oldAdj + && newAdj < ProcessList.CACHED_APP_MIN_ADJ) { + cancelCompaction(); + } + + // Perform a minor compaction when a perceptible app becomes the prev/home app + // Perform a major compaction when any app enters cached + if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ + && (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) { + compactAppSome(app); + } else if (newAdj >= ProcessList.CACHED_APP_MIN_ADJ + && newAdj <= ProcessList.CACHED_APP_MAX_ADJ) { + compactAppFull(app); + } + } + @VisibleForTesting static final class LastCompactionStats { private final long[] mRssAfterCompaction; @@ -1068,6 +1090,13 @@ public final class CachedAppOptimizer { name = proc.processName; opt.setHasPendingCompact(false); + if (mAm.mInternal.isPendingTopUid(proc.uid)) { + // In case the OOM Adjust has not yet been propagated we see if this is + // pending on becoming top app in which case we should not compact. + Slog.e(TAG_AM, "Skip compaction since UID is active for " + name); + return; + } + // don't compact if the process has returned to perceptible // and this is only a cached/home/prev compaction if ((pendingAction == COMPACT_PROCESS_SOME @@ -1477,6 +1506,8 @@ public final class CachedAppOptimizer { * Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class. */ private static final class DefaultProcessDependencies implements ProcessDependencies { + public static int mPidCompacting = -1; + // Get memory RSS from process. @Override public long[] getRss(int pid) { @@ -1486,6 +1517,7 @@ public final class CachedAppOptimizer { // Compact process. @Override public void performCompaction(String action, int pid) throws IOException { + mPidCompacting = pid; if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) { compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG); } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) { @@ -1493,6 +1525,7 @@ public final class CachedAppOptimizer { } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) { compactProcess(pid, COMPACT_ACTION_ANON_FLAG); } + mPidCompacting = -1; } } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 7673123c6476..88ca8a5e8356 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -2606,18 +2606,9 @@ public class OomAdjuster { // don't compact during bootup if (mCachedAppOptimizer.useCompaction() && mService.mBooted) { // Cached and prev/home compaction + // reminder: here, setAdj is previous state, curAdj is upcoming state if (state.getCurAdj() != state.getSetAdj()) { - // Perform a minor compaction when a perceptible app becomes the prev/home app - // Perform a major compaction when any app enters cached - // reminder: here, setAdj is previous state, curAdj is upcoming state - if (state.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ - && (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ - || state.getCurAdj() == ProcessList.HOME_APP_ADJ)) { - mCachedAppOptimizer.compactAppSome(app); - } else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ - && state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) { - mCachedAppOptimizer.compactAppFull(app); - } + mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app); } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE && state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ // Because these can fire independent of oom_adj/procstate changes, we need diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 7cf60e8fedb2..2e02e4977094 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -8422,7 +8422,7 @@ public class AudioService extends IAudioService.Stub /** @see Spatializer#getSpatializerCompatibleAudioDevices() */ public @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() { - enforceModifyAudioRoutingPermission(); + enforceModifyDefaultAudioEffectsPermission(); return mSpatializerHelper.getCompatibleAudioDevices(); } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index e8a3a8150377..185c0231b534 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -796,6 +796,9 @@ public class PreferencesHelper implements RankingConfig { if (r == null) { throw new IllegalArgumentException("Invalid package"); } + if (fromTargetApp) { + group.setBlocked(false); + } final NotificationChannelGroup oldGroup = r.groups.get(group.getId()); if (oldGroup != null) { group.setChannels(oldGroup.getChannels()); diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java index 0a69aec76306..449fae13f137 100644 --- a/services/core/java/com/android/server/notification/VibratorHelper.java +++ b/services/core/java/com/android/server/notification/VibratorHelper.java @@ -19,6 +19,7 @@ package com.android.server.notification; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.media.AudioAttributes; import android.os.Process; import android.os.VibrationAttributes; @@ -39,18 +40,16 @@ public final class VibratorHelper { private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps - private static final int CHIRP_LEVEL_DURATION_MILLIS = 100; - private static final int DEFAULT_CHIRP_RAMP_DURATION_MILLIS = 100; - private static final int FALLBACK_CHIRP_RAMP_DURATION_MILLIS = 50; private final Vibrator mVibrator; private final long[] mDefaultPattern; private final long[] mFallbackPattern; + @Nullable private final float[] mDefaultPwlePattern; + @Nullable private final float[] mFallbackPwlePattern; public VibratorHelper(Context context) { mVibrator = context.getSystemService(Vibrator.class); - mDefaultPattern = getLongArray( - context.getResources(), + mDefaultPattern = getLongArray(context.getResources(), com.android.internal.R.array.config_defaultNotificationVibePattern, VIBRATE_PATTERN_MAXLEN, DEFAULT_VIBRATE_PATTERN); @@ -58,6 +57,10 @@ public final class VibratorHelper { R.array.config_notificationFallbackVibePattern, VIBRATE_PATTERN_MAXLEN, DEFAULT_VIBRATE_PATTERN); + mDefaultPwlePattern = getFloatArray(context.getResources(), + com.android.internal.R.array.config_defaultNotificationVibeWaveform); + mFallbackPwlePattern = getFloatArray(context.getResources(), + com.android.internal.R.array.config_notificationFallbackVibeWaveform); } /** @@ -83,6 +86,50 @@ public final class VibratorHelper { } /** + * Safely create a {@link VibrationEffect} from given waveform description. + * + * <p>The waveform is described by a sequence of values for target amplitude, frequency and + * duration, that are forwarded to + * {@link VibrationEffect.WaveformBuilder#addRamp(float, float, int)}. + * + * <p>This method returns {@code null} if the pattern is also {@code null} or invalid. + * + * @param values The list of values describing the waveform as a sequence of target amplitude, + * frequency and duration. + * @param insistent {@code true} if the vibration should loop until it is cancelled. + */ + @Nullable + public static VibrationEffect createPwleWaveformVibration(@Nullable float[] values, + boolean insistent) { + try { + if (values == null) { + return null; + } + + int length = values.length; + // The waveform is described by triples (amplitude, frequency, duration) + if ((length == 0) || (length % 3 != 0)) { + return null; + } + + VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform(); + for (int i = 0; i < length; i += 3) { + waveformBuilder.addRamp(/* amplitude= */ values[i], /* frequency= */ values[i + 1], + /* duration= */ (int) values[i + 2]); + } + + if (insistent) { + return waveformBuilder.build(/* repeat= */ 0); + } + return waveformBuilder.build(); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Error creating vibration PWLE waveform with pattern: " + + Arrays.toString(values)); + } + return null; + } + + /** * Vibrate the device with given {@code effect}. * * <p>We need to vibrate as "android" so we can breakthrough DND. @@ -106,7 +153,10 @@ public final class VibratorHelper { */ public VibrationEffect createFallbackVibration(boolean insistent) { if (mVibrator.hasFrequencyControl()) { - return createChirpVibration(FALLBACK_CHIRP_RAMP_DURATION_MILLIS, insistent); + VibrationEffect effect = createPwleWaveformVibration(mFallbackPwlePattern, insistent); + if (effect != null) { + return effect; + } } return createWaveformVibration(mFallbackPattern, insistent); } @@ -118,29 +168,29 @@ public final class VibratorHelper { */ public VibrationEffect createDefaultVibration(boolean insistent) { if (mVibrator.hasFrequencyControl()) { - return createChirpVibration(DEFAULT_CHIRP_RAMP_DURATION_MILLIS, insistent); + VibrationEffect effect = createPwleWaveformVibration(mDefaultPwlePattern, insistent); + if (effect != null) { + return effect; + } } return createWaveformVibration(mDefaultPattern, insistent); } - private static VibrationEffect createChirpVibration(int rampDuration, boolean insistent) { - VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform() - .addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0) - .addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, rampDuration) - .addStep(/* amplitude= */ 1, /* frequency= */ -0.25f, CHIRP_LEVEL_DURATION_MILLIS) - .addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, rampDuration); - - if (insistent) { - return waveformBuilder - .addStep(/* amplitude= */ 0, CHIRP_LEVEL_DURATION_MILLIS) - .build(/* repeat= */ 0); + @Nullable + private static float[] getFloatArray(Resources resources, int resId) { + TypedArray array = resources.obtainTypedArray(resId); + try { + float[] values = new float[array.length()]; + for (int i = 0; i < values.length; i++) { + values[i] = array.getFloat(i, Float.NaN); + if (Float.isNaN(values[i])) { + return null; + } + } + return values; + } finally { + array.recycle(); } - - VibrationEffect singleBeat = waveformBuilder.build(); - return VibrationEffect.startComposition() - .addEffect(singleBeat) - .addEffect(singleBeat, /* delay= */ CHIRP_LEVEL_DURATION_MILLIS) - .compose(); } private static long[] getLongArray(Resources resources, int resId, int maxLength, long[] def) { diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java index ef0079e0c01f..ca675973b2fd 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java @@ -35,7 +35,6 @@ import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; - import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils; import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils; @@ -313,12 +312,12 @@ public final class PowerStatsLogger extends Handler { return mStartWallTime; } - public PowerStatsLogger(Context context, File dataStoragePath, + public PowerStatsLogger(Context context, Looper looper, File dataStoragePath, String meterFilename, String meterCacheFilename, String modelFilename, String modelCacheFilename, String residencyFilename, String residencyCacheFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { - super(Looper.getMainLooper()); + super(looper); mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime(); if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime); mPowerStatsHALWrapper = powerStatsHALWrapper; diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index bb52c1dc1a29..9953ca8f9b65 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -28,6 +28,7 @@ import android.os.Binder; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; +import android.os.Looper; import android.os.UserHandle; import android.power.PowerStatsInternal; import android.util.Slog; @@ -79,6 +80,9 @@ public class PowerStatsService extends SystemService { private StatsPullAtomCallbackImpl mPullAtomCallback; @Nullable private PowerStatsInternal mPowerStatsInternal; + @Nullable + @GuardedBy("this") + private Looper mLooper; @VisibleForTesting static class Injector { @@ -127,12 +131,12 @@ public class PowerStatsService extends SystemService { } } - PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, - String meterFilename, String meterCacheFilename, + PowerStatsLogger createPowerStatsLogger(Context context, Looper looper, + File dataStoragePath, String meterFilename, String meterCacheFilename, String modelFilename, String modelCacheFilename, String residencyFilename, String residencyCacheFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { - return new PowerStatsLogger(context, dataStoragePath, + return new PowerStatsLogger(context, looper, dataStoragePath, meterFilename, meterCacheFilename, modelFilename, modelCacheFilename, residencyFilename, residencyCacheFilename, @@ -229,11 +233,11 @@ public class PowerStatsService extends SystemService { mDataStoragePath = mInjector.createDataStoragePath(); // Only start logger and triggers if initialization is successful. - mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mDataStoragePath, - mInjector.createMeterFilename(), mInjector.createMeterCacheFilename(), - mInjector.createModelFilename(), mInjector.createModelCacheFilename(), - mInjector.createResidencyFilename(), mInjector.createResidencyCacheFilename(), - getPowerStatsHal()); + mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, getLooper(), + mDataStoragePath, mInjector.createMeterFilename(), + mInjector.createMeterCacheFilename(), mInjector.createModelFilename(), + mInjector.createModelCacheFilename(), mInjector.createResidencyFilename(), + mInjector.createResidencyCacheFilename(), getPowerStatsHal()); mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger); mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger); } else { @@ -245,6 +249,17 @@ public class PowerStatsService extends SystemService { return mInjector.getPowerStatsHALWrapperImpl(); } + private Looper getLooper() { + synchronized (this) { + if (mLooper == null) { + HandlerThread thread = new HandlerThread(TAG); + thread.start(); + return thread.getLooper(); + } + return mLooper; + } + } + public PowerStatsService(Context context) { this(context, new Injector()); } @@ -260,9 +275,7 @@ public class PowerStatsService extends SystemService { private final Handler mHandler; LocalService() { - HandlerThread thread = new HandlerThread(TAG); - thread.start(); - mHandler = new Handler(thread.getLooper()); + mHandler = new Handler(getLooper()); } diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index 212a036f15c2..e8490c5873ef 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -174,7 +174,7 @@ final class LetterboxConfiguration { * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0, * both it and a value of {@link * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and - * and corners of the activity won't be rounded. + * corners of the activity won't be rounded. */ void setLetterboxActivityCornersRadius(int cornersRadius) { mLetterboxActivityCornersRadius = cornersRadius; diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 7d073573835f..8866343afe03 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -150,7 +150,7 @@ final class LetterboxUiController { if (mLetterbox == null) { mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null), mActivityRecord.mWmService.mTransactionFactory, - mLetterboxConfiguration::isLetterboxActivityCornersRounded, + this::shouldLetterboxHaveRoundedCorners, this::getLetterboxBackgroundColor, this::hasWallpaperBackgroudForLetterbox, this::getLetterboxWallpaperBlurRadius, @@ -175,6 +175,13 @@ final class LetterboxUiController { } } + private boolean shouldLetterboxHaveRoundedCorners() { + // TODO(b/214030873): remove once background is drawn for transparent activities + // Letterbox shouldn't have rounded corners if the activity is transparent + return mLetterboxConfiguration.isLetterboxActivityCornersRounded() + && mActivityRecord.fillsParent(); + } + float getHorizontalPositionMultiplier(Configuration parentConfiguration) { // Don't check resolved configuration because it may not be updated yet during // configuration change. @@ -257,8 +264,6 @@ final class LetterboxUiController { @VisibleForTesting boolean shouldShowLetterboxUi(WindowState mainWindow) { return isSurfaceReadyAndVisible(mainWindow) && mainWindow.areAppWindowBoundsLetterboxed() - // Check that an activity isn't transparent. - && mActivityRecord.fillsParent() // Check for FLAG_SHOW_WALLPAPER explicitly instead of using // WindowContainer#showWallpaper because the later will return true when this // activity is using blurred wallpaper for letterbox backgroud. diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 4190a91710fc..94bc22a05d7a 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -59,6 +59,9 @@ using android::base::unique_fd; namespace android { +static bool cancelRunningCompaction; +static bool compactionInProgress; + // Legacy method for compacting processes, any new code should // use compactProcess instead. static inline void compactProcessProcfs(int pid, const std::string& compactionType) { @@ -83,9 +86,18 @@ static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseT // Skip compaction if failed to open pidfd with any error return -errno; } + compactionInProgress = true; + cancelRunningCompaction = false; int64_t totalBytesCompacted = 0; for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) { + if (CC_UNLIKELY(cancelRunningCompaction)) { + // There could be a significant delay betweenwhen a compaction + // is requested and when it is handled during this time + // our OOM adjust could have improved. + cancelRunningCompaction = false; + break; + } int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase)); for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) { vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start; @@ -95,11 +107,13 @@ static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseT auto bytesCompacted = process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0); if (CC_UNLIKELY(bytesCompacted == -1)) { + compactionInProgress = false; return -errno; } totalBytesCompacted += bytesCompacted; } + compactionInProgress = false; return totalBytesCompacted; } @@ -228,6 +242,12 @@ static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, job } } +static void com_android_server_am_CachedAppOptimizer_cancelCompaction(JNIEnv*, jobject) { + if (compactionInProgress) { + cancelRunningCompaction = true; + } +} + static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid, jint compactionFlags) { compactProcessOrFallback(pid, compactionFlags); @@ -279,6 +299,8 @@ static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIE static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ + {"cancelCompaction", "()V", + (void*)com_android_server_am_CachedAppOptimizer_cancelCompaction}, {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess}, {"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 41e605bea0a4..774a485aac6b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4003,6 +4003,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); + // System caller can query policy for a particular admin. + Preconditions.checkCallAuthorization( + who == null || isCallingFromPackage(who.getPackageName(), caller.getUid()) + || isSystemUid(caller)); synchronized (getLockObject()) { int mode = PASSWORD_QUALITY_UNSPECIFIED; @@ -4218,7 +4222,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getCallerIdentity(); + final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { @@ -4368,7 +4372,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getCallerIdentity(); + final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { @@ -4581,7 +4585,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getCallerIdentity(); + final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { @@ -4999,6 +5003,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); + // System caller can query policy for a particular admin. + Preconditions.checkCallAuthorization( + who == null || isCallingFromPackage(who.getPackageName(), caller.getUid()) + || isSystemUid(caller)); synchronized (getLockObject()) { ActiveAdmin admin = (who != null) @@ -5310,6 +5318,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); + // System caller can query policy for a particular admin. + Preconditions.checkCallAuthorization( + who == null || isCallingFromPackage(who.getPackageName(), caller.getUid()) + || isSystemUid(caller)); synchronized (getLockObject()) { if (who != null) { @@ -5387,7 +5399,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); - final CallerIdentity caller = getCallerIdentity(); + final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId)); if (!mLockPatternUtils.hasSecureLockScreen()) { @@ -7452,7 +7464,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); + Preconditions.checkCallAuthorization( + hasFullCrossUsersPermission(caller, userHandle) && isSystemUid(caller)); synchronized (getLockObject()) { DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); @@ -7730,6 +7743,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return false; } + + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); + if (parent) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId())); @@ -9950,7 +9967,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(agent, "agent null"); Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getCallerIdentity(); + final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index 26b34fdd4e04..304fe5a1c9c3 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -30,6 +30,7 @@ import android.hardware.power.stats.PowerEntity; import android.hardware.power.stats.State; import android.hardware.power.stats.StateResidency; import android.hardware.power.stats.StateResidencyResult; +import android.os.Looper; import androidx.test.InstrumentationRegistry; @@ -145,12 +146,12 @@ public class PowerStatsServiceTest { } @Override - PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, - String meterFilename, String meterCacheFilename, + PowerStatsLogger createPowerStatsLogger(Context context, Looper looper, + File dataStoragePath, String meterFilename, String meterCacheFilename, String modelFilename, String modelCacheFilename, String residencyFilename, String residencyCacheFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { - mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, + mPowerStatsLogger = new PowerStatsLogger(context, looper, dataStoragePath, meterFilename, meterCacheFilename, modelFilename, modelCacheFilename, residencyFilename, residencyCacheFilename, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 66d157708332..77612b9d8cef 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -2224,6 +2224,19 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testIsGroupBlocked_appCannotCreateAsBlocked() throws Exception { + NotificationChannelGroup group = new NotificationChannelGroup("id", "name"); + group.setBlocked(true); + mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true); + assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId())); + + NotificationChannelGroup group3 = group.clone(); + group3.setBlocked(false); + mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group3, true); + assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId())); + } + + @Test public void testIsGroup_appCannotResetBlock() throws Exception { NotificationChannelGroup group = new NotificationChannelGroup("id", "name"); mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true); @@ -3615,7 +3628,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testGetConversations_noDisabledGroups() { NotificationChannelGroup group = new NotificationChannelGroup("a", "a"); group.setBlocked(true); - mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true); + mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, false); NotificationChannel parent = new NotificationChannel("parent", "p", 1); mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java index c77a474e032c..8bc0c6c65fa3 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java @@ -40,7 +40,10 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class VibratorHelperTest extends UiServiceTestCase { + // OFF/ON vibration pattern private static final long[] CUSTOM_PATTERN = new long[] { 100, 200, 300, 400 }; + // (amplitude, frequency, duration) triples list + private static final float[] PWLE_PATTERN = new float[] { 1, 0, 100 }; @Mock private Vibrator mVibrator; @@ -58,12 +61,16 @@ public class VibratorHelperTest extends UiServiceTestCase { public void createWaveformVibration_insistent_createsRepeatingVibration() { assertRepeatingVibration( VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ true)); + assertRepeatingVibration( + VibratorHelper.createPwleWaveformVibration(PWLE_PATTERN, /* insistent= */ true)); } @Test public void createWaveformVibration_nonInsistent_createsSingleShotVibration() { assertSingleVibration( VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ false)); + assertSingleVibration( + VibratorHelper.createPwleWaveformVibration(PWLE_PATTERN, /* insistent= */ false)); } @Test @@ -71,6 +78,11 @@ public class VibratorHelperTest extends UiServiceTestCase { assertNull(VibratorHelper.createWaveformVibration(null, false)); assertNull(VibratorHelper.createWaveformVibration(new long[0], false)); assertNull(VibratorHelper.createWaveformVibration(new long[] { 0, 0 }, false)); + + assertNull(VibratorHelper.createPwleWaveformVibration(null, false)); + assertNull(VibratorHelper.createPwleWaveformVibration(new float[0], false)); + assertNull(VibratorHelper.createPwleWaveformVibration(new float[] { 0 }, false)); + assertNull(VibratorHelper.createPwleWaveformVibration(new float[] { 0, 0, 0 }, false)); } @Test |