Merge "Prevent recomputeDisableFlags from clobbering the system disable flags" into sc-v2-dev
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 17bb797..19a1d7a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -29,6 +29,7 @@
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
@@ -320,7 +321,7 @@
@UnsupportedAppUsage
private ContextImpl mSystemContext;
- private ContextImpl mSystemUiContext;
+ private final SparseArray<ContextImpl> mDisplaySystemUiContexts = new SparseArray<>();
@UnsupportedAppUsage
static volatile IPackageManager sPackageManager;
@@ -2633,22 +2634,26 @@
}
@Override
+ @NonNull
public ContextImpl getSystemUiContext() {
- synchronized (this) {
- if (mSystemUiContext == null) {
- mSystemUiContext = ContextImpl.createSystemUiContext(getSystemContext());
- }
- return mSystemUiContext;
- }
+ return getSystemUiContext(DEFAULT_DISPLAY);
}
/**
- * Create the context instance base on system resources & display information which used for UI.
+ * Gets the context instance base on system resources & display information which used for UI.
* @param displayId The ID of the display where the UI is shown.
* @see ContextImpl#createSystemUiContext(ContextImpl, int)
*/
- public ContextImpl createSystemUiContext(int displayId) {
- return ContextImpl.createSystemUiContext(getSystemUiContext(), displayId);
+ @NonNull
+ public ContextImpl getSystemUiContext(int displayId) {
+ synchronized (this) {
+ ContextImpl systemUiContext = mDisplaySystemUiContexts.get(displayId);
+ if (systemUiContext == null) {
+ systemUiContext = ContextImpl.createSystemUiContext(getSystemContext(), displayId);
+ mDisplaySystemUiContexts.put(displayId, systemUiContext);
+ }
+ return systemUiContext;
+ }
}
public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
@@ -3767,7 +3772,7 @@
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
- if (id != Display.DEFAULT_DISPLAY) {
+ if (id != DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getResources());
appContext = (ContextImpl) appContext.createDisplayContext(display);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b5ed171..e76c139 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2618,7 +2618,10 @@
overrideConfig, display.getDisplayAdjustments().getCompatibilityInfo(),
mResources.getLoaders()));
context.mDisplay = display;
- context.mContextType = CONTEXT_TYPE_DISPLAY_CONTEXT;
+ // Inherit context type if the container is from System or System UI context to bypass
+ // UI context check.
+ context.mContextType = mContextType == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
+ ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI : CONTEXT_TYPE_DISPLAY_CONTEXT;
// Display contexts and any context derived from a display context should always override
// the display that would otherwise be inherited from mToken (or the global configuration if
// mToken is null).
@@ -2671,7 +2674,8 @@
// Step 2. Create the base context of the window context, it will also create a Resources
// associated with the WindowTokenClient and set the token to the base context.
- final ContextImpl windowContextBase = createWindowContextBase(windowTokenClient, display);
+ final ContextImpl windowContextBase = createWindowContextBase(windowTokenClient,
+ display.getDisplayId());
// Step 3. Create a WindowContext instance and set it as the outer context of the base
// context to make the service obtained by #getSystemService(String) able to query
@@ -2696,9 +2700,7 @@
if (display == null) {
throw new IllegalArgumentException("Display must not be null");
}
- final ContextImpl tokenContext = createWindowContextBase(token, display);
- tokenContext.setResources(createWindowContextResources(tokenContext));
- return tokenContext;
+ return createWindowContextBase(token, display.getDisplayId());
}
/**
@@ -2706,13 +2708,13 @@
* window.
*
* @param token The token to associate with {@link Resources}
- * @param display The {@link Display} to associate with.
+ * @param displayId The ID of {@link Display} to associate with.
*
* @see #createWindowContext(Display, int, Bundle)
* @see #createTokenContext(IBinder, Display)
*/
@UiContext
- ContextImpl createWindowContextBase(@NonNull IBinder token, @NonNull Display display) {
+ ContextImpl createWindowContextBase(@NonNull IBinder token, int displayId) {
ContextImpl baseContext = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
mAttributionSource.getAttributionTag(),
mAttributionSource.getNext(),
@@ -2726,8 +2728,8 @@
baseContext.setResources(windowContextResources);
// Associate the display with window context resources so that configuration update from
// the server side will also apply to the display's metrics.
- baseContext.mDisplay = ResourcesManager.getInstance()
- .getAdjustedDisplay(display.getDisplayId(), windowContextResources);
+ baseContext.mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(displayId,
+ windowContextResources);
return baseContext;
}
@@ -2963,6 +2965,18 @@
mContentCaptureOptions = options;
}
+ @Override
+ protected void finalize() throws Throwable {
+ // If mToken is a WindowTokenClient, the Context is usually associated with a
+ // WindowContainer. We should detach from WindowContainer when the Context is finalized
+ // if this Context is not a WindowContext. WindowContext finalization is handled in
+ // WindowContext class.
+ if (mToken instanceof WindowTokenClient && mContextType != CONTEXT_TYPE_WINDOW_CONTEXT) {
+ ((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded();
+ }
+ super.finalize();
+ }
+
@UnsupportedAppUsage
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
@@ -2983,22 +2997,13 @@
* @param displayId The ID of the display where the UI is shown.
*/
static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
- final LoadedApk packageInfo = systemContext.mPackageInfo;
- ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo,
- ContextParams.EMPTY, null, null, null, null, null, 0, null, null);
- context.setResources(createResources(null, packageInfo, null, displayId, null,
- packageInfo.getCompatibilityInfo(), null));
- context.updateDisplay(displayId);
+ final WindowTokenClient token = new WindowTokenClient();
+ final ContextImpl context = systemContext.createWindowContextBase(token, displayId);
+ token.attachContext(context);
+ token.attachToDisplayContent(displayId);
context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
- return context;
- }
- /**
- * The overloaded method of {@link #createSystemUiContext(ContextImpl, int)}.
- * Uses {@Code Display.DEFAULT_DISPLAY} as the target display.
- */
- static ContextImpl createSystemUiContext(ContextImpl systemContext) {
- return createSystemUiContext(systemContext, Display.DEFAULT_DISPLAY);
+ return context;
}
@UnsupportedAppUsage
@@ -3206,7 +3211,13 @@
@Override
public IBinder getWindowContextToken() {
- return mContextType == CONTEXT_TYPE_WINDOW_CONTEXT ? mToken : null;
+ switch (mContextType) {
+ case CONTEXT_TYPE_WINDOW_CONTEXT:
+ case CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI:
+ return mToken;
+ default:
+ return null;
+ }
}
private void checkMode(int mode) {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9e41e4d..ae32a48 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -866,6 +866,23 @@
void attachWindowContextToWindowToken(IBinder clientToken, IBinder token);
/**
+ * Attaches a {@code clientToken} to associate with DisplayContent.
+ * <p>
+ * Note that this API should be invoked after calling
+ * {@link android.window.WindowTokenClient#attachContext(Context)}
+ * </p>
+ *
+ * @param clientToken {@link android.window.WindowContext#getWindowContextToken()
+ * the WindowContext's token}
+ * @param displayId The display associated with the window context
+ *
+ * @return the DisplayContent's {@link android.app.res.Configuration} if the Context is
+ * attached to the DisplayContent successfully. {@code null}, otherwise.
+ * @throws android.view.WindowManager.InvalidDisplayException if the display ID is invalid
+ */
+ Configuration attachToDisplayContent(IBinder clientToken, int displayId);
+
+ /**
* Detaches {@link android.window.WindowContext} from the window manager node it's currently
* attached to. It is no-op if the WindowContext is not attached to a window manager node.
*
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 5aa6233..17b675f 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -19,13 +19,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
import android.view.IWindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
@@ -38,7 +35,6 @@
* @hide
*/
public class WindowContextController {
- private final IWindowManager mWms;
/**
* {@code true} to indicate that the {@code mToken} is associated with a
* {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
@@ -56,14 +52,7 @@
* {@link Context#getWindowContextToken()}.
*/
public WindowContextController(@NonNull WindowTokenClient token) {
- this(token, WindowManagerGlobal.getWindowManagerService());
- }
-
- /** Used for test only. DO NOT USE it in production code. */
- @VisibleForTesting
- public WindowContextController(@NonNull WindowTokenClient token, IWindowManager mockWms) {
mToken = token;
- mWms = mockWms;
}
/**
@@ -80,19 +69,7 @@
throw new IllegalStateException("A Window Context can be only attached to "
+ "a DisplayArea once.");
}
- try {
- final Configuration configuration = mWms.attachWindowContextToDisplayArea(mToken, type,
- displayId, options);
- if (configuration != null) {
- mAttachedToDisplayArea = true;
- // Send the DisplayArea's configuration to WindowContext directly instead of
- // waiting for dispatching from WMS.
- mToken.onConfigurationChanged(configuration, displayId,
- false /* shouldReportConfigChange */);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options);
}
/**
@@ -120,22 +97,14 @@
throw new IllegalStateException("The Window Context should have been attached"
+ " to a DisplayArea.");
}
- try {
- mWms.attachWindowContextToWindowToken(mToken, windowToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mToken.attachToWindowToken(windowToken);
}
/** Detaches the window context from the node it's currently associated with. */
public void detachIfNeeded() {
if (mAttachedToDisplayArea) {
- try {
- mWms.detachWindowContextFromWindowContainer(mToken);
- mAttachedToDisplayArea = false;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mToken.detachFromWindowContainerIfNeeded();
+ mAttachedToDisplayArea = false;
}
}
}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index f3e3859..b331a9e 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -21,6 +21,7 @@
import static android.window.ConfigurationHelper.shouldUpdateResources;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
@@ -31,7 +32,11 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
+import android.view.IWindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
+import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
@@ -59,10 +64,14 @@
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
+ private IWindowManager mWms;
+
private final Configuration mConfiguration = new Configuration();
private boolean mShouldDumpConfigForIme;
+ private boolean mAttachToWindowContainer;
+
/**
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}.
@@ -84,6 +93,88 @@
}
/**
+ * Attaches this {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
+ *
+ * @param type The window type of the {@link WindowContext}
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @param options The window context launched option
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayArea(@WindowType int type, int displayId,
+ @Nullable Bundle options) {
+ try {
+ final Configuration configuration = getWindowManagerService()
+ .attachWindowContextToDisplayArea(this, type, displayId, options);
+ if (configuration == null) {
+ return false;
+ }
+ onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+ mAttachToWindowContainer = true;
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attaches this {@link WindowTokenClient} to a {@code DisplayContent}.
+ *
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayContent(int displayId) {
+ final IWindowManager wms = getWindowManagerService();
+ // #createSystemUiContext may call this method before WindowManagerService is initialized.
+ if (wms == null) {
+ return false;
+ }
+ try {
+ final Configuration configuration = wms.attachToDisplayContent(this, displayId);
+ if (configuration == null) {
+ return false;
+ }
+ onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+ mAttachToWindowContainer = true;
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
+ *
+ * @param windowToken the window token to associated with
+ */
+ public void attachToWindowToken(IBinder windowToken) {
+ try {
+ getWindowManagerService().attachWindowContextToWindowToken(this, windowToken);
+ mAttachToWindowContainer = true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Detaches this {@link WindowTokenClient} from associated WindowContainer if there's one. */
+ public void detachFromWindowContainerIfNeeded() {
+ if (!mAttachToWindowContainer) {
+ return;
+ }
+ try {
+ getWindowManagerService().detachWindowContextFromWindowContainer(this);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private IWindowManager getWindowManagerService() {
+ if (mWms == null) {
+ mWms = WindowManagerGlobal.getWindowManagerService();
+ }
+ return mWms;
+ }
+
+ /**
* Called when {@link Configuration} updates from the server side receive.
*
* @param newConfig the updated {@link Configuration}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index a6e351d..52cb9f3 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -24,16 +24,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.content.res.Configuration;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
-import android.view.IWindowManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -59,17 +56,14 @@
public class WindowContextControllerTest {
private WindowContextController mController;
@Mock
- private IWindowManager mMockWms;
- @Mock
private WindowTokenClient mMockToken;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mController = new WindowContextController(mMockToken, mMockWms);
+ mController = new WindowContextController(mMockToken);
doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
- doReturn(new Configuration()).when(mMockWms).attachWindowContextToDisplayArea(any(),
- anyInt(), anyInt(), any());
+ doReturn(true).when(mMockToken).attachToDisplayArea(anyInt(), anyInt(), any());
}
@Test(expected = IllegalStateException.class)
@@ -81,10 +75,10 @@
}
@Test
- public void testDetachIfNeeded_NotAttachedYet_DoNothing() throws Exception {
+ public void testDetachIfNeeded_NotAttachedYet_DoNothing() {
mController.detachIfNeeded();
- verify(mMockWms, never()).detachWindowContextFromWindowContainer(any());
+ verify(mMockToken, never()).detachFromWindowContainerIfNeeded();
}
@Test
@@ -93,8 +87,6 @@
null /* options */);
assertThat(mController.mAttachedToDisplayArea).isTrue();
- verify(mMockToken).onConfigurationChanged(any(), eq(DEFAULT_DISPLAY),
- eq(false) /* shouldReportConfigChange */);
mController.detachIfNeeded();
diff --git a/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml b/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml
new file mode 100644
index 0000000..329e5b9
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
index 94165a1..22cd384 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
@@ -16,6 +16,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="@color/size_compat_hint_bubble"/>
+ <solid android:color="@color/size_compat_background"/>
<corners android:radius="@dimen/size_compat_hint_corner_radius"/>
</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
index a8f0f76..af9063a 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
@@ -20,6 +20,6 @@
android:viewportWidth="10"
android:viewportHeight="8">
<path
- android:fillColor="@color/size_compat_hint_bubble"
+ android:fillColor="@color/size_compat_background"
android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index 3e486df..18caa35 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -20,16 +20,16 @@
android:viewportWidth="48"
android:viewportHeight="48">
<path
- android:fillColor="#53534D"
+ android:fillColor="@color/size_compat_background"
android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
<group
android:translateX="12"
android:translateY="12">
<path
- android:fillColor="#E4E3DA"
+ android:fillColor="@color/size_compat_text"
android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
<path
- android:fillColor="#E4E3DA"
+ android:fillColor="@color/size_compat_text"
android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
</group>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
new file mode 100644
index 0000000..95decff
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/size_compat_background_ripple">
+ <item android:drawable="@drawable/size_compat_restart_button"/>
+</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
index 17347f6..d0e7c42 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
@@ -40,7 +40,7 @@
android:padding="16dp"
android:text="@string/restart_button_description"
android:textAlignment="viewStart"
- android:textColor="#E4E3DA"
+ android:textColor="@color/size_compat_text"
android:textSize="14sp"/>
<ImageView
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
index 47e76f0..82ebee2 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
+++ b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
@@ -30,7 +30,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:src="@drawable/size_compat_restart_button"
+ android:src="@drawable/size_compat_restart_button_ripple"
android:background="@android:color/transparent"
android:contentDescription="@string/restart_button_description"/>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index b25a218..23a2172 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -29,7 +29,10 @@
<color name="bubbles_light">#FFFFFF</color>
<color name="bubbles_dark">@color/GM2_grey_800</color>
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
- <color name="size_compat_hint_bubble">#30312B</color>
+
+ <!-- Size Compat Restart Button -->
+ <color name="size_compat_background">@android:color/system_neutral1_800</color>
+ <color name="size_compat_text">@android:color/system_neutral1_50</color>
<!-- GM2 colors -->
<color name="GM2_grey_200">#E8EAED</color>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e24f07c..b56d189 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -801,13 +801,16 @@
mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
}
+ boolean lockedOutStateChanged = false;
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged |= !mFingerprintLockedOutPermanent;
mFingerprintLockedOutPermanent = true;
requireStrongAuthIfAllLockedOut();
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged |= !mFingerprintLockedOut;
mFingerprintLockedOut = true;
if (isUdfpsEnrolled()) {
updateFingerprintListeningState();
@@ -820,9 +823,14 @@
cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT);
}
}
+
+ if (lockedOutStateChanged) {
+ notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
+ }
}
private void handleFingerprintLockoutReset() {
+ boolean changed = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
mFingerprintLockedOut = false;
mFingerprintLockedOutPermanent = false;
@@ -837,6 +845,10 @@
} else {
updateFingerprintListeningState();
}
+
+ if (changed) {
+ notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
+ }
}
private void setFingerprintRunningState(int fingerprintRunningState) {
@@ -999,7 +1011,9 @@
}
}
+ boolean lockedOutStateChanged = false;
if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged = !mFaceLockedOutPermanent;
mFaceLockedOutPermanent = true;
requireStrongAuthIfAllLockedOut();
}
@@ -1011,11 +1025,21 @@
BiometricSourceType.FACE);
}
}
+
+ if (lockedOutStateChanged) {
+ notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ }
}
private void handleFaceLockoutReset() {
+ boolean changed = mFaceLockedOutPermanent;
mFaceLockedOutPermanent = false;
+
updateFaceListeningState();
+
+ if (changed) {
+ notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ }
}
private void setFaceRunningState(int faceRunningState) {
@@ -1237,6 +1261,16 @@
}
}
+ private void notifyLockedOutStateChanged(BiometricSourceType type) {
+ Assert.isMainThread();
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onLockedOutStateChanged(type);
+ }
+ }
+ }
+
public boolean isScreenOn() {
return mScreenOn;
}
@@ -2454,6 +2488,10 @@
}
}
+ public boolean isFingerprintLockedOut() {
+ return mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+ }
+
/**
* If biometrics hardware is available, not disabled, and user has enrolled templates.
* This does NOT check if the device is encrypted or in lockdown.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 12431984..8170a81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -292,6 +292,11 @@
public void onStrongAuthStateChanged(int userId) { }
/**
+ * When the current user's locked out state changed.
+ */
+ public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) { }
+
+ /**
* Called when the dream's window state is changed.
* @param dreaming true if the dream's window has been created and is visible
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 223eb78..8f4d6f6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -255,7 +255,6 @@
private void maybeShowInputBouncer() {
if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
mKeyguardViewManager.showBouncer(true);
- mKeyguardViewManager.resetAlternateAuth(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7f9eae8..8d07336 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -793,7 +793,8 @@
return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
} else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
- } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
+ } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0
+ || mUpdateMonitor.isFingerprintLockedOut())) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) {
return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
new file mode 100644
index 0000000..52103d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
+/**
+ * Extracts shared elements between navbar and taskbar delegate to de-dupe logic and help them
+ * experience the joys of friendship.
+ * The events are then passed through
+ *
+ * Currently consolidates
+ * * A11y
+ * * Assistant
+ */
+@SysUISingleton
+public final class NavBarHelper implements
+ AccessibilityButtonModeObserver.ModeChangedListener,
+ OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+ Dumpable {
+ private final AccessibilityManager mAccessibilityManager;
+ private final Lazy<AssistManager> mAssistManagerLazy;
+ private final UserTracker mUserTracker;
+ private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>();
+ private Context mContext;
+ private ContentResolver mContentResolver;
+ private boolean mAssistantAvailable;
+ private boolean mLongPressHomeEnabled;
+ private boolean mAssistantTouchGestureEnabled;
+ private int mNavBarMode;
+
+ private final ContentObserver mAssistContentObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateAssitantAvailability();
+ }
+ };
+
+ @Inject
+ public NavBarHelper(AccessibilityManager accessibilityManager,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
+ AccessibilityButtonModeObserver accessibilityButtonModeObserver,
+ OverviewProxyService overviewProxyService,
+ Lazy<AssistManager> assistManagerLazy,
+ NavigationModeController navigationModeController,
+ UserTracker userTracker,
+ DumpManager dumpManager) {
+ mAccessibilityManager = accessibilityManager;
+ mAssistManagerLazy = assistManagerLazy;
+ mUserTracker = userTracker;
+ accessibilityManagerWrapper.addCallback(
+ accessibilityManager1 -> NavBarHelper.this.dispatchA11yEventUpdate());
+ mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+
+ mAccessibilityButtonModeObserver.addListener(this);
+ mNavBarMode = navigationModeController.addListener(this);
+ overviewProxyService.addCallback(this);
+ dumpManager.registerDumpable(this);
+ }
+
+ public void init(Context context) {
+ mContext = context;
+ mContentResolver = mContext.getContentResolver();
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
+ false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ updateAssitantAvailability();
+ }
+
+ public void destroy() {
+ mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ }
+
+ /**
+ * @param listener Will immediately get callbacks based on current state
+ */
+ public void registerNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
+ mA11yEventListeners.add(listener);
+ listener.updateAccessibilityServicesState();
+ listener.updateAssistantAvailable(mAssistantAvailable);
+ }
+
+ public void removeNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
+ mA11yEventListeners.remove(listener);
+ }
+
+ private void dispatchA11yEventUpdate() {
+ for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
+ listener.updateAccessibilityServicesState();
+ }
+ }
+
+ private void dispatchAssistantEventUpdate(boolean assistantAvailable) {
+ for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
+ listener.updateAssistantAvailable(assistantAvailable);
+ }
+ }
+
+ @Override
+ public void onAccessibilityButtonModeChanged(int mode) {
+ dispatchA11yEventUpdate();
+ }
+
+ /**
+ * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
+ * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
+ *
+ * @return the a11y button clickable and long_clickable states, or 0 if there is no
+ * a11y button in the navbar
+ */
+ public int getA11yButtonState() {
+ // AccessibilityManagerService resolves services for the current user since the local
+ // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
+ final List<String> a11yButtonTargets =
+ mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
+ final int requestingServices = a11yButtonTargets.size();
+
+ // If accessibility button is floating menu mode, click and long click state should be
+ // disabled.
+ if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return 0;
+ }
+
+ return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+ | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+ }
+
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ if (isConnected) {
+ updateAssitantAvailability();
+ }
+ }
+
+ private void updateAssitantAvailability() {
+ boolean assistantAvailableForUser = mAssistManagerLazy.get()
+ .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ boolean longPressDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
+ mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
+ boolean gestureDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
+ mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
+
+ mAssistantAvailable = assistantAvailableForUser
+ && mAssistantTouchGestureEnabled
+ && QuickStepContract.isGesturalMode(mNavBarMode);
+ dispatchAssistantEventUpdate(mAssistantAvailable);
+ }
+
+ public boolean getLongPressHomeEnabled() {
+ return mLongPressHomeEnabled;
+ }
+
+ @Override
+ public void startAssistant(Bundle bundle) {
+ mAssistManagerLazy.get().startAssist(bundle);
+ }
+
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ mNavBarMode = mode;
+ updateAssitantAvailability();
+ }
+
+ /**
+ * Callbacks will get fired once immediately after registering via
+ * {@link #registerNavTaskStateUpdater(NavbarTaskbarStateUpdater)}
+ */
+ public interface NavbarTaskbarStateUpdater {
+ void updateAccessibilityServicesState();
+ void updateAssistantAvailable(boolean available);
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("NavbarTaskbarFriendster");
+ pw.println(" longPressHomeEnabled=" + mLongPressHomeEnabled);
+ pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
+ pw.println(" mAssistantAvailable=" + mAssistantAvailable);
+ pw.println(" mNavBarMode=" + mNavBarMode);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index bc023cc..5141f25 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -68,22 +68,18 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.database.ContentObserver;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.inputmethodservice.InputMethodService;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.Log;
@@ -200,8 +196,7 @@
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
- private final UserTracker mUserTracker;
+ private final NavBarHelper mNavBarHelper;
private final NotificationShadeDepthController mNotificationShadeDepthController;
private Bundle mSavedState;
@@ -213,9 +208,7 @@
private int mNavigationIconHints = 0;
private @TransitionMode int mNavigationBarMode;
private ContentResolver mContentResolver;
- private boolean mAssistantAvailable;
private boolean mLongPressHomeEnabled;
- private boolean mAssistantTouchGestureEnabled;
private int mDisabledFlags1;
private int mDisabledFlags2;
@@ -309,16 +302,31 @@
}
};
+ private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
+ new NavBarHelper.NavbarTaskbarStateUpdater() {
+ @Override
+ public void updateAccessibilityServicesState() {
+ updateAcessibilityStateFlags();
+ }
+
+ @Override
+ public void updateAssistantAvailable(boolean available) {
+ // TODO(b/198002034): Content observers currently can still be called back after
+ // being unregistered, and in this case we can ignore the change if the nav bar
+ // has been destroyed already
+ if (mNavigationBarView == null) {
+ return;
+ }
+ mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
+ updateAssistantEntrypoints(available);
+ }
+ };
+
private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
@Override
public void onConnectionChanged(boolean isConnected) {
mNavigationBarView.updateStates();
updateScreenPinningGestures();
-
- // Send the assistant availability upon connection
- if (isConnected) {
- updateAssistantEntrypoints();
- }
}
@Override
@@ -421,20 +429,6 @@
}
};
- private final ContentObserver mAssistContentObserver = new ContentObserver(
- new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- // TODO(b/198002034): Content observers currently can still be called back after being
- // unregistered, and in this case we can ignore the change if the nav bar has been
- // destroyed already
- if (mNavigationBarView == null) {
- return;
- }
- updateAssistantEntrypoints();
- }
- };
-
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -504,7 +498,7 @@
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
@@ -535,8 +529,7 @@
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
- mUserTracker = userTracker;
+ mNavBarHelper = navBarHelper;
mNotificationShadeDepthController = notificationShadeDepthController;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
@@ -568,18 +561,9 @@
mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mCommandQueue.addCallback(this);
- mAssistantAvailable = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
mContentResolver = mContext.getContentResolver();
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
- false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
- false, mAssistContentObserver, UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
- false, mAssistContentObserver, UserHandle.USER_ALL);
+ mNavBarHelper.init(mContext);
mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
R.bool.allow_force_nav_bar_handle_opaque);
mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
@@ -593,7 +577,6 @@
)).filter(duration -> duration != 0);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
- updateAssistantEntrypoints();
if (savedState != null) {
mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
@@ -620,8 +603,8 @@
mWindowManager.removeViewImmediate(mNavigationBarView.getRootView());
mNavigationModeController.removeListener(this);
- mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
- mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.destroy();
mDeviceProvisionedController.removeCallback(mUserSetupListener);
mNotificationShadeDepthController.removeListener(mDepthListener);
@@ -643,7 +626,7 @@
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
mNavigationBarView.setBehavior(mBehavior);
- mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -716,7 +699,7 @@
mHandler.removeCallbacks(mAutoDim);
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
- mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mFrame = null;
mNavigationBarView = null;
mOrientationHandle = null;
@@ -885,7 +868,6 @@
pw.println(" mCurrentRotation=" + mCurrentRotation);
pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled);
- pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
pw.println(" mNavigationBarWindowState="
+ windowStateToString(mNavigationBarWindowState));
pw.println(" mNavigationBarMode="
@@ -1165,7 +1147,7 @@
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
- updateAccessibilityServicesState();
+ updateAcessibilityStateFlags();
ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
@@ -1390,8 +1372,8 @@
return true;
}
- void updateAccessibilityServicesState() {
- int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ void updateAcessibilityStateFlags() {
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
if (mNavigationBarView != null) {
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
@@ -1403,7 +1385,7 @@
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
- a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ a11yFlags = mNavBarHelper.getA11yButtonState();
}
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1430,24 +1412,10 @@
}
}
- private void updateAssistantEntrypoints() {
- mAssistantAvailable = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
- boolean longPressDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
- mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
- boolean gestureDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
- mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
+ private void updateAssistantEntrypoints(boolean assistantAvailable) {
if (mOverviewProxyService.getProxy() != null) {
try {
- mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
- && mAssistantTouchGestureEnabled
- && QuickStepContract.isGesturalMode(mNavBarMode));
+ mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable);
} catch (RemoteException e) {
Log.w(TAG, "Unable to send assistant availability data to launcher");
}
@@ -1518,8 +1486,6 @@
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
- // update assistant entry points on system navigation radio button click
- updateAssistantEntrypoints();
if (!QuickStepContract.isGesturalMode(mode)) {
// Reset the override alpha
@@ -1558,9 +1524,6 @@
mNavigationBarView.getBarTransitions().finishAnimations();
}
- private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener =
- this::updateAccessibilityServicesState;
-
private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
lp.paramsForRotation = new WindowManager.LayoutParams[4];
@@ -1668,7 +1631,7 @@
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
- updateAccessibilityServicesState();
+ updateAcessibilityStateFlags();
}
}
};
@@ -1704,7 +1667,7 @@
private final Handler mMainHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private final NavBarHelper mNavBarHelper;
private final UserTracker mUserTracker;
private final LightBarController mMainLightBarController;
private final LightBarController.Factory mLightBarControllerFactory;
@@ -1737,7 +1700,7 @@
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
@@ -1767,7 +1730,7 @@
mMainHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mNavBarHelper = navBarHelper;
mUserTracker = userTracker;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
@@ -1788,7 +1751,7 @@
mSplitScreenOptional, mRecentsOptional, mStatusBarOptionalLazy,
mShadeController, mNotificationRemoteInputManager,
mNotificationShadeDepthController, mSystemActions, mMainHandler,
- mNavbarOverlayController, mUiEventLogger, mNavigationBarA11yHelper,
+ mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
mUserTracker, mMainLightBarController, mLightBarControllerFactory,
mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional,
mInputMethodManager);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
deleted file mode 100644
index 13e6d8b..0000000
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.android.systemui.navigationbar;
-
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
-
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Extracts shared elements of a11y necessary between navbar and taskbar delegate
- */
-@SysUISingleton
-public final class NavigationBarA11yHelper implements
- AccessibilityButtonModeObserver.ModeChangedListener {
- private final AccessibilityManager mAccessibilityManager;
- private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
- private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>();
-
- @Inject
- public NavigationBarA11yHelper(AccessibilityManager accessibilityManager,
- AccessibilityManagerWrapper accessibilityManagerWrapper,
- AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
- mAccessibilityManager = accessibilityManager;
- accessibilityManagerWrapper.addCallback(
- accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate());
- mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
-
- mAccessibilityButtonModeObserver.addListener(this);
- }
-
- public void registerA11yEventListener(NavA11yEventListener listener) {
- mA11yEventListeners.add(listener);
- }
-
- public void removeA11yEventListener(NavA11yEventListener listener) {
- mA11yEventListeners.remove(listener);
- }
-
- private void dispatchEventUpdate() {
- for (NavA11yEventListener listener : mA11yEventListeners) {
- listener.updateAccessibilityServicesState();
- }
- }
-
- @Override
- public void onAccessibilityButtonModeChanged(int mode) {
- dispatchEventUpdate();
- }
-
- /**
- * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
- * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
- *
- * @return the a11y button clickable and long_clickable states, or 0 if there is no
- * a11y button in the navbar
- */
- public int getA11yButtonState() {
- // AccessibilityManagerService resolves services for the current user since the local
- // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
- final List<String> a11yButtonTargets =
- mAccessibilityManager.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- final int requestingServices = a11yButtonTargets.size();
-
- // If accessibility button is floating menu mode, click and long click state should be
- // disabled.
- if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
- == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return 0;
- }
-
- return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
- | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
- }
-
- public interface NavA11yEventListener {
- void updateAccessibilityServicesState();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 3dc79c4..3398778 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -100,7 +100,7 @@
CommandQueue commandQueue,
@Main Handler mainHandler,
ConfigurationController configurationController,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
TaskbarDelegate taskbarDelegate,
NavigationBar.Factory navigationBarFactory,
DumpManager dumpManager,
@@ -115,7 +115,7 @@
mNavMode = navigationModeController.addListener(this);
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
- navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer,
+ navBarHelper, navigationModeController, sysUiFlagsContainer,
dumpManager, autoHideController);
mIsTablet = isTablet(mContext);
dumpManager.registerDumpable(this);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index d707dbd..428d9d6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -43,6 +43,8 @@
import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.Display;
import android.view.InsetsVisibilities;
import android.view.View;
@@ -73,19 +75,30 @@
public class TaskbarDelegate implements CommandQueue.Callbacks,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
ComponentCallbacks, Dumpable {
+ private static final String TAG = TaskbarDelegate.class.getSimpleName();
private final EdgeBackGestureHandler mEdgeBackGestureHandler;
-
+ private boolean mInitialized;
private CommandQueue mCommandQueue;
private OverviewProxyService mOverviewProxyService;
- private NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private NavBarHelper mNavBarHelper;
private NavigationModeController mNavigationModeController;
private SysUiState mSysUiState;
private AutoHideController mAutoHideController;
private int mDisplayId;
private int mNavigationIconHints;
- private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener =
- this::updateSysuiFlags;
+ private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
+ new NavBarHelper.NavbarTaskbarStateUpdater() {
+ @Override
+ public void updateAccessibilityServicesState() {
+ updateSysuiFlags();
+ }
+
+ @Override
+ public void updateAssistantAvailable(boolean available) {
+ updateAssistantAvailability(available);
+ }
+ };
private int mDisabledFlags;
private @WindowVisibleState int mTaskBarWindowState = WINDOW_STATE_SHOWING;
private @Behavior int mBehavior;
@@ -125,14 +138,14 @@
public void setDependencies(CommandQueue commandQueue,
OverviewProxyService overviewProxyService,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
NavigationModeController navigationModeController,
SysUiState sysUiState, DumpManager dumpManager,
AutoHideController autoHideController) {
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
mOverviewProxyService = overviewProxyService;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mNavBarHelper = navBarHelper;
mNavigationModeController = navigationModeController;
mSysUiState = sysUiState;
dumpManager.registerDumpable(this);
@@ -140,12 +153,16 @@
}
public void init(int displayId) {
+ if (mInitialized) {
+ return;
+ }
mDisplayId = displayId;
mCommandQueue.addCallback(this);
mOverviewProxyService.addCallback(this);
mEdgeBackGestureHandler.onNavigationModeChanged(
mNavigationModeController.addListener(this));
- mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.init(mContext);
mEdgeBackGestureHandler.onNavBarAttached();
// Initialize component callback
Display display = mDisplayManager.getDisplay(displayId);
@@ -154,23 +171,29 @@
// Set initial state for any listeners
updateSysuiFlags();
mAutoHideController.setNavigationBar(mAutoHideUiElement);
+ mInitialized = true;
}
public void destroy() {
+ if (!mInitialized) {
+ return;
+ }
mCommandQueue.removeCallback(this);
mOverviewProxyService.removeCallback(this);
mNavigationModeController.removeListener(this);
- mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.destroy();
mEdgeBackGestureHandler.onNavBarDetached();
if (mWindowContext != null) {
mWindowContext.unregisterComponentCallbacks(this);
mWindowContext = null;
}
mAutoHideController.setNavigationBar(null);
+ mInitialized = false;
}
private void updateSysuiFlags() {
- int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -194,6 +217,18 @@
.commitUpdate(mDisplayId);
}
+ private void updateAssistantAvailability(boolean assistantAvailable) {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onAssistantAvailable() failed, available: " + assistantAvailable, e);
+ }
+ }
+
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 6647769..a12fd4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -444,8 +444,8 @@
final int height = (view instanceof ExpandableView)
? ((ExpandableView) view).getActualHeight()
: view.getHeight();
- final int rx = (int) ev.getX();
- final int ry = (int) ev.getY();
+ final int rx = (int) ev.getRawX();
+ final int ry = (int) ev.getRawY();
int[] temp = new int[2];
view.getLocationOnScreen(temp);
final int x = temp[0];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 67f51cb..aa3b3e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.PowerManager;
@@ -46,9 +47,11 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.FileDescriptor;
@@ -71,6 +74,7 @@
private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
+ private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3;
@IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
@@ -167,6 +171,10 @@
private final MetricsLogger mMetricsLogger;
private final AuthController mAuthController;
+ private final StatusBarStateController mStatusBarStateController;
+
+ private long mLastFpFailureUptimeMillis;
+ private int mNumConsecutiveFpFailures;
private static final class PendingAuthenticated {
public final int userId;
@@ -209,7 +217,10 @@
BIOMETRIC_IRIS_FAILURE(403),
@UiEvent(doc = "A biometric event of type iris errored.")
- BIOMETRIC_IRIS_ERROR(404);
+ BIOMETRIC_IRIS_ERROR(404),
+
+ @UiEvent(doc = "Bouncer was shown as a result of consecutive failed UDFPS attempts.")
+ BIOMETRIC_BOUNCER_SHOWN(916);
private final int mId;
@@ -257,7 +268,8 @@
NotificationMediaManager notificationMediaManager,
WakefulnessLifecycle wakefulnessLifecycle,
ScreenLifecycle screenLifecycle,
- AuthController authController) {
+ AuthController authController,
+ StatusBarStateController statusBarStateController) {
mContext = context;
mPowerManager = powerManager;
mShadeController = shadeController;
@@ -279,6 +291,7 @@
mKeyguardBypassController.setUnlockController(this);
mMetricsLogger = metricsLogger;
mAuthController = authController;
+ mStatusBarStateController = statusBarStateController;
dumpManager.registerDumpable(getClass().getName(), this);
}
@@ -620,6 +633,22 @@
.setType(MetricsEvent.TYPE_FAILURE).setSubtype(toSubtype(biometricSourceType)));
Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(UI_EVENT_LOGGER::log);
+
+ long currUptimeMillis = SystemClock.uptimeMillis();
+ if (currUptimeMillis - mLastFpFailureUptimeMillis < 2000) { // attempt within 2 seconds
+ mNumConsecutiveFpFailures += 1;
+ } else {
+ mNumConsecutiveFpFailures = 1;
+ }
+ mLastFpFailureUptimeMillis = currUptimeMillis;
+
+ if (biometricSourceType.equals(BiometricSourceType.FINGERPRINT)
+ && mUpdateMonitor.isUdfpsSupported()
+ && mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
+ mKeyguardViewController.showBouncer(true);
+ UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+ mNumConsecutiveFpFailures = 0;
+ }
cleanup();
}
@@ -631,6 +660,16 @@
.addTaggedData(MetricsEvent.FIELD_BIOMETRIC_AUTH_ERROR, msgId));
Optional.ofNullable(BiometricUiEvent.ERROR_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(UI_EVENT_LOGGER::log);
+
+ // if we're on the shade and we're locked out, immediately show the bouncer
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT
+ && (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+ && mUpdateMonitor.isUdfpsSupported()
+ && (mStatusBarStateController.getState() == StatusBarState.SHADE
+ || mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED)) {
+ mKeyguardViewController.showBouncer(true);
+ }
cleanup();
}
@@ -664,6 +703,8 @@
mBiometricModeListener.onResetMode();
mBiometricModeListener.notifyBiometricAuthModeChanged();
}
+ mNumConsecutiveFpFailures = 0;
+ mLastFpFailureUptimeMillis = 0;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 353868b..9647486 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
@@ -81,6 +82,13 @@
public void onStrongAuthStateChanged(int userId) {
mBouncerPromptReason = mCallback.getBouncerPromptReason();
}
+
+ @Override
+ public void onLockedOutStateChanged(BiometricSourceType type) {
+ if (type == BiometricSourceType.FINGERPRINT) {
+ mBouncerPromptReason = mCallback.getBouncerPromptReason();
+ }
+ }
};
private final Runnable mRemoveViewRunnable = this::removeView;
private final KeyguardBypassController mKeyguardBypassController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7ab4a1e..0d23d66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -441,6 +441,8 @@
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
*/
public void showBouncer(boolean scrimmed) {
+ resetAlternateAuth(false);
+
if (mShowing && !mBouncer.isShowing()) {
mBouncer.show(false /* resetSecuritySelection */, scrimmed);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 0e86964..1cf21ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -351,9 +351,8 @@
mSystemClock.advanceTime(205);
mController.onTouchOutsideView();
- // THEN show the bouncer and reset alt auth
+ // THEN show the bouncer
verify(mStatusBarKeyguardViewManager).showBouncer(eq(true));
- verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
new file mode 100644
index 0000000..734faec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import dagger.Lazy;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NavBarHelperTest extends SysuiTestCase {
+
+ @Mock
+ AccessibilityManager mAccessibilityManager;
+ @Mock
+ AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+ @Mock
+ AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ @Mock
+ OverviewProxyService mOverviewProxyService;
+ @Mock
+ Lazy<AssistManager> mAssistManagerLazy;
+ @Mock
+ AssistManager mAssistManager;
+ @Mock
+ NavigationModeController mNavigationModeController;
+ @Mock
+ UserTracker mUserTracker;
+ @Mock
+ ComponentName mAssistantComponent;
+ @Mock
+ DumpManager mDumpManager;
+ @Mock
+ NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater;
+
+ private NavBarHelper mNavBarHelper;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mAssistManagerLazy.get()).thenReturn(mAssistManager);
+ when(mAssistManager.getAssistInfoForUser(anyInt())).thenReturn(mAssistantComponent);
+ when(mUserTracker.getUserId()).thenReturn(1);
+
+ mNavBarHelper = new NavBarHelper(mAccessibilityManager,
+ mAccessibilityManagerWrapper, mAccessibilityButtonModeObserver,
+ mOverviewProxyService, mAssistManagerLazy, mNavigationModeController,
+ mUserTracker, mDumpManager);
+
+ }
+
+ @Test
+ public void registerListenersInCtor() {
+ verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper);
+ verify(mNavigationModeController, times(1)).addListener(mNavBarHelper);
+ verify(mOverviewProxyService, times(1)).addCallback(mNavBarHelper);
+ }
+
+ @Test
+ public void registerAssistantContentObserver() {
+ mNavBarHelper.init(mContext);
+ verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt());
+ }
+
+ @Test
+ public void callbacksFiredWhenRegistering() {
+ mNavBarHelper.init(mContext);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void assistantCallbacksFiredAfterConnecting() {
+ mNavBarHelper.init(mContext);
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onConnectionChanged(false);
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+
+ mNavBarHelper.onConnectionChanged(true);
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void a11yCallbacksFiredAfterModeChange() {
+ mNavBarHelper.init(mContext);
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onAccessibilityButtonModeChanged(0);
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void assistantCallbacksFiredAfterNavModeChange() {
+ mNavBarHelper.init(mContext);
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onNavigationModeChanged(0);
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void removeListenerNoCallbacksFired() {
+ mNavBarHelper.init(mContext);
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ // Remove listener
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ // Would have fired 2nd callback if not removed
+ mNavBarHelper.onAccessibilityButtonModeChanged(0);
+
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 4fc329f..31d8830 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -82,7 +82,7 @@
mCommandQueue,
Dependency.get(Dependency.MAIN_HANDLER),
mock(ConfigurationController.class),
- mock(NavigationBarA11yHelper.class),
+ mock(NavBarHelper.class),
mock(TaskbarDelegate.class),
mNavigationBarFactory,
mock(DumpManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 223ffbd..776b6aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -131,7 +131,7 @@
@Mock
EdgeBackGestureHandler mEdgeBackGestureHandler;
@Mock
- NavigationBarA11yHelper mNavigationBarA11yHelper;
+ NavBarHelper mNavBarHelper;
@Mock
private LightBarController mLightBarController;
@Mock
@@ -220,6 +220,7 @@
new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI)
.setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
.build());
+ when(mNavBarHelper.getLongPressHomeEnabled()).thenReturn(true);
mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
@@ -284,14 +285,14 @@
public void testA11yEventAfterDetach() {
View v = mNavigationBar.createView(null);
mNavigationBar.onViewAttachedToWindow(v);
- verify(mNavigationBarA11yHelper).registerA11yEventListener(any(
- NavigationBarA11yHelper.NavA11yEventListener.class));
+ verify(mNavBarHelper).registerNavTaskStateUpdater(any(
+ NavBarHelper.NavbarTaskbarStateUpdater.class));
mNavigationBar.onViewDetachedFromWindow(v);
- verify(mNavigationBarA11yHelper).removeA11yEventListener(any(
- NavigationBarA11yHelper.NavA11yEventListener.class));
+ verify(mNavBarHelper).removeNavTaskStateUpdater(any(
+ NavBarHelper.NavbarTaskbarStateUpdater.class));
// Should be safe even though the internal view is now null.
- mNavigationBar.updateAccessibilityServicesState();
+ mNavigationBar.updateAcessibilityStateFlags();
}
private NavigationBar createNavBar(Context context) {
@@ -321,7 +322,7 @@
mHandler,
mock(NavigationBarOverlayController.class),
mUiEventLogger,
- mNavigationBarA11yHelper,
+ mNavBarHelper,
mock(UserTracker.class),
mLightBarController,
mLightBarcontrollerFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 0faf5d4..a0e91fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -438,8 +438,8 @@
assertEquals("returns false when view is null", false,
NotificationSwipeHelper.isTouchInView(mEvent, null));
- doReturn(5f).when(mEvent).getX();
- doReturn(10f).when(mEvent).getY();
+ doReturn(5f).when(mEvent).getRawX();
+ doReturn(10f).when(mEvent).getRawY();
doReturn(20).when(mView).getWidth();
doReturn(20).when(mView).getHeight();
@@ -455,7 +455,7 @@
assertTrue("Touch is within the view",
mSwipeHelper.isTouchInView(mEvent, mView));
- doReturn(50f).when(mEvent).getX();
+ doReturn(50f).when(mEvent).getRawX();
assertFalse("Touch is not within the view",
mSwipeHelper.isTouchInView(mEvent, mView));
@@ -466,8 +466,8 @@
assertEquals("returns false when view is null", false,
NotificationSwipeHelper.isTouchInView(mEvent, null));
- doReturn(5f).when(mEvent).getX();
- doReturn(10f).when(mEvent).getY();
+ doReturn(5f).when(mEvent).getRawX();
+ doReturn(10f).when(mEvent).getRawY();
doReturn(20).when(mNotificationRow).getWidth();
doReturn(20).when(mNotificationRow).getActualHeight();
@@ -483,7 +483,7 @@
assertTrue("Touch is within the view",
mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
- doReturn(50f).when(mEvent).getX();
+ doReturn(50f).when(mEvent).getRawX();
assertFalse("Touch is not within the view",
mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 25fd801..07debe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -101,6 +102,8 @@
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
private ScreenLifecycle mScreenLifecycle;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
private BiometricUnlockController mBiometricUnlockController;
@Before
@@ -123,7 +126,7 @@
mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
mMetricsLogger, mDumpManager, mPowerManager,
mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
- mAuthController);
+ mAuthController, mStatusBarStateController);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
}
@@ -378,6 +381,23 @@
}
@Test
+ public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() {
+ // GIVEN UDFPS is supported
+ when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+
+ // WHEN udfps fails twice - then don't show the bouncer
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+
+ // WHEN udfps fails the third time
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+
+ // THEN show the bouncer
+ verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ }
+
+ @Test
public void onFinishedGoingToSleep_authenticatesWhenPending() {
when(mUpdateMonitor.isGoingToSleep()).thenReturn(true);
mBiometricUnlockController.onFinishedGoingToSleep(-1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 6f174cb..c5bdfed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -374,6 +374,21 @@
}
@Test
+ public void testHideAltAuth_onShowBouncer() {
+ // GIVEN alt auth is showing
+ mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ when(mBouncer.isShowing()).thenReturn(false);
+ when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
+ reset(mAlternateAuthInterceptor);
+
+ // WHEN showBouncer is called
+ mStatusBarKeyguardViewManager.showBouncer(true);
+
+ // THEN alt bouncer should be hidden
+ verify(mAlternateAuthInterceptor).hideAlternateAuthBouncer();
+ }
+
+ @Test
public void testUpdateResources_delegatesToBouncer() {
mStatusBarKeyguardViewManager.updateResources();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 73baf79..82b34c3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -223,7 +223,7 @@
public Context getSettingsContext(int displayId) {
if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) {
final Context systemUiContext = ActivityThread.currentActivityThread()
- .createSystemUiContext(displayId);
+ .getSystemUiContext(displayId);
final Context windowContext = systemUiContext.createWindowContext(
WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, null /* options */);
mSettingsContext = new ContextThemeWrapper(
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 6ad2f7c..bb5d962 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -496,11 +496,13 @@
* @param activityIntent intent to start the activity.
* @param activityOptions ActivityOptions to start the activity with.
* @param resultTo the caller activity
+ * @param callingUid the caller uid
+ * @param callingPid the caller pid
* @return the start result.
*/
int startActivityInTaskFragment(@NonNull TaskFragment taskFragment,
@NonNull Intent activityIntent, @Nullable Bundle activityOptions,
- @Nullable IBinder resultTo) {
+ @Nullable IBinder resultTo, int callingUid, int callingPid) {
final ActivityRecord caller =
resultTo != null ? ActivityRecord.forTokenLocked(resultTo) : null;
return obtainStarter(activityIntent, "startActivityInTaskFragment")
@@ -508,8 +510,8 @@
.setInTaskFragment(taskFragment)
.setResultTo(resultTo)
.setRequestCode(-1)
- .setCallingUid(Binder.getCallingUid())
- .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(callingUid)
+ .setCallingPid(callingPid)
.setUserId(caller != null ? caller.mUserId : mService.getCurrentUserId())
.execute();
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index df9a6d2..8788225 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -590,7 +590,7 @@
}
// We don't want the organizer to handle transition of non-embedded activity of other
// app.
- if (r.getUid() != rootActivity.getUid() && !r.isEmbedded()) {
+ if (r.getUid() != task.effectiveUid && !r.isEmbedded()) {
leafTask = null;
break;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 07ab571..36579d9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -34,6 +34,7 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Build.VERSION_CODES.N;
+import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.util.RotationUtils.deltaRotation;
@@ -62,6 +63,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
@@ -4997,6 +4999,12 @@
reconfigureDisplayLocked();
onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
+ // Attach the SystemUiContext to this DisplayContent the get latest configuration.
+ // Note that the SystemUiContext will be removed automatically if this DisplayContent
+ // is detached.
+ mWmService.mWindowContextListenerController.registerWindowContainerListener(
+ getDisplayUiContext().getWindowContextToken(), this, SYSTEM_UID,
+ INVALID_WINDOW_TYPE, null /* options */);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index eb82957..b2657e8 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -452,7 +452,7 @@
: service.mContext.createDisplayContext(displayContent.getDisplay());
mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.mUiContext
: service.mAtmService.mSystemThread
- .createSystemUiContext(displayContent.getDisplayId());
+ .getSystemUiContext(displayContent.getDisplayId());
mDisplayContent = displayContent;
mLock = service.getWindowManagerLock();
@@ -2260,7 +2260,8 @@
// user's package info (see ContextImpl.createDisplayContext)
final LoadedApk pi = ActivityThread.currentActivityThread().getPackageInfo(
uiContext.getPackageName(), null, 0, userId);
- mCurrentUserResources = ResourcesManager.getInstance().getResources(null,
+ mCurrentUserResources = ResourcesManager.getInstance().getResources(
+ uiContext.getWindowContextToken(),
pi.getResDir(),
null /* splitResDirs */,
pi.getOverlayDirs(),
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b1eca9d..3ffa62d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2585,7 +2585,7 @@
// starts. Instead, we expect home activities to be launched when the system is ready
// (ActivityManagerService#systemReady).
if (mService.isBooted() || mService.isBooting()) {
- startSystemDecorations(display.mDisplayContent);
+ startSystemDecorations(display);
}
// Drop any cached DisplayInfos associated with this display id - the values are now
// out of date given this display added event.
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index bc53041..86e356a 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -45,7 +45,7 @@
*
* <ul>
* <li>When a {@link WindowContext} is created, it registers the listener via
- * {@link WindowManagerService#registerWindowContextListener(IBinder, int, int, Bundle)}
+ * {@link WindowManagerService#attachWindowContextToDisplayArea(IBinder, int, int, Bundle)}
* automatically.</li>
* <li>When the {@link WindowContext} adds the first window to the screen via
* {@link android.view.WindowManager#addView(View, android.view.ViewGroup.LayoutParams)},
@@ -53,7 +53,7 @@
* to corresponding {@link WindowToken} via this controller.</li>
* <li>When the {@link WindowContext} is GCed, it unregisters the previously
* registered listener via
- * {@link WindowManagerService#unregisterWindowContextListener(IBinder)}.
+ * {@link WindowManagerService#detachWindowContextFromWindowContainer(IBinder)}.
* {@link WindowManagerService} is also responsible for removing the
* {@link WindowContext} created {@link WindowToken}.</li>
* </ul>
@@ -68,7 +68,7 @@
/**
* Registers the listener to a {@code container} which is associated with
- * a {@code clientToken}, which is a {@link android.app.WindowContext} representation. If the
+ * a {@code clientToken}, which is a {@link android.window.WindowContext} representation. If the
* listener associated with {@code clientToken} hasn't been initialized yet, create one
* {@link WindowContextListenerImpl}. Otherwise, the listener associated with
* {@code clientToken} switches to listen to the {@code container}.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 943a3c1..cc7485c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2741,6 +2741,9 @@
@Override
public Configuration attachWindowContextToDisplayArea(IBinder clientToken, int
type, int displayId, Bundle options) {
+ if (clientToken == null) {
+ throw new IllegalArgumentException("clientToken must not be null!");
+ }
final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
"attachWindowContextToDisplayArea", false /* printLog */);
final int callingUid = Binder.getCallingUid();
@@ -2831,6 +2834,39 @@
}
}
+ @Override
+ public Configuration attachToDisplayContent(IBinder clientToken, int displayId) {
+ if (clientToken == null) {
+ throw new IllegalArgumentException("clientToken must not be null!");
+ }
+ final int callingUid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ // We use "getDisplayContent" instead of "getDisplayContentOrCreate" because
+ // this method may be called in DisplayPolicy's constructor and may cause
+ // infinite loop. In this scenario, we early return here and switch to do the
+ // registration in DisplayContent#onParentChanged at DisplayContent initialization.
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc == null) {
+ if (Binder.getCallingPid() != myPid()) {
+ throw new WindowManager.InvalidDisplayException("attachToDisplayContent: "
+ + "trying to attach to a non-existing display:" + displayId);
+ }
+ // Early return if this method is invoked from system process.
+ // See above comments for more detail.
+ return null;
+ }
+
+ mWindowContextListenerController.registerWindowContainerListener(clientToken, dc,
+ callingUid, INVALID_WINDOW_TYPE, null /* options */);
+ return dc.getConfiguration();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
/** Returns {@code true} if this binder is a registered window token. */
@Override
public boolean isWindowToken(IBinder binder) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index bd8d116..877965e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -588,7 +588,7 @@
case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
final TaskFragmentCreationParams taskFragmentCreationOptions =
hop.getTaskFragmentCreationOptions();
- createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
+ createTaskFragment(taskFragmentCreationOptions, errorCallbackToken, caller);
break;
}
case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: {
@@ -631,7 +631,7 @@
final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
final int result = mService.getActivityStartController()
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
- hop.getCallingActivity());
+ hop.getCallingActivity(), caller.mUid, caller.mPid);
if (!isStartResultSuccessful(result)) {
sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
errorCallbackToken,
@@ -1200,7 +1200,7 @@
}
void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
- @Nullable IBinder errorCallbackToken) {
+ @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller) {
final ActivityRecord ownerActivity =
ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
final ITaskFragmentOrganizer organizer = ITaskFragmentOrganizer.Stub.asInterface(
@@ -1218,9 +1218,9 @@
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return;
}
- // The ownerActivity has to belong to the same app as the root Activity of the target Task.
- final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity();
- if (rootActivity.getUid() != ownerActivity.getUid()) {
+ // The ownerActivity has to belong to the same app as the target Task.
+ if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
+ || ownerActivity.getTask().effectiveUid != caller.mUid) {
final Throwable exception =
new IllegalArgumentException("Not allowed to operate with the ownerToken while "
+ "the root activity of the target task belong to the different app");
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 5d0e34a..6fa306b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -803,6 +803,7 @@
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
openingActivity.allDrawn = true;
+ task.effectiveUid = openingActivity.getUid();
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition.
@@ -879,6 +880,7 @@
final ActivityRecord closingActivity = taskFragment.getTopMostActivity();
closingActivity.allDrawn = true;
closingActivity.info.applicationInfo.uid = 12345;
+ task.effectiveUid = closingActivity.getUid();
// Opening non-embedded activity with different UID.
final ActivityRecord openingActivity = createActivityRecord(task);
openingActivity.info.applicationInfo.uid = 54321;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index a8ede13..d7daa57 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -29,7 +29,9 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -44,6 +46,7 @@
import com.android.server.inputmethod.InputMethodManagerService;
import com.android.server.inputmethod.InputMethodMenuController;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,6 +65,9 @@
private InputMethodMenuController mController;
private DualDisplayAreaGroupPolicyTest.DualDisplayContent mSecondaryDisplay;
+ private IWindowManager mIWindowManager;
+ private DisplayManagerGlobal mDisplayManagerGlobal;
+
@Before
public void setUp() throws Exception {
// Let the Display to be created with the DualDisplay policy.
@@ -70,10 +76,12 @@
Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
+ mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
+ .Builder(mAtm, 1000, 1000).build();
// Mock addWindowTokenWithOptions to create a test window token.
- IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
- spyOn(wms);
+ mIWindowManager = WindowManagerGlobal.getWindowManagerService();
+ spyOn(mIWindowManager);
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
IBinder clientToken = (IBinder) args[0];
@@ -83,19 +91,24 @@
dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
null /* options */);
return dc.getImeContainer().getConfiguration();
- }).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG),
- anyInt(), any());
-
- mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
- .Builder(mAtm, 1000, 1000).build();
-
- // Mock DisplayManagerGlobal to return test display when obtaining Display instance.
+ }).when(mIWindowManager).attachWindowContextToDisplayArea(any(),
+ eq(TYPE_INPUT_METHOD_DIALOG), anyInt(), any());
+ mDisplayManagerGlobal = DisplayManagerGlobal.getInstance();
+ spyOn(mDisplayManagerGlobal);
final int displayId = mSecondaryDisplay.getDisplayId();
final Display display = mSecondaryDisplay.getDisplay();
- DisplayManagerGlobal displayManagerGlobal = DisplayManagerGlobal.getInstance();
- spyOn(displayManagerGlobal);
- doReturn(display).when(displayManagerGlobal).getCompatibleDisplay(eq(displayId),
+ doReturn(display).when(mDisplayManagerGlobal).getCompatibleDisplay(eq(displayId),
(Resources) any());
+ Context systemUiContext = ActivityThread.currentActivityThread()
+ .getSystemUiContext(displayId);
+ spyOn(systemUiContext);
+ doReturn(display).when(systemUiContext).getDisplay();
+ }
+
+ @After
+ public void tearDown() {
+ reset(mIWindowManager);
+ reset(mDisplayManagerGlobal);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index b8e12d1..7014851 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.testing.Assert.assertThrows;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -352,28 +353,66 @@
}
@Test
- public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() {
+ public void testApplyTransaction_enforceHierarchyChange_createTaskFragment()
+ throws RemoteException {
+ mController.registerOrganizer(mIOrganizer);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ final int uid = Binder.getCallingUid();
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid;
+ final IBinder fragmentToken = new Binder();
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken, activity.token).build();
mOrganizer.applyTransaction(mTransaction);
// Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
- final TaskFragmentCreationParams mockParams = mock(TaskFragmentCreationParams.class);
- doReturn(mOrganizerToken).when(mockParams).getOrganizer();
- mTransaction.createTaskFragment(mockParams);
+ mTransaction.createTaskFragment(params);
mTransaction.startActivityInTaskFragment(
mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */);
mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class));
mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class),
null /* options */);
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are
- // testing the security check here.
- assertThrows(IllegalArgumentException.class, () -> {
- try {
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- } catch (RemoteException e) {
- fail();
- }
- });
+ // Successfully created a TaskFragment.
+ final TaskFragment taskFragment = mAtm.mWindowOrganizerController
+ .getTaskFragment(fragmentToken);
+ assertNotNull(taskFragment);
+ assertEquals(activity.getTask(), taskFragment.getTask());
+ }
+
+ @Test
+ public void testApplyTransaction_createTaskFragment_failForDifferentUid()
+ throws RemoteException {
+ mController.registerOrganizer(mIOrganizer);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ final int uid = Binder.getCallingUid();
+ final IBinder fragmentToken = new Binder();
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken, activity.token).build();
+ mOrganizer.applyTransaction(mTransaction);
+ mTransaction.createTaskFragment(params);
+
+ // Fail to create TaskFragment when the task uid is different from caller.
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid + 1;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
+
+ // Fail to create TaskFragment when the task uid is different from owner activity.
+ activity.info.applicationInfo.uid = uid + 1;
+ activity.getTask().effectiveUid = uid;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
+
+ // Successfully created a TaskFragment for same uid.
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
}
@Test