diff options
author | John Spurlock <jspurlock@google.com> | 2014-01-17 15:22:06 -0500 |
---|---|---|
committer | John Spurlock <jspurlock@google.com> | 2014-02-05 16:45:23 -0500 |
commit | c6d1c60fb1de5a48e85ddbfe9b66ccc9285df4af (patch) | |
tree | c6153be8b0cb7e61a8caa9c371b0f0803eb1bbc8 /policy | |
parent | 2a1fdefe16046860fcaf524af4fc380d850d28bb (diff) | |
download | frameworks_base-c6d1c60fb1de5a48e85ddbfe9b66ccc9285df4af.zip frameworks_base-c6d1c60fb1de5a48e85ddbfe9b66ccc9285df4af.tar.gz frameworks_base-c6d1c60fb1de5a48e85ddbfe9b66ccc9285df4af.tar.bz2 |
Global system setting to override certain window policy.
Specifically, the ability to force immersive-mode-like behavior
on the status bar, the navigation bar (or both) on a
package-by-package basis - and to disable immersive mode
confirmations for specific packages.
Change-Id: I2df7092a91eceeb815367ef917dd7289f4f2b27e
Diffstat (limited to 'policy')
4 files changed, 276 insertions, 15 deletions
diff --git a/policy/src/com/android/internal/policy/impl/BarController.java b/policy/src/com/android/internal/policy/impl/BarController.java index 0ce4b12..fc49a569 100644 --- a/policy/src/com/android/internal/policy/impl/BarController.java +++ b/policy/src/com/android/internal/policy/impl/BarController.java @@ -108,7 +108,7 @@ public class BarController { if (mWin != null) { if (win != null && (win.getAttrs().privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) { - if ((win.getAttrs().flags & mTranslucentWmFlag) != 0) { + if ((PolicyControl.getWindowFlags(win, null) & mTranslucentWmFlag) != 0) { vis |= mTranslucentFlag; } else { vis &= ~mTranslucentFlag; diff --git a/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java b/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java index b734c41..507d385 100644 --- a/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java +++ b/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java @@ -117,7 +117,7 @@ public class ImmersiveModeConfirmation { } public void immersiveModeChanged(String pkg, boolean isImmersiveMode) { - if (pkg == null) { + if (pkg == null || PolicyControl.disableImmersiveConfirmation(pkg)) { return; } mHandler.removeMessages(H.SHOW); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index ce6166a..b6a4052 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -526,6 +526,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { resolver.registerContentObserver(Settings.System.getUriFor( Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.POLICY_CONTROL), false, this, + UserHandle.USER_ALL); updateSettings(); } @@ -1167,6 +1170,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mImmersiveModeConfirmation != null) { mImmersiveModeConfirmation.loadSetting(); } + PolicyControl.reloadFromSetting(mContext); } if (updateRotation) { updateRotation(true); @@ -2564,7 +2568,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) { - final int fl = attrs.flags; + final int fl = PolicyControl.getWindowFlags(null, attrs); final int systemUiVisibility = (attrs.systemUiVisibility|attrs.subtreeSystemUiVisibility); if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) @@ -2737,7 +2741,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // We currently want to hide the navigation UI. mNavigationBarController.setBarShowingLw(false); } - if (navVisible && !navTranslucent && !mNavigationBar.isAnimatingLw() + if (navVisible && !navTranslucent && !navAllowedHidden + && !mNavigationBar.isAnimatingLw() && !mNavigationBarController.wasRecentlyTranslucent()) { // If the opaque nav bar is currently requested to be visible, // and not in the process of animating on or off, then @@ -2946,9 +2951,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { offsetInputMethodWindowLw(mLastInputMethodWindow); } - final int fl = attrs.flags; + final int fl = PolicyControl.getWindowFlags(win, attrs); final int sim = attrs.softInputMode; - final int sysUiFl = win.getSystemUiVisibility(); + final int sysUiFl = PolicyControl.getSystemUiVisibility(win); final Rect pf = mTmpParentFrame; final Rect df = mTmpDisplayFrame; @@ -3366,6 +3371,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowManager.LayoutParams attrs) { if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw=" + win.isVisibleOrBehindKeyguardLw()); + final int fl = PolicyControl.getWindowFlags(win, attrs); if (mTopFullscreenOpaqueWindowState == null && win.isVisibleLw() && attrs.type == TYPE_INPUT_METHOD) { mForcingShowNavBar = true; @@ -3373,7 +3379,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (mTopFullscreenOpaqueWindowState == null && win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()) { - if ((attrs.flags & FLAG_FORCE_NOT_FULLSCREEN) != 0) { + if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { if (attrs.type == TYPE_KEYGUARD) { mForceStatusBarFromKeyguard = true; } else { @@ -3400,12 +3406,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) { if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win); mTopFullscreenOpaqueWindowState = win; - if ((attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) { + if ((fl & FLAG_SHOW_WHEN_LOCKED) != 0) { if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mHideLockScreen to true by win " + win); mHideLockScreen = true; mForceStatusBarFromKeyguard = false; } - if ((attrs.flags & FLAG_DISMISS_KEYGUARD) != 0 + if ((fl & FLAG_DISMISS_KEYGUARD) != 0 && mDismissKeyguard == DISMISS_KEYGUARD_NONE) { if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mDismissKeyguard true by win " + win); mDismissKeyguard = mWinDismissingKeyguard == win ? @@ -3413,7 +3419,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWinDismissingKeyguard = win; mForceStatusBarFromKeyguard = mShowingLockscreen && isKeyguardSecure(); } - if ((attrs.flags & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { + if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { mAllowLockscreenWhenOn = true; } } @@ -3455,13 +3461,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { mLastSystemUiFlags, mLastSystemUiFlags); } } else if (mTopFullscreenOpaqueWindowState != null) { + final int fl = PolicyControl.getWindowFlags(null, lp); if (localLOGV) { Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw() + " shown frame: " + mTopFullscreenOpaqueWindowState.getShownFrameLw()); Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs() - + " lp.flags=0x" + Integer.toHexString(lp.flags)); + + " lp.flags=0x" + Integer.toHexString(fl)); } - topIsFullscreen = (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0 + topIsFullscreen = (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0 || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; // The subtle difference between the window for mTopFullscreenOpaqueWindowState // and mTopIsFullscreen is that that mTopIsFullscreen is set only if the window @@ -5061,11 +5068,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } - int tmpVisibility = win.getSystemUiVisibility() + int tmpVisibility = PolicyControl.getSystemUiVisibility(win) & ~mResettingSystemUiFlags & ~mForceClearedSystemUiFlags; if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) { - tmpVisibility &= ~View.SYSTEM_UI_CLEARABLE_FLAGS; + tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS); } final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); final int diff = visibility ^ mLastSystemUiFlags; @@ -5124,7 +5131,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; boolean hideStatusBarWM = mTopFullscreenOpaqueWindowState != null && - (mTopFullscreenOpaqueWindowState.getAttrs().flags + (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null) & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; boolean hideStatusBarSysui = (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; @@ -5412,5 +5419,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation); mStatusBarController.dump(pw, prefix); mNavigationBarController.dump(pw, prefix); + PolicyControl.dump(prefix, pw); } } diff --git a/policy/src/com/android/internal/policy/impl/PolicyControl.java b/policy/src/com/android/internal/policy/impl/PolicyControl.java new file mode 100644 index 0000000..e6a7d76 --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/PolicyControl.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2014 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.internal.policy.impl; + +import android.content.Context; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.ArraySet; +import android.util.Slog; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.WindowManagerPolicy.WindowState; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Runtime adjustments applied to the global window policy. + * + * This includes forcing immersive mode behavior for one or both system bars (based on a package + * list) and permanently disabling immersive mode confirmations for specific packages. + * + * Control by setting {@link Settings.Global.POLICY_CONTROL} to one or more name-value pairs. + * e.g. + * to force immersive mode everywhere: + * "immersive.full=*" + * to force transient status for all apps except a specific package: + * "immersive.status=apps,-com.package" + * to disable the immersive mode confirmations for specific packages: + * "immersive.preconfirms=com.package.one,com.package.two" + * + * Separate multiple name-value pairs with ':' + * e.g. "immersive.status=apps:immersive.preconfirms=*" + */ +public class PolicyControl { + private static String TAG = "PolicyControl"; + private static 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"; + private static final String NAME_IMMERSIVE_PRECONFIRMATIONS = "immersive.preconfirms"; + + private static String sSettingValue; + private static Filter sImmersivePreconfirmationsFilter; + private static Filter sImmersiveStatusFilter; + private static Filter sImmersiveNavigationFilter; + + public static int getSystemUiVisibility(WindowState win) { + int vis = win.getSystemUiVisibility(); + if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(win)) { + vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.STATUS_BAR_TRANSLUCENT); + } + if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(win)) { + vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.NAVIGATION_BAR_TRANSLUCENT); + } + return vis; + } + + public static int getWindowFlags(WindowState win, LayoutParams attrs) { + int flags = (attrs != null ? attrs : win.getAttrs()).flags; + if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(win)) { + flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; + flags &= ~(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + } + if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(win)) { + flags &= ~WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; + } + return flags; + } + + public static int adjustClearableFlags(WindowState win, int clearableFlags) { + if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(win)) { + clearableFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; + } + return clearableFlags; + } + + public static boolean disableImmersiveConfirmation(String pkg) { + return sImmersivePreconfirmationsFilter != null + && sImmersivePreconfirmationsFilter.matches(pkg); + } + + public static void reloadFromSetting(Context context) { + if (DEBUG) Slog.d(TAG, "reloadFromSetting()"); + String value = null; + try { + value = Settings.Global.getStringForUser(context.getContentResolver(), + Settings.Global.POLICY_CONTROL, + UserHandle.USER_CURRENT); + if (sSettingValue != null && sSettingValue.equals(value)) return; + setFilters(value); + sSettingValue = value; + } catch (Throwable t) { + Slog.w(TAG, "Error loading policy control, value=" + value, t); + } + } + + public static void dump(String prefix, PrintWriter pw) { + dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw); + dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw); + dump("sImmersivePreconfirmationsFilter", sImmersivePreconfirmationsFilter, prefix, pw); + } + + private static void dump(String name, Filter filter, String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("PolicyControl."); pw.print(name); pw.print('='); + if (filter == null) { + pw.println("null"); + } else { + filter.dump(pw); pw.println(); + } + } + + private static void setFilters(String value) { + if (DEBUG) Slog.d(TAG, "setFilters: " + value); + sImmersiveStatusFilter = null; + sImmersiveNavigationFilter = null; + sImmersivePreconfirmationsFilter = 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; + if (sImmersivePreconfirmationsFilter == null) { + sImmersivePreconfirmationsFilter = 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 (sImmersivePreconfirmationsFilter == null) { + sImmersivePreconfirmationsFilter = f; + } + } else if (n.equals(NAME_IMMERSIVE_PRECONFIRMATIONS)) { + Filter f = Filter.parse(v); + sImmersivePreconfirmationsFilter = f; + } + } + if (DEBUG) { + Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter); + Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter); + Slog.d(TAG, "immersivePreconfirmationsFilter: " + sImmersivePreconfirmationsFilter); + } + } + + private static class Filter { + private static final String ALL = "*"; + private static final String APPS = "apps"; + + private final ArraySet<String> mWhitelist; + private final ArraySet<String> mBlacklist; + + private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { + mWhitelist = whitelist; + mBlacklist = blacklist; + } + + boolean matches(WindowState win) { + if (win == null) return false; + LayoutParams attrs = win.getAttrs(); + if (attrs == null) return false; + boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + if (isApp && mBlacklist.contains(APPS)) return false; + if (onBlacklist(attrs.packageName)) return false; + if (isApp && mWhitelist.contains(APPS)) return true; + return onWhitelist(attrs.packageName); + } + + boolean matches(String packageName) { + return !onBlacklist(packageName) && 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("=("); + final 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|apps|*) + // e.g. "com.package1", or "apps, 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); + } + } +} |