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