Added systemui controller to control system bars.

When config_remoteInsetsControllerControlsSystemBars is true,
DisplaySystemBarsController provides its own policy of how system
bars are displayed for specific packages. Currently limited to
only auto versions of Android.

Bug: 149585273
Test: Manual, atest BarControlPolicyTest, atest InsetsPolicyTest,
      atest DisplaySystemBarsControllerTest
Change-Id: Ie6b1cc3e2760cbc9e48d62dfbd8bc3e23ffca20c
Merged-In: Ie6b1cc3e2760cbc9e48d62dfbd8bc3e23ffca20c
This commit is contained in:
JianYang Liu 2020-06-26 14:18:11 -07:00 committed by Jian-Yang Liu
parent ccf383243a
commit 2c403a6526
22 changed files with 1034 additions and 129 deletions

View File

@ -34,6 +34,7 @@ package android {
public static final class R.bool {
field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
field public static final int config_remoteInsetsControllerControlsSystemBars = 17891334; // 0x1110006
}
public static final class R.string {

View File

@ -26,6 +26,13 @@ import android.view.InsetsState;
*/
oneway interface IDisplayWindowInsetsController {
/**
* Called when top focused window changes to determine whether or not to take over insets
* control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
* @param packageName: Passes the top package name
*/
void topFocusedWindowChanged(String packageName);
/**
* @see IWindow#insetsChanged
*/

View File

@ -387,16 +387,6 @@ interface IWindowManager
*/
oneway void hideTransientBars(int displayId);
/**
* When set to {@code true} the system bars will always be shown. This is true even if an app
* requests to be fullscreen by setting the system ui visibility flags. The
* functionality was added for the automotive case as a way to guarantee required content stays
* on screen at all times.
*
* @hide
*/
oneway void setForceShowSystemBars(boolean show);
/**
* Called by System UI to notify of changes to the visibility of Recents.
*/

View File

@ -712,10 +712,17 @@
case, this can be disabled (set to false). -->
<bool name="config_enableCarDockHomeLaunch">true</bool>
<!-- Control whether to force the display of System UI Bars at all times regardless of
System Ui Flags. This can be useful in the Automotive case if there's a requirement for
a UI element to be on screen at all times. -->
<bool name="config_forceShowSystemBars">false</bool>
<!-- Control whether to force apps to give up control over the display of system bars at all
times regardless of System Ui Flags.
In the Automotive case, this is helpful if there's a requirement for an UI element to be on
screen at all times. Setting this to true also gives System UI the ability to override the
visibility controls for the system through the usage of the
"SYSTEM_BAR_VISIBILITY_OVERRIDE" setting.
Ex: Only setting the config to true will force show system bars for the entire system.
Ex: Setting the config to true and the "SYSTEM_BAR_VISIBILITY_OVERRIDE" setting to
"immersive.status=apps" will force show navigation bar for all apps and force hide status
bar for all apps. -->
<bool name="config_remoteInsetsControllerControlsSystemBars">false</bool>
<!-- HDMI behavior -->

View File

@ -3024,6 +3024,8 @@
<!-- @hide @TestApi -->
<public type="bool" name="config_assistantOnTopOfDream" id="0x01110005" />
<!-- @hide @TestApi -->
<public type="bool" name="config_remoteInsetsControllerControlsSystemBars" id="0x01110006" />
<!-- ===============================================================
Resources added in version S of the platform

View File

@ -1669,7 +1669,7 @@
<java-symbol type="bool" name="config_enableCarDockHomeLaunch" />
<java-symbol type="bool" name="config_enableLockBeforeUnlockScreen" />
<java-symbol type="bool" name="config_enableLockScreenRotation" />
<java-symbol type="bool" name="config_forceShowSystemBars" />
<java-symbol type="bool" name="config_remoteInsetsControllerControlsSystemBars" />
<java-symbol type="bool" name="config_lidControlsScreenLock" />
<java-symbol type="bool" name="config_lidControlsSleep" />
<java-symbol type="bool" name="config_lockDayNightMode" />

View File

@ -67,6 +67,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.volume.VolumeDialogComponent;
import com.android.systemui.wm.DisplayImeController;
import com.android.systemui.wm.DisplaySystemBarsController;
import javax.inject.Named;
import javax.inject.Singleton;
@ -97,6 +99,10 @@ public abstract class CarSystemUIModule {
groupManager, configurationController);
}
@Binds
abstract DisplayImeController bindDisplayImeController(
DisplaySystemBarsController displaySystemBarsController);
@Singleton
@Provides
@Named(LEAK_REPORT_EMAIL_NAME)

View File

@ -0,0 +1,250 @@
/*
* Copyright (C) 2020 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.wm;
import android.car.settings.CarSettings;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.Slog;
import android.view.WindowInsets;
import androidx.annotation.VisibleForTesting;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* Util class to load PolicyControl and allow for querying if a package matches immersive filters.
* Similar to {@link com.android.server.wm.PolicyControl}, but separate due to CarSystemUI needing
* to set its own policies for system bar visibilities.
*
* This forces immersive mode behavior for one or both system bars (based on a package
* list).
*
* Control by setting {@link Settings.Global#POLICY_CONTROL_AUTO} to one or more name-value pairs.
* e.g.
* to force immersive mode everywhere:
* "immersive.full=*"
* to force hide status bars for com.package1 but not com.package2:
* "immersive.status=com.package1,-com.package2"
*
* Separate multiple name-value pairs with ':'
* e.g. "immersive.status=com.package:immersive.navigation=*"
*/
public class BarControlPolicy {
private static final String TAG = "BarControlPolicy";
private static final boolean DEBUG = false;
private static final String NAME_IMMERSIVE_FULL = "immersive.full";
private static final String NAME_IMMERSIVE_STATUS = "immersive.status";
private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation";
@VisibleForTesting
static String sSettingValue;
@VisibleForTesting
static Filter sImmersiveStatusFilter;
private static Filter sImmersiveNavigationFilter;
/** Loads values from the POLICY_CONTROL setting to set filters. */
static boolean reloadFromSetting(Context context) {
if (DEBUG) Slog.d(TAG, "reloadFromSetting()");
String value = null;
try {
value = Settings.Global.getStringForUser(context.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
UserHandle.USER_CURRENT);
if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) {
return false;
}
setFilters(value);
sSettingValue = value;
} catch (Throwable t) {
Slog.w(TAG, "Error loading policy control, value=" + value, t);
return false;
}
return true;
}
/** Used in testing to reset BarControlPolicy. */
@VisibleForTesting
static void reset() {
sSettingValue = null;
sImmersiveStatusFilter = null;
sImmersiveNavigationFilter = null;
}
/**
* Registers a content observer to listen to updates to the SYSTEM_BAR_VISIBILITY_OVERRIDE flag.
*/
static void registerContentObserver(Context context, Handler handler, FilterListener listener) {
context.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE), false,
new ContentObserver(handler) {
@Override
public void onChange(boolean selfChange) {
if (reloadFromSetting(context)) {
listener.onFilterUpdated();
}
}
}, UserHandle.USER_ALL);
}
/**
* Returns bar visibilities based on POLICY_CONTROL_AUTO filters and window policies.
* @return int[], where the first value is the inset types that should be shown, and the second
* is the inset types that should be hidden.
*/
@WindowInsets.Type.InsetsType
static int[] getBarVisibilities(String packageName) {
int hideTypes = 0;
int showTypes = 0;
if (matchesStatusFilter(packageName)) {
hideTypes |= WindowInsets.Type.statusBars();
} else {
showTypes |= WindowInsets.Type.statusBars();
}
if (matchesNavigationFilter(packageName)) {
hideTypes |= WindowInsets.Type.navigationBars();
} else {
showTypes |= WindowInsets.Type.navigationBars();
}
return new int[] {showTypes, hideTypes};
}
private static boolean matchesStatusFilter(String packageName) {
return sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(packageName);
}
private static boolean matchesNavigationFilter(String packageName) {
return sImmersiveNavigationFilter != null
&& sImmersiveNavigationFilter.matches(packageName);
}
private static void setFilters(String value) {
if (DEBUG) Slog.d(TAG, "setFilters: " + value);
sImmersiveStatusFilter = null;
sImmersiveNavigationFilter = null;
if (value != null) {
String[] nvps = value.split(":");
for (String nvp : nvps) {
int i = nvp.indexOf('=');
if (i == -1) continue;
String n = nvp.substring(0, i);
String v = nvp.substring(i + 1);
if (n.equals(NAME_IMMERSIVE_FULL)) {
Filter f = Filter.parse(v);
sImmersiveStatusFilter = sImmersiveNavigationFilter = f;
} else if (n.equals(NAME_IMMERSIVE_STATUS)) {
Filter f = Filter.parse(v);
sImmersiveStatusFilter = f;
} else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) {
Filter f = Filter.parse(v);
sImmersiveNavigationFilter = f;
}
}
}
if (DEBUG) {
Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter);
Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter);
}
}
private static class Filter {
private static final String ALL = "*";
private final ArraySet<String> mWhitelist;
private final ArraySet<String> mBlacklist;
private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) {
mWhitelist = whitelist;
mBlacklist = blacklist;
}
boolean matches(String packageName) {
if (packageName == null) return false;
if (onBlacklist(packageName)) return false;
return onWhitelist(packageName);
}
private boolean onBlacklist(String packageName) {
return mBlacklist.contains(packageName) || mBlacklist.contains(ALL);
}
private boolean onWhitelist(String packageName) {
return mWhitelist.contains(ALL) || mWhitelist.contains(packageName);
}
void dump(PrintWriter pw) {
pw.print("Filter[");
dump("whitelist", mWhitelist, pw); pw.print(',');
dump("blacklist", mBlacklist, pw); pw.print(']');
}
private void dump(String name, ArraySet<String> set, PrintWriter pw) {
pw.print(name); pw.print("=(");
int n = set.size();
for (int i = 0; i < n; i++) {
if (i > 0) pw.print(',');
pw.print(set.valueAt(i));
}
pw.print(')');
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
dump(new PrintWriter(sw, true));
return sw.toString();
}
// value = comma-delimited list of tokens, where token = (package name|*)
// e.g. "com.package1", or "com.android.systemui, com.android.keyguard" or "*"
static Filter parse(String value) {
if (value == null) return null;
ArraySet<String> whitelist = new ArraySet<String>();
ArraySet<String> blacklist = new ArraySet<String>();
for (String token : value.split(",")) {
token = token.trim();
if (token.startsWith("-") && token.length() > 1) {
token = token.substring(1);
blacklist.add(token);
} else {
whitelist.add(token);
}
}
return new Filter(whitelist, blacklist);
}
}
/**
* Interface to listen for updates to the filter triggered by the content observer listening to
* the SYSTEM_BAR_VISIBILITY_OVERRIDE flag.
*/
interface FilterListener {
/** Callback triggered when the content observer updates the filter. */
void onFilterUpdated();
}
private BarControlPolicy() {}
}

