PolicyControl.java revision c6d1c60fb1de5a48e85ddbfe9b66ccc9285df4af
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.policy.impl; 18 19import android.content.Context; 20import android.os.UserHandle; 21import android.provider.Settings; 22import android.util.ArraySet; 23import android.util.Slog; 24import android.view.View; 25import android.view.WindowManager; 26import android.view.WindowManager.LayoutParams; 27import android.view.WindowManagerPolicy.WindowState; 28 29import java.io.PrintWriter; 30import java.io.StringWriter; 31 32/** 33 * Runtime adjustments applied to the global window policy. 34 * 35 * This includes forcing immersive mode behavior for one or both system bars (based on a package 36 * list) and permanently disabling immersive mode confirmations for specific packages. 37 * 38 * Control by setting {@link Settings.Global.POLICY_CONTROL} to one or more name-value pairs. 39 * e.g. 40 * to force immersive mode everywhere: 41 * "immersive.full=*" 42 * to force transient status for all apps except a specific package: 43 * "immersive.status=apps,-com.package" 44 * to disable the immersive mode confirmations for specific packages: 45 * "immersive.preconfirms=com.package.one,com.package.two" 46 * 47 * Separate multiple name-value pairs with ':' 48 * e.g. "immersive.status=apps:immersive.preconfirms=*" 49 */ 50public class PolicyControl { 51 private static String TAG = "PolicyControl"; 52 private static boolean DEBUG = false; 53 54 private static final String NAME_IMMERSIVE_FULL = "immersive.full"; 55 private static final String NAME_IMMERSIVE_STATUS = "immersive.status"; 56 private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation"; 57 private static final String NAME_IMMERSIVE_PRECONFIRMATIONS = "immersive.preconfirms"; 58 59 private static String sSettingValue; 60 private static Filter sImmersivePreconfirmationsFilter; 61 private static Filter sImmersiveStatusFilter; 62 private static Filter sImmersiveNavigationFilter; 63 64 public static int getSystemUiVisibility(WindowState win) { 65 int vis = win.getSystemUiVisibility(); 66 if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(win)) { 67 vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 68 | View.SYSTEM_UI_FLAG_FULLSCREEN 69 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 70 vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE 71 | View.STATUS_BAR_TRANSLUCENT); 72 } 73 if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(win)) { 74 vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 75 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 76 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 77 vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE 78 | View.NAVIGATION_BAR_TRANSLUCENT); 79 } 80 return vis; 81 } 82 83 public static int getWindowFlags(WindowState win, LayoutParams attrs) { 84 int flags = (attrs != null ? attrs : win.getAttrs()).flags; 85 if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(win)) { 86 flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; 87 flags &= ~(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS 88 | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); 89 } 90 if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(win)) { 91 flags &= ~WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 92 } 93 return flags; 94 } 95 96 public static int adjustClearableFlags(WindowState win, int clearableFlags) { 97 if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(win)) { 98 clearableFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; 99 } 100 return clearableFlags; 101 } 102 103 public static boolean disableImmersiveConfirmation(String pkg) { 104 return sImmersivePreconfirmationsFilter != null 105 && sImmersivePreconfirmationsFilter.matches(pkg); 106 } 107 108 public static void reloadFromSetting(Context context) { 109 if (DEBUG) Slog.d(TAG, "reloadFromSetting()"); 110 String value = null; 111 try { 112 value = Settings.Global.getStringForUser(context.getContentResolver(), 113 Settings.Global.POLICY_CONTROL, 114 UserHandle.USER_CURRENT); 115 if (sSettingValue != null && sSettingValue.equals(value)) return; 116 setFilters(value); 117 sSettingValue = value; 118 } catch (Throwable t) { 119 Slog.w(TAG, "Error loading policy control, value=" + value, t); 120 } 121 } 122 123 public static void dump(String prefix, PrintWriter pw) { 124 dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw); 125 dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw); 126 dump("sImmersivePreconfirmationsFilter", sImmersivePreconfirmationsFilter, prefix, pw); 127 } 128 129 private static void dump(String name, Filter filter, String prefix, PrintWriter pw) { 130 pw.print(prefix); pw.print("PolicyControl."); pw.print(name); pw.print('='); 131 if (filter == null) { 132 pw.println("null"); 133 } else { 134 filter.dump(pw); pw.println(); 135 } 136 } 137 138 private static void setFilters(String value) { 139 if (DEBUG) Slog.d(TAG, "setFilters: " + value); 140 sImmersiveStatusFilter = null; 141 sImmersiveNavigationFilter = null; 142 sImmersivePreconfirmationsFilter = null; 143 String[] nvps = value.split(":"); 144 for (String nvp : nvps) { 145 int i = nvp.indexOf('='); 146 if (i == -1) continue; 147 String n = nvp.substring(0, i); 148 String v = nvp.substring(i + 1); 149 if (n.equals(NAME_IMMERSIVE_FULL)) { 150 Filter f = Filter.parse(v); 151 sImmersiveStatusFilter = sImmersiveNavigationFilter = f; 152 if (sImmersivePreconfirmationsFilter == null) { 153 sImmersivePreconfirmationsFilter = f; 154 } 155 } else if (n.equals(NAME_IMMERSIVE_STATUS)) { 156 Filter f = Filter.parse(v); 157 sImmersiveStatusFilter = f; 158 } else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) { 159 Filter f = Filter.parse(v); 160 sImmersiveNavigationFilter = f; 161 if (sImmersivePreconfirmationsFilter == null) { 162 sImmersivePreconfirmationsFilter = f; 163 } 164 } else if (n.equals(NAME_IMMERSIVE_PRECONFIRMATIONS)) { 165 Filter f = Filter.parse(v); 166 sImmersivePreconfirmationsFilter = f; 167 } 168 } 169 if (DEBUG) { 170 Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter); 171 Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter); 172 Slog.d(TAG, "immersivePreconfirmationsFilter: " + sImmersivePreconfirmationsFilter); 173 } 174 } 175 176 private static class Filter { 177 private static final String ALL = "*"; 178 private static final String APPS = "apps"; 179 180 private final ArraySet<String> mWhitelist; 181 private final ArraySet<String> mBlacklist; 182 183 private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { 184 mWhitelist = whitelist; 185 mBlacklist = blacklist; 186 } 187 188 boolean matches(WindowState win) { 189 if (win == null) return false; 190 LayoutParams attrs = win.getAttrs(); 191 if (attrs == null) return false; 192 boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW 193 && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; 194 if (isApp && mBlacklist.contains(APPS)) return false; 195 if (onBlacklist(attrs.packageName)) return false; 196 if (isApp && mWhitelist.contains(APPS)) return true; 197 return onWhitelist(attrs.packageName); 198 } 199 200 boolean matches(String packageName) { 201 return !onBlacklist(packageName) && onWhitelist(packageName); 202 } 203 204 private boolean onBlacklist(String packageName) { 205 return mBlacklist.contains(packageName) || mBlacklist.contains(ALL); 206 } 207 208 private boolean onWhitelist(String packageName) { 209 return mWhitelist.contains(ALL) || mWhitelist.contains(packageName); 210 } 211 212 void dump(PrintWriter pw) { 213 pw.print("Filter["); 214 dump("whitelist", mWhitelist, pw); pw.print(','); 215 dump("blacklist", mBlacklist, pw); pw.print(']'); 216 } 217 218 private void dump(String name, ArraySet<String> set, PrintWriter pw) { 219 pw.print(name); pw.print("=("); 220 final int n = set.size(); 221 for (int i = 0; i < n; i++) { 222 if (i > 0) pw.print(','); 223 pw.print(set.valueAt(i)); 224 } 225 pw.print(')'); 226 } 227 228 @Override 229 public String toString() { 230 StringWriter sw = new StringWriter(); 231 dump(new PrintWriter(sw, true)); 232 return sw.toString(); 233 } 234 235 // value = comma-delimited list of tokens, where token = (package name|apps|*) 236 // e.g. "com.package1", or "apps, com.android.keyguard" or "*" 237 static Filter parse(String value) { 238 if (value == null) return null; 239 ArraySet<String> whitelist = new ArraySet<String>(); 240 ArraySet<String> blacklist = new ArraySet<String>(); 241 for (String token : value.split(",")) { 242 token = token.trim(); 243 if (token.startsWith("-") && token.length() > 1) { 244 token = token.substring(1); 245 blacklist.add(token); 246 } else { 247 whitelist.add(token); 248 } 249 } 250 return new Filter(whitelist, blacklist); 251 } 252 } 253} 254