1 /* 2 * Copyright (C) 2018 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.wm; 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 java.io.PrintWriter; 30 import 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 */ 50 class PolicyControl { 51 private static final String TAG = "PolicyControl"; 52 private static final 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 getSystemUiVisibility(WindowState win, LayoutParams attrs)64 static int getSystemUiVisibility(WindowState win, LayoutParams attrs) { 65 attrs = attrs != null ? attrs : win.getAttrs(); 66 int vis = win != null ? win.getSystemUiVisibility() 67 : (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility); 68 if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { 69 vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 70 | View.SYSTEM_UI_FLAG_FULLSCREEN 71 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 72 vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE 73 | View.STATUS_BAR_TRANSLUCENT); 74 } 75 if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) { 76 vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 77 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 78 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 79 vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE 80 | View.NAVIGATION_BAR_TRANSLUCENT); 81 } 82 return vis; 83 } 84 getWindowFlags(WindowState win, LayoutParams attrs)85 static int getWindowFlags(WindowState win, LayoutParams attrs) { 86 attrs = attrs != null ? attrs : win.getAttrs(); 87 int flags = attrs.flags; 88 if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { 89 flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; 90 flags &= ~(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS 91 | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); 92 } 93 if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) { 94 flags &= ~WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 95 } 96 return flags; 97 } 98 adjustClearableFlags(WindowState win, int clearableFlags)99 static int adjustClearableFlags(WindowState win, int clearableFlags) { 100 final LayoutParams attrs = win != null ? win.getAttrs() : null; 101 if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { 102 clearableFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; 103 } 104 return clearableFlags; 105 } 106 disableImmersiveConfirmation(String pkg)107 static boolean disableImmersiveConfirmation(String pkg) { 108 return (sImmersivePreconfirmationsFilter != null 109 && sImmersivePreconfirmationsFilter.matches(pkg)) 110 || ActivityManager.isRunningInTestHarness(); 111 } 112 reloadFromSetting(Context context)113 static boolean reloadFromSetting(Context context) { 114 if (DEBUG) Slog.d(TAG, "reloadFromSetting()"); 115 String value = null; 116 try { 117 value = Settings.Global.getStringForUser(context.getContentResolver(), 118 Settings.Global.POLICY_CONTROL, 119 UserHandle.USER_CURRENT); 120 if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) { 121 return false; 122 } 123 setFilters(value); 124 sSettingValue = value; 125 } catch (Throwable t) { 126 Slog.w(TAG, "Error loading policy control, value=" + value, t); 127 return false; 128 } 129 return true; 130 } 131 dump(String prefix, PrintWriter pw)132 static void dump(String prefix, PrintWriter pw) { 133 dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw); 134 dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw); 135 dump("sImmersivePreconfirmationsFilter", sImmersivePreconfirmationsFilter, prefix, pw); 136 } 137 dump(String name, Filter filter, String prefix, PrintWriter pw)138 private static void dump(String name, Filter filter, String prefix, PrintWriter pw) { 139 pw.print(prefix); pw.print("PolicyControl."); pw.print(name); pw.print('='); 140 if (filter == null) { 141 pw.println("null"); 142 } else { 143 filter.dump(pw); pw.println(); 144 } 145 } 146 setFilters(String value)147 private static void setFilters(String value) { 148 if (DEBUG) Slog.d(TAG, "setFilters: " + value); 149 sImmersiveStatusFilter = null; 150 sImmersiveNavigationFilter = null; 151 sImmersivePreconfirmationsFilter = null; 152 if (value != null) { 153 String[] nvps = value.split(":"); 154 for (String nvp : nvps) { 155 int i = nvp.indexOf('='); 156 if (i == -1) continue; 157 String n = nvp.substring(0, i); 158 String v = nvp.substring(i + 1); 159 if (n.equals(NAME_IMMERSIVE_FULL)) { 160 Filter f = Filter.parse(v); 161 sImmersiveStatusFilter = sImmersiveNavigationFilter = f; 162 if (sImmersivePreconfirmationsFilter == null) { 163 sImmersivePreconfirmationsFilter = f; 164 } 165 } else if (n.equals(NAME_IMMERSIVE_STATUS)) { 166 Filter f = Filter.parse(v); 167 sImmersiveStatusFilter = f; 168 } else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) { 169 Filter f = Filter.parse(v); 170 sImmersiveNavigationFilter = f; 171 if (sImmersivePreconfirmationsFilter == null) { 172 sImmersivePreconfirmationsFilter = f; 173 } 174 } else if (n.equals(NAME_IMMERSIVE_PRECONFIRMATIONS)) { 175 Filter f = Filter.parse(v); 176 sImmersivePreconfirmationsFilter = f; 177 } 178 } 179 } 180 if (DEBUG) { 181 Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter); 182 Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter); 183 Slog.d(TAG, "immersivePreconfirmationsFilter: " + sImmersivePreconfirmationsFilter); 184 } 185 } 186 187 private static class Filter { 188 private static final String ALL = "*"; 189 private static final String APPS = "apps"; 190 191 private final ArraySet<String> mWhitelist; 192 private final ArraySet<String> mBlacklist; 193 Filter(ArraySet<String> whitelist, ArraySet<String> blacklist)194 private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { 195 mWhitelist = whitelist; 196 mBlacklist = blacklist; 197 } 198 matches(LayoutParams attrs)199 boolean matches(LayoutParams attrs) { 200 if (attrs == null) return false; 201 boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW 202 && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; 203 if (isApp && mBlacklist.contains(APPS)) return false; 204 if (onBlacklist(attrs.packageName)) return false; 205 if (isApp && mWhitelist.contains(APPS)) return true; 206 return onWhitelist(attrs.packageName); 207 } 208 matches(String packageName)209 boolean matches(String packageName) { 210 return !onBlacklist(packageName) && onWhitelist(packageName); 211 } 212 onBlacklist(String packageName)213 private boolean onBlacklist(String packageName) { 214 return mBlacklist.contains(packageName) || mBlacklist.contains(ALL); 215 } 216 onWhitelist(String packageName)217 private boolean onWhitelist(String packageName) { 218 return mWhitelist.contains(ALL) || mWhitelist.contains(packageName); 219 } 220 dump(PrintWriter pw)221 void dump(PrintWriter pw) { 222 pw.print("Filter["); 223 dump("whitelist", mWhitelist, pw); pw.print(','); 224 dump("blacklist", mBlacklist, pw); pw.print(']'); 225 } 226 dump(String name, ArraySet<String> set, PrintWriter pw)227 private void dump(String name, ArraySet<String> set, PrintWriter pw) { 228 pw.print(name); pw.print("=("); 229 final int n = set.size(); 230 for (int i = 0; i < n; i++) { 231 if (i > 0) pw.print(','); 232 pw.print(set.valueAt(i)); 233 } 234 pw.print(')'); 235 } 236 237 @Override toString()238 public String toString() { 239 StringWriter sw = new StringWriter(); 240 dump(new PrintWriter(sw, true)); 241 return sw.toString(); 242 } 243 244 // value = comma-delimited list of tokens, where token = (package name|apps|*) 245 // e.g. "com.package1", or "apps, com.android.keyguard" or "*" parse(String value)246 static Filter parse(String value) { 247 if (value == null) return null; 248 ArraySet<String> whitelist = new ArraySet<String>(); 249 ArraySet<String> blacklist = new ArraySet<String>(); 250 for (String token : value.split(",")) { 251 token = token.trim(); 252 if (token.startsWith("-") && token.length() > 1) { 253 token = token.substring(1); 254 blacklist.add(token); 255 } else { 256 whitelist.add(token); 257 } 258 } 259 return new Filter(whitelist, blacklist); 260 } 261 } 262 } 263