View File

@ -0,0 +1,168 @@
/*
* Copyright (C) 2020 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.wm;
import android.os.Handler;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
import android.view.InsetsController;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.WindowInsets;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.TransactionPool;
import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Controller that maps between displays and {@link IDisplayWindowInsetsController} in order to
* give system bar control to SystemUI.
* {@link R.bool#config_remoteInsetsControllerControlsSystemBars} determines whether this controller
* takes control or not.
*/
@Singleton
public class DisplaySystemBarsController extends DisplayImeController {
private static final String TAG = "DisplaySystemBarsController";
private SparseArray<PerDisplay> mPerDisplaySparseArray;
@Inject
public DisplaySystemBarsController(
SystemWindows syswin,
DisplayController displayController,
@Main Handler mainHandler,
TransactionPool transactionPool) {
super(syswin, displayController, mainHandler, transactionPool);
}
@Override
public void onDisplayAdded(int displayId) {
PerDisplay pd = new PerDisplay(displayId);
try {
mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + displayId);
}
// Lazy loading policy control filters instead of during boot.
if (mPerDisplaySparseArray == null) {
mPerDisplaySparseArray = new SparseArray<>();
BarControlPolicy.reloadFromSetting(mSystemWindows.mContext);
BarControlPolicy.registerContentObserver(mSystemWindows.mContext, mHandler, () -> {
int size = mPerDisplaySparseArray.size();
for (int i = 0; i < size; i++) {
mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets();
}
});
}
mPerDisplaySparseArray.put(displayId, pd);
}
@Override
public void onDisplayRemoved(int displayId) {
try {
mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
}
mPerDisplaySparseArray.remove(displayId);
}
@VisibleForTesting
class PerDisplay extends IDisplayWindowInsetsController.Stub {
int mDisplayId;
InsetsController mInsetsController;
InsetsState mInsetsState = new InsetsState();
String mPackageName;
PerDisplay(int displayId) {
mDisplayId = displayId;
mInsetsController = new InsetsController(
new DisplaySystemBarsInsetsControllerHost(mHandler, this));
}
@Override
public void insetsChanged(InsetsState insetsState) {
if (mInsetsState.equals(insetsState)) {
return;
}
mInsetsState.set(insetsState, true /* copySources */);
mInsetsController.onStateChanged(insetsState);
if (mPackageName != null) {
modifyDisplayWindowInsets();
}
}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
mInsetsController.onControlsChanged(activeControls);
}
@Override
public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
mInsetsController.hide(types);
}
@Override
public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
mInsetsController.show(types);
}
@Override
public void topFocusedWindowChanged(String packageName) {
// If both package names are null or both package names are equal, return.
if (mPackageName == packageName
|| (mPackageName != null && mPackageName.equals(packageName))) {
return;
}
mPackageName = packageName;
modifyDisplayWindowInsets();
}
private void modifyDisplayWindowInsets() {
if (mPackageName == null) {
return;
}
int[] barVisibilities = BarControlPolicy.getBarVisibilities(mPackageName);
updateInsetsState(barVisibilities[0], /* visible= */ true);
updateInsetsState(barVisibilities[1], /* visible= */ false);
showInsets(barVisibilities[0], /* fromIme= */ false);
hideInsets(barVisibilities[1], /* fromIme= */ false);
try {
mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to update window manager service.");
}
}
private void updateInsetsState(@WindowInsets.Type.InsetsType int types, boolean visible) {
ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
mInsetsState.getSource(internalTypes.valueAt(i)).setVisible(visible);
}
}
}
}

