Merge "Move to android_mallopt for malloc debug calls." into qt-dev
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 5fee2c9..8f8e4d8 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -48,8 +48,7 @@
void setIconVisibility(String slot, boolean visible);
@UnsupportedAppUsage
void removeIcon(String slot);
- // TODO(b/117478341): support back button change when IME is showing on a external display.
- void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
+ void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
void expandSettingsPanel(String subPanel);
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index eca3926..2e9b03c 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -383,7 +383,7 @@
}
if (TextUtils.isEmpty(displayText) && !airplaneMode) {
- displayText = TextUtils.join(mSeparator, carrierNames);
+ displayText = joinNotEmpty(mSeparator, carrierNames);
}
final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
displayText,
@@ -546,6 +546,25 @@
}
}
+ /**
+ * Joins the strings in a sequence using a separator. Empty strings are discarded with no extra
+ * separator added so there are no extra separators that are not needed.
+ */
+ private static CharSequence joinNotEmpty(CharSequence separator, CharSequence[] sequences) {
+ int length = sequences.length;
+ if (length == 0) return "";
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ if (!TextUtils.isEmpty(sequences[i])) {
+ if (!TextUtils.isEmpty(sb)) {
+ sb.append(separator);
+ }
+ sb.append(sequences[i]);
+ }
+ }
+ return sb.toString();
+ }
+
private static List<CharSequence> append(List<CharSequence> list, CharSequence string) {
if (!TextUtils.isEmpty(string)) {
list.add(string);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 2f99cf3..d584959 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -18,7 +18,10 @@
import static android.app.StatusBarManager.DISABLE2_NONE;
import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
@@ -40,6 +43,7 @@
import android.os.Message;
import android.util.Pair;
import android.util.SparseArray;
+import android.view.inputmethod.InputMethodSystemProperty;
import androidx.annotation.VisibleForTesting;
@@ -127,6 +131,11 @@
private Handler mHandler = new H(Looper.getMainLooper());
/** A map of display id - disable flag pair */
private SparseArray<Pair<Integer, Integer>> mDisplayDisabled = new SparseArray<>();
+ /**
+ * The last ID of the display where IME window for which we received setImeWindowStatus
+ * event.
+ */
+ private int mLastUpdatedImeDisplayId = INVALID_DISPLAY;
/**
* These methods are called back on the main thread.
@@ -785,6 +794,32 @@
}
}
+ private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher) {
+ if (displayId == INVALID_DISPLAY) return;
+
+ if (!InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED
+ && mLastUpdatedImeDisplayId != displayId
+ && mLastUpdatedImeDisplayId != INVALID_DISPLAY) {
+ // Set previous NavBar's IME window status as invisible when IME
+ // window switched to another display for single-session IME case.
+ sendImeInvisibleStatusForPrevNavBar();
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).setImeWindowStatus(displayId, token, vis, backDisposition,
+ showImeSwitcher);
+ }
+ mLastUpdatedImeDisplayId = displayId;
+ }
+
+ private void sendImeInvisibleStatusForPrevNavBar() {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).setImeWindowStatus(mLastUpdatedImeDisplayId,
+ null /* token */, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT,
+ false /* showImeSwitcher */);
+ }
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -852,10 +887,9 @@
break;
case MSG_SHOW_IME_BUTTON:
args = (SomeArgs) msg.obj;
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).setImeWindowStatus(args.argi1, (IBinder) args.arg1,
- args.argi2, args.argi3, args.argi4 != 0 /* showImeSwitcher */);
- }
+ handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
+ args.argi2 /* vis */, args.argi3 /* backDisposition */,
+ args.argi4 != 0 /* showImeSwitcher */);
break;
case MSG_SHOW_RECENT_APPS:
for (int i = 0; i < mCallbacks.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 4d2b56c..6c1a4fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -1063,4 +1063,9 @@
context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
return navigationBarView;
}
+
+ @VisibleForTesting
+ int getNavigationIconHints() {
+ return mNavigationIconHints;
+ }
}
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 a381bbc..3f33ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3926,6 +3926,10 @@
mScrimController.setWakeLockScreenSensorActive(true);
}
+ if (reason == DozeLog.PULSE_REASON_DOCKING && mStatusBarWindow != null) {
+ mStatusBarWindow.suppressWakeUpGesture(true);
+ }
+
boolean passiveAuthInterrupt = reason == DozeLog.PULSE_REASON_NOTIFICATION;
// Set the state to pulsing, so ScrimController will know what to do once we ask it to
// execute the transition. The pulse callback will then be invoked when the scrims
@@ -3945,6 +3949,9 @@
callback.onPulseFinished();
updateNotificationPanelTouchState();
mScrimController.setWakeLockScreenSensorActive(false);
+ if (mStatusBarWindow != null) {
+ mStatusBarWindow.suppressWakeUpGesture(false);
+ }
setPulsing(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 28db28c..44996ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -107,12 +107,13 @@
private boolean mTouchActive;
private boolean mExpandAnimationRunning;
private boolean mExpandAnimationPending;
+ private boolean mSuppressingWakeUpGesture;
private final GestureDetector.SimpleOnGestureListener mGestureListener =
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
- if (mSingleTapEnabled) {
+ if (mSingleTapEnabled && !mSuppressingWakeUpGesture) {
mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this,
"SINGLE_TAP");
return true;
@@ -327,6 +328,10 @@
mTouchActive = touchActive;
}
+ void suppressWakeUpGesture(boolean suppress) {
+ mSuppressingWakeUpGesture = suppress;
+ }
+
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index df534d7..9f91a17 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -104,7 +104,8 @@
mCarrierTextCallbackInfo = new CarrierTextController.CarrierTextCallbackInfo("",
new CharSequence[]{}, false, new int[]{});
- when(mTelephonyManager.getPhoneCount()).thenReturn(2);
+ when(mTelephonyManager.getPhoneCount()).thenReturn(3);
+
mCarrierTextController = new TestCarrierTextController(mContext, SEPARATOR, true, true,
mKeyguardUpdateMonitor);
// This should not start listening on any of the real dependencies
@@ -130,6 +131,12 @@
reset(mCarrierTextCallback);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
new ArrayList<>());
+
+ // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
+ // same answer as KeyguardUpdateMonitor. Remove when this is addressed
+ when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(
+ new ArrayList<>());
+
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(
IccCardConstants.State.CARD_IO_ERROR);
// This should not produce an out of bounds error, even though there are no subscriptions
@@ -173,7 +180,11 @@
list.add(TEST_SUBSCRIPTION);
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+
+ // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
+ // same answer as KeyguardUpdateMonitor. Remove when this is addressed
when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
+
mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -197,7 +208,11 @@
list.add(TEST_SUBSCRIPTION_ROAMING);
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+
+ // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
+ // same answer as KeyguardUpdateMonitor. Remove when this is addressed
when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
+
mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -219,6 +234,12 @@
reset(mCarrierTextCallback);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
new ArrayList<>());
+
+ // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
+ // same answer as KeyguardUpdateMonitor. Remove when this is addressed
+ when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(
+ new ArrayList<>());
+
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
@@ -233,6 +254,121 @@
}
+ @Test
+ public void testCarrierText_twoValidSubscriptions() {
+ reset(mCarrierTextCallback);
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION);
+ list.add(TEST_SUBSCRIPTION);
+ when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
+ when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+
+ // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
+ // same answer as KeyguardUpdateMonitor. Remove when this is addressed
+ when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
+
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
+ ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextController.CarrierTextCallbackInfo.class);
+
+ mCarrierTextController.updateCarrierText();
+ mTestableLooper.processAllMessages();
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+
+ assertEquals(TEST_CARRIER + SEPARATOR + TEST_CARRIER,
+ captor.getValue().carrierText);
+ }
+
+ @Test
+ public void testCarrierText_oneDisabledSub() {
+ reset(mCarrierTextCallback);
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION);
+ list.add(TEST_SUBSCRIPTION);
+ when(mKeyguardUpdateMonitor.getSimState(anyInt()))
+ .thenReturn(IccCardConstants.State.READY)
+ .thenReturn(IccCardConstants.State.NOT_READY);
+ when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+
+ // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
+ // same answer as KeyguardUpdateMonitor. Remove when this is addressed
+ when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
+
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
+ ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextController.CarrierTextCallbackInfo.class);
+
+ mCarrierTextController.updateCarrierText();
+ mTestableLooper.processAllMessages();
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+
+ assertEquals(TEST_CARRIER,
+ captor.getValue().carrierText);
+ }
+
+ @Test
+ public void testCarrierText_firstDisabledSub() {
+ reset(mCarrierTextCallback);
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION);
+ list.add(TEST_SUBSCRIPTION);
+ when(mKeyguardUpdateMonitor.getSimState(anyInt()))
+ .thenReturn(IccCardConstants.State.NOT_READY)
+ .thenReturn(IccCardConstants.State.READY);
+ when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+
+ // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
+ // same answer as KeyguardUpdateMonitor. Remove when this is addressed
+ when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
+
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
+ ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextController.CarrierTextCallbackInfo.class);
+
+ mCarrierTextController.updateCarrierText();
+ mTestableLooper.processAllMessages();
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+
+ assertEquals(TEST_CARRIER,
+ captor.getValue().carrierText);
+ }
+
+ @Test
+ public void testCarrierText_threeSubsMiddleDisabled() {
+ reset(mCarrierTextCallback);
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION);
+ list.add(TEST_SUBSCRIPTION);
+ list.add(TEST_SUBSCRIPTION);
+ when(mKeyguardUpdateMonitor.getSimState(anyInt()))
+ .thenReturn(IccCardConstants.State.READY)
+ .thenReturn(IccCardConstants.State.NOT_READY)
+ .thenReturn(IccCardConstants.State.READY);
+ when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
+ // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
+ // same answer as KeyguardUpdateMonitor. Remove when this is addressed
+ when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
+
+ ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextController.CarrierTextCallbackInfo.class);
+
+ mCarrierTextController.updateCarrierText();
+ mTestableLooper.processAllMessages();
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+
+ assertEquals(TEST_CARRIER + SEPARATOR + TEST_CARRIER,
+ captor.getValue().carrierText);
+ }
+
public static class TestCarrierTextController extends CarrierTextController {
private KeyguardUpdateMonitor mKUM;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 6a0d61d..3ae57e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -14,18 +14,38 @@
package com.android.systemui.statusbar.phone;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.LayoutRes;
+import android.annotation.Nullable;
import android.app.Fragment;
+import android.app.FragmentController;
+import android.app.FragmentHostCallback;
import android.content.Context;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.LeakCheck.Tracker;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
@@ -34,6 +54,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.SysuiTestableContext;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
@@ -50,9 +71,16 @@
@RunWithLooper()
@SmallTest
public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
+ private static final int EXTERNAL_DISPLAY_ID = 2;
+ private static final int NAV_BAR_VIEW_ID = 43;
+ private Fragment mFragmentExternalDisplay;
+ private FragmentController mControllerExternalDisplay;
+
+ private SysuiTestableContext mSysuiTestableContextExternal;
private OverviewProxyService mOverviewProxyService =
mDependency.injectMockDependency(OverviewProxyService.class);
+ private CommandQueue mCommandQueue;
private AccessibilityManagerWrapper mAccessibilityWrapper =
new AccessibilityManagerWrapper(mContext) {
Tracker mTracker = mLeakCheck.getTracker("accessibility_manager");
@@ -73,15 +101,51 @@
}
protected void createRootView() {
- mView = new NavigationBarFrame(mContext);
+ mView = new NavigationBarFrame(mSysuiContext);
+ mView.setId(NAV_BAR_VIEW_ID);
}
@Before
- public void setup() {
- mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+ public void setupFragment() throws Exception {
+ setupSysuiDependency();
+ createRootView();
+ TestableLooper.get(this).runWithLooper(() -> {
+ mHandler = new Handler();
+
+ mFragment = instantiate(mSysuiContext, NavigationBarFragment.class.getName(), null);
+ mFragments = FragmentController.createController(
+ new HostCallbacksForExternalDisplay(mSysuiContext));
+ mFragments.attachHost(null);
+ mFragments.getFragmentManager().beginTransaction()
+ .replace(NAV_BAR_VIEW_ID, mFragment)
+ .commit();
+ mControllerExternalDisplay = FragmentController.createController(
+ new HostCallbacksForExternalDisplay(mSysuiTestableContextExternal));
+ mControllerExternalDisplay.attachHost(null);
+ mFragmentExternalDisplay = instantiate(mSysuiTestableContextExternal,
+ NavigationBarFragment.class.getName(), null);
+ mControllerExternalDisplay.getFragmentManager().beginTransaction()
+ .replace(NAV_BAR_VIEW_ID, mFragmentExternalDisplay)
+ .commit();
+ });
+ }
+
+ private void setupSysuiDependency() {
+ mCommandQueue = new CommandQueue(mContext);
+ mSysuiContext.putComponent(CommandQueue.class, mCommandQueue);
mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
mSysuiContext.putComponent(Recents.class, mock(Recents.class));
mSysuiContext.putComponent(Divider.class, mock(Divider.class));
+
+ Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID,
+ new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
+ mSysuiTestableContextExternal = (SysuiTestableContext) mSysuiContext.createDisplayContext(
+ display);
+ mSysuiTestableContextExternal.putComponent(CommandQueue.class, mCommandQueue);
+ mSysuiTestableContextExternal.putComponent(StatusBar.class, mock(StatusBar.class));
+ mSysuiTestableContextExternal.putComponent(Recents.class, mock(Recents.class));
+ mSysuiTestableContextExternal.putComponent(Divider.class, mock(Divider.class));
+
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
WindowManager windowManager = mock(WindowManager.class);
Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
@@ -102,15 +166,111 @@
navigationBarFragment.onHomeLongClick(navigationBarFragment.getView());
}
+ @Test
+ public void testSetImeWindowStatusWhenImeSwitchOnDisplay() {
+ // Create default & external NavBar fragment.
+ NavigationBarFragment defaultNavBar = (NavigationBarFragment) mFragment;
+ NavigationBarFragment externalNavBar = (NavigationBarFragment) mFragmentExternalDisplay;
+ mFragments.dispatchCreate();
+ processAllMessages();
+ mFragments.dispatchResume();
+ processAllMessages();
+ mControllerExternalDisplay.dispatchCreate();
+ processAllMessages();
+ mControllerExternalDisplay.dispatchResume();
+ processAllMessages();
+
+ // Set IME window status for default NavBar.
+ mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, true);
+ Handler.getMain().runWithScissors(() -> { }, 500);
+
+ // Verify IME window state will be updated in default NavBar & external NavBar state reset.
+ assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
+ defaultNavBar.getNavigationIconHints());
+ assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+ assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+
+ // Set IME window status for external NavBar.
+ mCommandQueue.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null,
+ IME_VISIBLE, BACK_DISPOSITION_DEFAULT, true);
+ Handler.getMain().runWithScissors(() -> { }, 500);
+
+ // Verify IME window state will be updated in external NavBar & default NavBar state reset.
+ assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
+ externalNavBar.getNavigationIconHints());
+ assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+ assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ }
+
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
DeviceProvisionedController deviceProvisionedController =
mock(DeviceProvisionedController.class);
assertNotNull(mAccessibilityWrapper);
- return new NavigationBarFragment(mAccessibilityWrapper,
+ return new NavigationBarFragment(
+ context.getDisplayId() == DEFAULT_DISPLAY ? mAccessibilityWrapper
+ : mock(AccessibilityManagerWrapper.class),
deviceProvisionedController,
new MetricsLogger(),
- new AssistManager(deviceProvisionedController, mContext),
+ mock(AssistManager.class),
mOverviewProxyService);
}
+
+ private class HostCallbacksForExternalDisplay extends
+ FragmentHostCallback<NavigationBarFragmentTest> {
+ private Context mDisplayContext;
+
+ HostCallbacksForExternalDisplay(Context context) {
+ super(context, mHandler, 0);
+ mDisplayContext = context;
+ }
+
+ @Override
+ public NavigationBarFragmentTest onGetHost() {
+ return NavigationBarFragmentTest.this;
+ }
+
+ @Override
+ public Fragment instantiate(Context context, String className, Bundle arguments) {
+ return NavigationBarFragmentTest.this.instantiate(context, className, arguments);
+ }
+
+ @Override
+ public View onFindViewById(int id) {
+ return mView.findViewById(id);
+ }
+
+ @Override
+ public LayoutInflater onGetLayoutInflater() {
+ return new LayoutInflaterWrapper(mDisplayContext);
+ }
+ }
+
+ private static class LayoutInflaterWrapper extends LayoutInflater {
+ protected LayoutInflaterWrapper(Context context) {
+ super(context);
+ }
+
+ @Override
+ public LayoutInflater cloneInContext(Context newContext) {
+ return null;
+ }
+
+ @Override
+ public View inflate(@LayoutRes int resource, @Nullable ViewGroup root,
+ boolean attachToRoot) {
+ NavigationBarView view = mock(NavigationBarView.class);
+ when(view.getDisplay()).thenReturn(mContext.getDisplay());
+ when(view.getBackButton()).thenReturn(mock(ButtonDispatcher.class));
+ when(view.getHomeButton()).thenReturn(mock(ButtonDispatcher.class));
+ when(view.getRecentsButton()).thenReturn(mock(ButtonDispatcher.class));
+ when(view.getAccessibilityButton()).thenReturn(mock(ButtonDispatcher.class));
+ when(view.getRotateSuggestionButton()).thenReturn(mock(RotationContextButton.class));
+ when(view.getBarTransitions()).thenReturn(mock(BarTransitions.class));
+ when(view.getLightTransitionsController()).thenReturn(
+ mock(LightBarTransitionsController.class));
+ return view;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 43bc21b..fb16465 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -681,6 +681,25 @@
}
@Test
+ public void testPulseWhileDozingWithDockingReason_suppressWakeUpGesture() {
+ // Keep track of callback to be able to stop the pulse
+ final DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
+ doAnswer(invocation -> {
+ pulseCallback[0] = invocation.getArgument(0);
+ return null;
+ }).when(mDozeScrimController).pulse(any(), anyInt());
+
+ // Starting a pulse while docking should suppress wakeup gesture
+ mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
+ DozeLog.PULSE_REASON_DOCKING);
+ verify(mStatusBarWindowView).suppressWakeUpGesture(eq(true));
+
+ // Ending a pulse should restore wakeup gesture
+ pulseCallback[0].onPulseFinished();
+ verify(mStatusBarWindowView).suppressWakeUpGesture(eq(false));
+ }
+
+ @Test
public void testSetState_changesIsFullScreenUserSwitcherState() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
assertFalse(mStatusBar.isFullScreenUserSwitcherState());
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3c97c39..c2aade3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -575,6 +575,12 @@
*/
int mCurTokenDisplayId = INVALID_DISPLAY;
+ /**
+ * The display ID of the input method indicates the fallback display which returned by
+ * {@link #computeImeDisplayIdForTarget}.
+ */
+ private static final int FALLBACK_DISPLAY_ID = DEFAULT_DISPLAY;
+
final ImeDisplayValidator mImeDisplayValidator;
/**
@@ -625,7 +631,8 @@
* currently invisible.
* </dd>
* </dl>
- * <em>Do not update this value outside of setImeWindowStatus.</em>
+ * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
+ * {@link #unbindCurrentMethodLocked()}.</em>
*/
int mImeWindowVis;
@@ -2124,12 +2131,10 @@
*/
static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
- // We always assume that the default display id suitable to show the IME window.
- return DEFAULT_DISPLAY;
+ return FALLBACK_DISPLAY_ID;
}
- // Show IME in default display when the display with IME target doesn't support system
- // decorations.
- return checker.displayCanShowIme(displayId) ? displayId : DEFAULT_DISPLAY;
+ // Show IME window on fallback display when the display is not allowed.
+ return checker.displayCanShowIme(displayId) ? displayId : FALLBACK_DISPLAY_ID;
}
@Override
@@ -2198,6 +2203,10 @@
mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId);
} catch (RemoteException e) {
}
+ // Set IME window status as invisible when unbind current method.
+ mImeWindowVis = 0;
+ mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
+ updateSystemUiLocked(mImeWindowVis, mBackDisposition);
mCurToken = null;
mCurTokenDisplayId = INVALID_DISPLAY;
}
@@ -2399,10 +2408,20 @@
@BinderThread
@SuppressWarnings("deprecation")
private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) {
+ final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId();
+
synchronized (mMethodMap) {
if (!calledWithValidTokenLocked(token)) {
return;
}
+ // Skip update IME status when current token display is not same as focused display.
+ // Note that we still need to update IME status when focusing external display
+ // that does not support system decoration and fallback to show IME on default
+ // display since it is intentional behavior.
+ if (mCurTokenDisplayId != topFocusedDisplayId
+ && mCurTokenDisplayId != FALLBACK_DISPLAY_ID) {
+ return;
+ }
mImeWindowVis = vis;
mBackDisposition = backDisposition;
updateSystemUiLocked(vis, backDisposition);
@@ -2447,7 +2466,8 @@
if (DEBUG) {
Slog.d(TAG, "IME window vis: " + vis
+ " active: " + (vis & InputMethodService.IME_ACTIVE)
- + " inv: " + (vis & InputMethodService.IME_INVISIBLE));
+ + " inv: " + (vis & InputMethodService.IME_INVISIBLE)
+ + " displayId: " + mCurTokenDisplayId);
}
// TODO: Move this clearing calling identity block to setImeWindowStatus after making sure
@@ -2461,7 +2481,7 @@
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
if (mStatusBar != null) {
- mStatusBar.setImeWindowStatus(mCurToken, vis, backDisposition,
+ mStatusBar.setImeWindowStatus(mCurTokenDisplayId, mCurToken, vis, backDisposition,
needsToShowImeSwitcher);
}
final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 8578bb7..a349d87 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -248,7 +248,8 @@
scheduleOpTimeOutLocked();
final Intent intent = new Intent().setComponent(job.getServiceComponent());
boolean binding = mContext.bindServiceAsUser(intent, this,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+ | Context.BIND_NOT_VISIBLE | Context.BIND_ADJUST_BELOW_PERCEPTIBLE,
new UserHandle(job.getUserId()));
if (!binding) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index aaf3df3..9cbf00b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -843,10 +843,9 @@
}
}
- // TODO(b/117478341): support back button change when IME is showing on a external display.
@Override
- public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
- final boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, final IBinder token, final int vis,
+ final int backDisposition, final boolean showImeSwitcher) {
enforceStatusBar();
if (SPEW) {
@@ -857,18 +856,13 @@
// In case of IME change, we need to call up setImeWindowStatus() regardless of
// mImeWindowVis because mImeWindowVis may not have been set to false when the
// previous IME was destroyed.
- // TODO(b/117478341): support back button change when IME is showing on a external
- // display.
- getUiState(DEFAULT_DISPLAY)
- .setImeWindowState(vis, backDisposition, showImeSwitcher, token);
+ getUiState(displayId).setImeWindowState(vis, backDisposition, showImeSwitcher, token);
mHandler.post(() -> {
if (mBar == null) return;
try {
- // TODO(b/117478341): support back button change when IME is showing on a
- // external display.
mBar.setImeWindowStatus(
- DEFAULT_DISPLAY, token, vis, backDisposition, showImeSwitcher);
+ displayId, token, vis, backDisposition, showImeSwitcher);
} catch (RemoteException ex) { }
});
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 76b0351..4ed07c3 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -4089,11 +4089,14 @@
// The activity that we are finishing may be over the lock screen. In this case, we do not
// want to consider activities that cannot be shown on the lock screen as running and should
// proceed with finishing the activity if there is no valid next top running activity.
+ // Note that if this finishing activity is floating task, we don't need to wait the
+ // next activity resume and can destroy it directly.
final ActivityDisplay display = getDisplay();
final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */);
+ final boolean isFloating = r.getConfiguration().windowConfiguration.tasksAreFloating();
if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
- && next != null && !next.nowVisible) {
+ && next != null && !next.nowVisible && !isFloating) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 9d80425..78c5dbd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -476,6 +476,11 @@
public abstract int getDisplayIdForWindow(IBinder windowToken);
/**
+ * @return The top focused display ID.
+ */
+ public abstract int getTopFocusedDisplayId();
+
+ /**
* Checks whether this display should support showing system decorations.
*/
public abstract boolean shouldShowSystemDecorOnDisplay(int displayId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8ed2a15..d46aa7b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7340,6 +7340,13 @@
}
@Override
+ public int getTopFocusedDisplayId() {
+ synchronized (mGlobalLock) {
+ return mRoot.getTopFocusedDisplayContent().getDisplayId();
+ }
+ }
+
+ @Override
public boolean shouldShowSystemDecorOnDisplay(int displayId) {
synchronized (mGlobalLock) {
return WindowManagerService.this.shouldShowSystemDecors(displayId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8e18683..4105487 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2861,6 +2861,13 @@
}
mDestroying = false;
destroyedSomething = true;
+
+ // Since mDestroying will affect AppWindowToken#allDrawn, we need to perform another
+ // traversal in case we are waiting on this window to start the transition.
+ if (getDisplayContent().mAppTransition.isTransitionSet()
+ && getDisplayContent().mOpeningApps.contains(mAppToken)) {
+ mWmService.mWindowPlacerLocked.requestTraversal();
+ }
}
return destroyedSomething;
diff --git a/tests/testables/src/android/testing/BaseFragmentTest.java b/tests/testables/src/android/testing/BaseFragmentTest.java
index 9f60cce..6cd88b5 100644
--- a/tests/testables/src/android/testing/BaseFragmentTest.java
+++ b/tests/testables/src/android/testing/BaseFragmentTest.java
@@ -52,7 +52,7 @@
private static final int VIEW_ID = 42;
private final Class<? extends Fragment> mCls;
- private Handler mHandler;
+ protected Handler mHandler;
protected FrameLayout mView;
protected FragmentController mFragments;
protected Fragment mFragment;