OSDN Git Service

Global system setting to override certain window policy.
authorJohn Spurlock <jspurlock@google.com>
Fri, 17 Jan 2014 20:22:06 +0000 (15:22 -0500)
committerJohn Spurlock <jspurlock@google.com>
Wed, 5 Feb 2014 21:45:23 +0000 (16:45 -0500)
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

core/java/android/provider/Settings.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
policy/src/com/android/internal/policy/impl/BarController.java
policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java
policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
policy/src/com/android/internal/policy/impl/PolicyControl.java [new file with mode: 0644]

index b9a898e..9332578 100644 (file)
@@ -6057,6 +6057,14 @@ public final class Settings {
          */
         public static final String WIFI_BOUNCE_DELAY_OVERRIDE_MS = "wifi_bounce_delay_override_ms";
 
+        /**
+         * Defines global runtime overrides to window policy.
+         *
+         * See {@link com.android.internal.policy.impl.PolicyControl} for value format.
+         *
+         * @hide
+         */
+        public static final String POLICY_CONTROL = "policy_control";
 
         /**
          * Settings to backup. This is here so that it's in the same place as the settings
index b72c25d..44e7dac 100644 (file)
@@ -1914,7 +1914,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
             }
             if (sbModeChanged || nbModeChanged) {
                 // update transient bar autohide
-                if (sbMode == MODE_SEMI_TRANSPARENT || nbMode == MODE_SEMI_TRANSPARENT) {
+                if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
                     scheduleAutohide();
                 } else {
                     cancelAutohide();
index 0ce4b12..fc49a56 100644 (file)
@@ -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;
index b734c41..507d385 100644 (file)
@@ -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);
index ce6166a..b6a4052 100644 (file)
@@ -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 (file)
index 0000000..e6a7d76
--- /dev/null
@@ -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);
+        }
+    }
+}