View File

@ -0,0 +1,173 @@
/*
* Copyright (C) 2020 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.wm;
import android.annotation.NonNull;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.IDisplayWindowInsetsController;
import android.view.InsetsController;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsController;
import android.view.inputmethod.InputMethodManager;
import java.util.List;
/**
* Implements {@link InsetsController.Host} for usage by
* {@link DisplaySystemBarsController.PerDisplay} instances in {@link DisplaySystemBarsController}.
* @hide
*/
public class DisplaySystemBarsInsetsControllerHost implements InsetsController.Host {
private static final String TAG = DisplaySystemBarsInsetsControllerHost.class.getSimpleName();
private final Handler mHandler;
private final IDisplayWindowInsetsController mController;
private final float[] mTmpFloat9 = new float[9];
public DisplaySystemBarsInsetsControllerHost(
Handler handler, IDisplayWindowInsetsController controller) {
mHandler = handler;
mController = controller;
}
@Override
public Handler getHandler() {
return mHandler;
}
@Override
public void notifyInsetsChanged() {
// no-op
}
@Override
public void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation) {
// no-op
}
@Override
public WindowInsetsAnimation.Bounds dispatchWindowInsetsAnimationStart(
@NonNull WindowInsetsAnimation animation,
@NonNull WindowInsetsAnimation.Bounds bounds) {
return null;
}
@Override
public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
@NonNull List<WindowInsetsAnimation> runningAnimations) {
return null;
}
@Override
public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) {
// no-op
}
@Override
public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
for (int i = params.length - 1; i >= 0; i--) {
SyncRtSurfaceTransactionApplier.applyParams(
new SurfaceControl.Transaction(), params[i], mTmpFloat9);
}
}
@Override
public void updateCompatSysUiVisibility(
@InsetsState.InternalInsetsType int type, boolean visible, boolean hasControl) {
// no-op
}
@Override
public void onInsetsModified(InsetsState insetsState) {
try {
mController.insetsChanged(insetsState);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send insets to controller");
}
}
@Override
public boolean hasAnimationCallbacks() {
return false;
}
@Override
public void setSystemBarsAppearance(
@WindowInsetsController.Appearance int appearance,
@WindowInsetsController.Appearance int mask) {
// no-op
}
@Override
public @WindowInsetsController.Appearance int getSystemBarsAppearance() {
return 0;
}
@Override
public void setSystemBarsBehavior(@WindowInsetsController.Behavior int behavior) {
// no-op
}
@Override
public @WindowInsetsController.Behavior int getSystemBarsBehavior() {
return 0;
}
@Override
public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
surfaceControl.release();
}
@Override
public void addOnPreDrawRunnable(Runnable r) {
mHandler.post(r);
}
@Override
public void postInsetsAnimationCallback(Runnable r) {
mHandler.post(r);
}
@Override
public InputMethodManager getInputMethodManager() {
return null;
}
@Override
public String getRootViewTitle() {
return null;
}
@Override
public int dipToPx(int dips) {
return 0;
}
@Override
public IBinder getWindowToken() {
return null;
}
}

