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();
}
if (mImmersiveModeConfirmation != null) {
mImmersiveModeConfirmation.loadSetting();
}
+ PolicyControl.reloadFromSetting(mContext);
}
if (updateRotation) {
updateRotation(true);
@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))
// 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
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;
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;
}
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 {
&& 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 ?
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;
}
}
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
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;
(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;
pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation);
mStatusBarController.dump(pw, prefix);
mNavigationBarController.dump(pw, prefix);
+ PolicyControl.dump(prefix, pw);
}
}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}