View File

@ -0,0 +1,195 @@
/*
* Copyright (C) 2020 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.wm;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
import static com.google.common.truth.Truth.assertThat;
import android.car.settings.CarSettings;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class BarControlPolicyTest extends SysuiTestCase {
private static final String PACKAGE_NAME = "sample.app";
@Before
public void setUp() {
BarControlPolicy.reset();
}
@After
public void tearDown() {
Settings.Global.clearProviderForTest();
}
@Test
public void reloadFromSetting_notSet_doesNotSetFilters() {
BarControlPolicy.reloadFromSetting(mContext);
assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull();
}
@Test
public void reloadFromSetting_invalidPolicyControlString_doesNotSetFilters() {
String text = "sample text";
Settings.Global.putString(
mContext.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
text
);
BarControlPolicy.reloadFromSetting(mContext);
assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull();
}
@Test
public void reloadFromSetting_validPolicyControlString_setsFilters() {
String text = "immersive.status=" + PACKAGE_NAME;
Settings.Global.putString(
mContext.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
text
);
BarControlPolicy.reloadFromSetting(mContext);
assertThat(BarControlPolicy.sImmersiveStatusFilter).isNotNull();
}
@Test
public void reloadFromSetting_filtersSet_doesNotSetFiltersAgain() {
String text = "immersive.status=" + PACKAGE_NAME;
Settings.Global.putString(
mContext.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
text
);
BarControlPolicy.reloadFromSetting(mContext);
assertThat(BarControlPolicy.reloadFromSetting(mContext)).isFalse();
}
@Test
public void getBarVisibilities_policyControlNotSet_showsSystemBars() {
int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
assertThat(visibilities[1]).isEqualTo(0);
}
@Test
public void getBarVisibilities_immersiveStatusForAppAndMatchingApp_hidesStatusBar() {
Settings.Global.putString(
mContext.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
"immersive.status=" + PACKAGE_NAME);
BarControlPolicy.reloadFromSetting(mContext);
int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
assertThat(visibilities[0]).isEqualTo(navigationBars());
assertThat(visibilities[1]).isEqualTo(statusBars());
}
@Test
public void getBarVisibilities_immersiveStatusForAppAndNonMatchingApp_showsSystemBars() {
Settings.Global.putString(
mContext.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
"immersive.status=" + PACKAGE_NAME);
BarControlPolicy.reloadFromSetting(mContext);
int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app");
assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
assertThat(visibilities[1]).isEqualTo(0);
}
@Test
public void getBarVisibilities_immersiveStatusForAppsAndNonApp_showsSystemBars() {
Settings.Global.putString(
mContext.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
"immersive.status=apps");
BarControlPolicy.reloadFromSetting(mContext);
int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
assertThat(visibilities[1]).isEqualTo(0);
}
@Test
public void getBarVisibilities_immersiveFullForAppAndMatchingApp_hidesSystemBars() {
Settings.Global.putString(
mContext.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
"immersive.full=" + PACKAGE_NAME);
BarControlPolicy.reloadFromSetting(mContext);
int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
assertThat(visibilities[0]).isEqualTo(0);
assertThat(visibilities[1]).isEqualTo(statusBars() | navigationBars());
}
@Test
public void getBarVisibilities_immersiveFullForAppAndNonMatchingApp_showsSystemBars() {
Settings.Global.putString(
mContext.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
"immersive.full=" + PACKAGE_NAME);
BarControlPolicy.reloadFromSetting(mContext);
int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app");
assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
assertThat(visibilities[1]).isEqualTo(0);
}
@Test
public void getBarVisibilities_immersiveFullForAppsAndNonApp_showsSystemBars() {
Settings.Global.putString(
mContext.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
"immersive.full=apps");
BarControlPolicy.reloadFromSetting(mContext);
int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
assertThat(visibilities[1]).isEqualTo(0);
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (C) 2020 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.wm;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.verify;
import android.car.settings.CarSettings;
import android.os.Handler;
import android.os.RemoteException;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.IWindowManager;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.TransactionPool;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class DisplaySystemBarsControllerTest extends SysuiTestCase {
private DisplaySystemBarsController mController;
private static final int DISPLAY_ID = 1;
@Mock
private SystemWindows mSystemWindows;
@Mock
private IWindowManager mIWindowManager;
@Mock
private DisplayController mDisplayController;
@Mock
private Handler mHandler;
@Mock
private TransactionPool mTransactionPool;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mSystemWindows.mContext = mContext;
mSystemWindows.mWmService = mIWindowManager;
mController = new DisplaySystemBarsController(
mSystemWindows,
mDisplayController,
mHandler,
mTransactionPool
);
}
@Test
public void onDisplayAdded_setsDisplayWindowInsetsControllerOnWMService()
throws RemoteException {
mController.onDisplayAdded(DISPLAY_ID);
verify(mIWindowManager).setDisplayWindowInsetsController(
eq(DISPLAY_ID), any(DisplaySystemBarsController.PerDisplay.class));
}
@Test
public void onDisplayAdded_loadsBarControlPolicyFilters() {
String text = "sample text";
Settings.Global.putString(
mContext.getContentResolver(),
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
text
);
mController.onDisplayAdded(DISPLAY_ID);
assertThat(BarControlPolicy.sSettingValue).isEqualTo(text);
}
@Test
public void onDisplayRemoved_unsetsDisplayWindowInsetsControllerInWMService()
throws RemoteException {
mController.onDisplayAdded(DISPLAY_ID);
mController.onDisplayRemoved(DISPLAY_ID);
verify(mIWindowManager).setDisplayWindowInsetsController(
DISPLAY_ID, /* displayWindowInsetsController= */ null);
}
}

View File

@ -251,6 +251,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mHandler.post(() -> startAnimation(false /* show */, false /* forceRestart */));
}
@Override
public void topFocusedWindowChanged(String packageName) {
// no-op
}
/**
* Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
*/

View File

@ -5747,6 +5747,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mRemoteInsetsController = controller;
}
/**
* Notifies the remote insets controller that the top focused window has changed.
*
* @param packageName The name of the package that is open in the top focused window.
*/
void topFocusedWindowChanged(String packageName) {
try {
mRemoteInsetsController.topFocusedWindowChanged(packageName);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to deliver package in top focused window change", e);
}
}
void notifyInsetsChanged() {
try {
mRemoteInsetsController.insetsChanged(

View File

@ -386,11 +386,6 @@ public class DisplayPolicy {
private int mForcingShowNavBarLayer;
private boolean mForceShowSystemBars;
/**
* Force the display of system bars regardless of other settings.
*/
private boolean mForceShowSystemBarsFromExternal;
private boolean mShowingDream;
private boolean mLastShowingDream;
private boolean mDreamingLockscreen;
@ -480,7 +475,6 @@ public class DisplayPolicy {
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer);
mForceShowSystemBarsFromExternal = r.getBoolean(R.bool.config_forceShowSystemBars);
mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
Context.ACCESSIBILITY_SERVICE);
@ -698,17 +692,6 @@ public class DisplayPolicy {
return mDockMode;
}
/**
* @see WindowManagerService.setForceShowSystemBars
*/
void setForceShowSystemBars(boolean forceShowSystemBars) {
mForceShowSystemBarsFromExternal = forceShowSystemBars;
}
boolean getForceShowSystemBars() {
return mForceShowSystemBarsFromExternal;
}
public boolean hasNavigationBar() {
return mHasNavigationBar;
}
@ -3550,8 +3533,7 @@ public class DisplayPolicy {
// We need to force system bars when the docked stack is visible, when the freeform stack
// is focused but also when we are resizing for the transitions when docked stack
// visibility changes.
mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing
|| mForceShowSystemBarsFromExternal;
mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing;
final boolean forceOpaqueStatusBar = mForceShowSystemBars && !isKeyguardShowing();
// apply translucent bar vis flags
@ -3926,8 +3908,8 @@ public class DisplayPolicy {
}
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
pw.print(prefix); pw.print("mForceShowSystemBarsFromExternal=");
pw.print(mForceShowSystemBarsFromExternal);
pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars");
pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
mStatusBarController.dump(pw, prefix);
mNavigationBarController.dump(pw, prefix);

View File

@ -46,7 +46,9 @@ import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.DisplayThread;
@ -97,12 +99,31 @@ class InsetsPolicy {
private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
private boolean mAnimatingShown;
/**
* Let remote insets controller control system bars regardless of other settings.
*/
private boolean mRemoteInsetsControllerControlsSystemBars;
private final float[] mTmpFloat9 = new float[9];
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
mDisplayContent = displayContent;
mPolicy = displayContent.getDisplayPolicy();
mRemoteInsetsControllerControlsSystemBars = mPolicy.getContext().getResources().getBoolean(
R.bool.config_remoteInsetsControllerControlsSystemBars);
}
boolean getRemoteInsetsControllerControlsSystemBars() {
return mRemoteInsetsControllerControlsSystemBars;
}
/**
* Used only for testing.
*/
@VisibleForTesting
void setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars) {
mRemoteInsetsControllerControlsSystemBars = controlsSystemBars;
}
/** Updates the target which can control system bars. */
@ -256,6 +277,11 @@ class InsetsPolicy {
// Notification shade has control anyways, no reason to force anything.
return focusedWin;
}
if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
focusedWin.mAttrs.packageName);
return mDisplayContent.mRemoteInsetsControlTarget;
}
if (forceShowsSystemBarsForWindowingMode) {
// Status bar is forcibly shown for the windowing mode which is a steady state.
// We don't want the client to control the status bar, and we will dispatch the real
@ -285,6 +311,11 @@ class InsetsPolicy {
// Notification shade has control anyways, no reason to force anything.
return focusedWin;
}
if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
focusedWin.mAttrs.packageName);
return mDisplayContent.mRemoteInsetsControlTarget;
}
if (forceShowsSystemBarsForWindowingMode) {
// Navigation bar is forcibly shown for the windowing mode which is a steady state.
// We don't want the client to control the navigation bar, and we will dispatch the real
@ -300,6 +331,28 @@ class InsetsPolicy {
return focusedWin;
}
/**
* Determines whether the remote insets controller should take control of system bars for all
* windows.
*/
boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) {
if (focusedWin == null) {
return false;
}
if (!mRemoteInsetsControllerControlsSystemBars) {
return false;
}
if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) {
// No remote insets control target to take control of insets.
return false;
}
// If necessary, auto can control application windows when
// config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases
// where we want to dictate system bar inset state for applications.
return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
&& focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
}
private boolean forceShowsStatusBarTransiently() {
final WindowState win = mPolicy.getStatusBar();
return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
@ -321,10 +374,7 @@ class InsetsPolicy {
// We need to force system bars when the docked stack is visible, when the freeform stack
// is visible but also when we are resizing for the transitions when docked stack
// visibility changes.
return isDockedStackVisible
|| isFreeformStackVisible
|| isResizing
|| mPolicy.getForceShowSystemBars();
return isDockedStackVisible || isFreeformStackVisible || isResizing;
}
@VisibleForTesting

View File

@ -5826,27 +5826,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
@Override
public void setForceShowSystemBars(boolean show) {
boolean isAutomotive = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE);
if (!isAutomotive) {
throw new UnsupportedOperationException("Force showing system bars is only supported"
+ "for Automotive use cases.");
}
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller does not hold permission "
+ android.Manifest.permission.STATUS_BAR);
}
synchronized (mGlobalLock) {
final PooledConsumer c = PooledLambda.obtainConsumer(
DisplayPolicy::setForceShowSystemBars, PooledLambda.__(), show);
mRoot.forAllDisplayPolicies(c);
c.recycle();
}
}
public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
!= PackageManager.PERMISSION_GRANTED) {

View File

@ -96,13 +96,10 @@ import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowRotationCallback;
import android.view.IDisplayWindowRotationController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl.Transaction;
@ -929,28 +926,6 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
}
private IDisplayWindowInsetsController createDisplayWindowInsetsController() {
return new IDisplayWindowInsetsController.Stub() {
@Override
public void insetsChanged(InsetsState insetsState) throws RemoteException {
}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] insetsSourceControls) throws RemoteException {
}
@Override
public void showInsets(int i, boolean b) throws RemoteException {
}
@Override
public void hideInsets(int i, boolean b) throws RemoteException {
}
};
}
@Test
public void testUpdateSystemGestureExclusion() throws Exception {
final DisplayContent dc = createNewDisplay();

View File

@ -67,7 +67,6 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Before;
@ -806,27 +805,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
new ToStringComparatorWrapper<>(simulatedInsetsState));
}
@Test
public void forceShowSystemBars_clearsSystemUIFlags() {
mDisplayPolicy.mLastSystemUiFlags |= SYSTEM_UI_FLAG_FULLSCREEN;
mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mSystemUiVisibility = SYSTEM_UI_FLAG_FULLSCREEN;
mDisplayPolicy.setForceShowSystemBars(true);
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
// triggers updateSystemUiVisibilityLw which will reset the flags as needed
int finishPostLayoutPolicyLw = mDisplayPolicy.focusChangedLw(mWindow, mWindow);
assertEquals(WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT, finishPostLayoutPolicyLw);
assertEquals(0, mDisplayPolicy.mLastSystemUiFlags);
assertEquals(0, mWindow.mAttrs.systemUiVisibility);
assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
}
@Test
public void testScreenDecorWindows() {
final WindowState decorWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, "decorWindow");

View File

@ -172,8 +172,9 @@ public class InsetsPolicyTest extends WindowTestsBase {
}
@Test
public void testControlsForDispatch_forceShowSystemBarsFromExternal_appHasNoControl() {
mDisplayContent.getDisplayPolicy().setForceShowSystemBars(true);
public void testControlsForDispatch_remoteInsetsControllerControlsBars_appHasNoControl() {
mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(true);
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");

View File

@ -60,24 +60,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
@Rule
public ExpectedException mExpectedException = ExpectedException.none();
@Test
public void testForceShowSystemBarsThrowsExceptionForNonAutomotive() {
if (!isAutomotive()) {
mExpectedException.expect(UnsupportedOperationException.class);
mWm.setForceShowSystemBars(true);
}
}
@Test
public void testForceShowSystemBarsDoesNotThrowExceptionForAutomotiveWithStatusBarPermission() {
if (isAutomotive()) {
mExpectedException.none();
mWm.setForceShowSystemBars(true);
}
}
private boolean isAutomotive() {
return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE);

View File

@ -41,11 +41,15 @@ import static org.mockito.Mockito.mock;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.IDisplayWindowInsetsController;
import android.view.IWindow;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.WindowManager;
@ -123,7 +127,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
mChildAppWindowBelow = createCommonWindow(mAppWindow,
TYPE_APPLICATION_MEDIA_OVERLAY,
"mChildAppWindowBelow");
mDisplayContent.getDisplayPolicy().setForceShowSystemBars(false);
mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(false);
// Adding a display will cause freezing the display. Make sure to wait until it's
// unfrozen to not run into race conditions with the tests.
@ -344,6 +348,32 @@ class WindowTestsBase extends SystemServiceTestsBase {
return createNewDisplay(displayInfo, false /* supportIme */);
}
IDisplayWindowInsetsController createDisplayWindowInsetsController() {
return new IDisplayWindowInsetsController.Stub() {
@Override
public void insetsChanged(InsetsState insetsState) throws RemoteException {
}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] insetsSourceControls) throws RemoteException {
}
@Override
public void showInsets(int i, boolean b) throws RemoteException {
}
@Override
public void hideInsets(int i, boolean b) throws RemoteException {
}
@Override
public void topFocusedWindowChanged(String packageName) {
}
};
}
/** Sets the default minimum task size to 1 so that tests can use small task sizes */
void removeGlobalMinSizeRestriction() {
mWm.mAtmService.mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp = 1;