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; 18 19 import android.app.ActivityManager; 20 import android.content.pm.FeatureInfo; 21 import android.os.*; 22 import android.os.Process; 23 import android.util.ArrayMap; 24 import android.util.ArraySet; 25 import android.util.Slog; 26 import android.util.SparseArray; 27 import android.util.Xml; 28 29 import libcore.io.IoUtils; 30 31 import com.android.internal.util.XmlUtils; 32 33 import org.xmlpull.v1.XmlPullParser; 34 import org.xmlpull.v1.XmlPullParserException; 35 36 import java.io.File; 37 import java.io.FileNotFoundException; 38 import java.io.FileReader; 39 import java.io.IOException; 40 41 import static com.android.internal.util.ArrayUtils.appendInt; 42 43 /** 44 * Loads global system configuration info. 45 */ 46 public class SystemConfig { 47 static final String TAG = "SystemConfig"; 48 49 static SystemConfig sInstance; 50 51 // Group-ids that are given to all packages as read from etc/permissions/*.xml. 52 int[] mGlobalGids; 53 54 // These are the built-in uid -> permission mappings that were read from the 55 // system configuration files. 56 final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>(); 57 58 // These are the built-in shared libraries that were read from the 59 // system configuration files. Keys are the library names; strings are the 60 // paths to the libraries. 61 final ArrayMap<String, String> mSharedLibraries = new ArrayMap<>(); 62 63 // These are the features this devices supports that were read from the 64 // system configuration files. 65 final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>(); 66 67 // These are the features which this device doesn't support; the OEM 68 // partition uses these to opt-out of features from the system image. 69 final ArraySet<String> mUnavailableFeatures = new ArraySet<>(); 70 71 public static final class PermissionEntry { 72 public final String name; 73 public int[] gids; 74 public boolean perUser; 75 76 PermissionEntry(String name, boolean perUser) { 77 this.name = name; 78 this.perUser = perUser; 79 } 80 } 81 82 // These are the permission -> gid mappings that were read from the 83 // system configuration files. 84 final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>(); 85 86 // These are the packages that are white-listed to be able to run in the 87 // background while in power save mode (but not whitelisted from device idle modes), 88 // as read from the configuration files. 89 final ArraySet<String> mAllowInPowerSaveExceptIdle = new ArraySet<>(); 90 91 // These are the packages that are white-listed to be able to run in the 92 // background while in power save mode, as read from the configuration files. 93 final ArraySet<String> mAllowInPowerSave = new ArraySet<>(); 94 95 // These are the app package names that should not allow IME switching. 96 final ArraySet<String> mFixedImeApps = new ArraySet<>(); 97 98 // These are the package names of apps which should be in the 'always' 99 // URL-handling state upon factory reset. 100 final ArraySet<String> mLinkedApps = new ArraySet<>(); 101 102 public static SystemConfig getInstance() { 103 synchronized (SystemConfig.class) { 104 if (sInstance == null) { 105 sInstance = new SystemConfig(); 106 } 107 return sInstance; 108 } 109 } 110 111 public int[] getGlobalGids() { 112 return mGlobalGids; 113 } 114 115 public SparseArray<ArraySet<String>> getSystemPermissions() { 116 return mSystemPermissions; 117 } 118 119 public ArrayMap<String, String> getSharedLibraries() { 120 return mSharedLibraries; 121 } 122 123 public ArrayMap<String, FeatureInfo> getAvailableFeatures() { 124 return mAvailableFeatures; 125 } 126 127 public ArrayMap<String, PermissionEntry> getPermissions() { 128 return mPermissions; 129 } 130 131 public ArraySet<String> getAllowInPowerSaveExceptIdle() { 132 return mAllowInPowerSaveExceptIdle; 133 } 134 135 public ArraySet<String> getAllowInPowerSave() { 136 return mAllowInPowerSave; 137 } 138 139 public ArraySet<String> getFixedImeApps() { 140 return mFixedImeApps; 141 } 142 143 public ArraySet<String> getLinkedApps() { 144 return mLinkedApps; 145 } 146 147 SystemConfig() { 148 // Read configuration from system 149 readPermissions(Environment.buildPath( 150 Environment.getRootDirectory(), "etc", "sysconfig"), false); 151 // Read configuration from the old permissions dir 152 readPermissions(Environment.buildPath( 153 Environment.getRootDirectory(), "etc", "permissions"), false); 154 // Only read features from OEM config 155 readPermissions(Environment.buildPath( 156 Environment.getOemDirectory(), "etc", "sysconfig"), true); 157 readPermissions(Environment.buildPath( 158 Environment.getOemDirectory(), "etc", "permissions"), true); 159 } 160 161 void readPermissions(File libraryDir, boolean onlyFeatures) { 162 // Read permissions from given directory. 163 if (!libraryDir.exists() || !libraryDir.isDirectory()) { 164 if (!onlyFeatures) { 165 Slog.w(TAG, "No directory " + libraryDir + ", skipping"); 166 } 167 return; 168 } 169 if (!libraryDir.canRead()) { 170 Slog.w(TAG, "Directory " + libraryDir + " cannot be read"); 171 return; 172 } 173 174 // Iterate over the files in the directory and scan .xml files 175 File platformFile = null; 176 for (File f : libraryDir.listFiles()) { 177 // We'll read platform.xml last 178 if (f.getPath().endsWith("etc/permissions/platform.xml")) { 179 platformFile = f; 180 continue; 181 } 182 183 if (!f.getPath().endsWith(".xml")) { 184 Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); 185 continue; 186 } 187 if (!f.canRead()) { 188 Slog.w(TAG, "Permissions library file " + f + " cannot be read"); 189 continue; 190 } 191 192 readPermissionsFromXml(f, onlyFeatures); 193 } 194 195 // Read platform permissions last so it will take precedence 196 if (platformFile != null) { 197 readPermissionsFromXml(platformFile, onlyFeatures); 198 } 199 } 200 201 private void readPermissionsFromXml(File permFile, boolean onlyFeatures) { 202 FileReader permReader = null; 203 try { 204 permReader = new FileReader(permFile); 205 } catch (FileNotFoundException e) { 206 Slog.w(TAG, "Couldn't find or open permissions file " + permFile); 207 return; 208 } 209 210 final boolean lowRam = ActivityManager.isLowRamDeviceStatic(); 211 212 try { 213 XmlPullParser parser = Xml.newPullParser(); 214 parser.setInput(permReader); 215 216 int type; 217 while ((type=parser.next()) != parser.START_TAG 218 && type != parser.END_DOCUMENT) { 219 ; 220 } 221 222 if (type != parser.START_TAG) { 223 throw new XmlPullParserException("No start tag found"); 224 } 225 226 if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) { 227 throw new XmlPullParserException("Unexpected start tag in " + permFile 228 + ": found " + parser.getName() + ", expected 'permissions' or 'config'"); 229 } 230 231 while (true) { 232 XmlUtils.nextElement(parser); 233 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { 234 break; 235 } 236 237 String name = parser.getName(); 238 if ("group".equals(name) && !onlyFeatures) { 239 String gidStr = parser.getAttributeValue(null, "gid"); 240 if (gidStr != null) { 241 int gid = android.os.Process.getGidForName(gidStr); 242 mGlobalGids = appendInt(mGlobalGids, gid); 243 } else { 244 Slog.w(TAG, "<group> without gid in " + permFile + " at " 245 + parser.getPositionDescription()); 246 } 247 248 XmlUtils.skipCurrentTag(parser); 249 continue; 250 } else if ("permission".equals(name) && !onlyFeatures) { 251 String perm = parser.getAttributeValue(null, "name"); 252 if (perm == null) { 253 Slog.w(TAG, "<permission> without name in " + permFile + " at " 254 + parser.getPositionDescription()); 255 XmlUtils.skipCurrentTag(parser); 256 continue; 257 } 258 perm = perm.intern(); 259 readPermission(parser, perm); 260 261 } else if ("assign-permission".equals(name) && !onlyFeatures) { 262 String perm = parser.getAttributeValue(null, "name"); 263 if (perm == null) { 264 Slog.w(TAG, "<assign-permission> without name in " + permFile + " at " 265 + parser.getPositionDescription()); 266 XmlUtils.skipCurrentTag(parser); 267 continue; 268 } 269 String uidStr = parser.getAttributeValue(null, "uid"); 270 if (uidStr == null) { 271 Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at " 272 + parser.getPositionDescription()); 273 XmlUtils.skipCurrentTag(parser); 274 continue; 275 } 276 int uid = Process.getUidForName(uidStr); 277 if (uid < 0) { 278 Slog.w(TAG, "<assign-permission> with unknown uid \"" 279 + uidStr + " in " + permFile + " at " 280 + parser.getPositionDescription()); 281 XmlUtils.skipCurrentTag(parser); 282 continue; 283 } 284 perm = perm.intern(); 285 ArraySet<String> perms = mSystemPermissions.get(uid); 286 if (perms == null) { 287 perms = new ArraySet<String>(); 288 mSystemPermissions.put(uid, perms); 289 } 290 perms.add(perm); 291 XmlUtils.skipCurrentTag(parser); 292 293 } else if ("library".equals(name) && !onlyFeatures) { 294 String lname = parser.getAttributeValue(null, "name"); 295 String lfile = parser.getAttributeValue(null, "file"); 296 if (lname == null) { 297 Slog.w(TAG, "<library> without name in " + permFile + " at " 298 + parser.getPositionDescription()); 299 } else if (lfile == null) { 300 Slog.w(TAG, "<library> without file in " + permFile + " at " 301 + parser.getPositionDescription()); 302 } else { 303 //Log.i(TAG, "Got library " + lname + " in " + lfile); 304 mSharedLibraries.put(lname, lfile); 305 } 306 XmlUtils.skipCurrentTag(parser); 307 continue; 308 309 } else if ("feature".equals(name)) { 310 String fname = parser.getAttributeValue(null, "name"); 311 boolean allowed; 312 if (!lowRam) { 313 allowed = true; 314 } else { 315 String notLowRam = parser.getAttributeValue(null, "notLowRam"); 316 allowed = !"true".equals(notLowRam); 317 } 318 if (fname == null) { 319 Slog.w(TAG, "<feature> without name in " + permFile + " at " 320 + parser.getPositionDescription()); 321 } else if (allowed) { 322 //Log.i(TAG, "Got feature " + fname); 323 FeatureInfo fi = new FeatureInfo(); 324 fi.name = fname; 325 mAvailableFeatures.put(fname, fi); 326 } 327 XmlUtils.skipCurrentTag(parser); 328 continue; 329 330 } else if ("unavailable-feature".equals(name)) { 331 String fname = parser.getAttributeValue(null, "name"); 332 if (fname == null) { 333 Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at " 334 + parser.getPositionDescription()); 335 } else { 336 mUnavailableFeatures.add(fname); 337 } 338 XmlUtils.skipCurrentTag(parser); 339 continue; 340 341 } else if ("allow-in-power-save-except-idle".equals(name) && !onlyFeatures) { 342 String pkgname = parser.getAttributeValue(null, "package"); 343 if (pkgname == null) { 344 Slog.w(TAG, "<allow-in-power-save-except-idle> without package in " 345 + permFile + " at " + parser.getPositionDescription()); 346 } else { 347 mAllowInPowerSaveExceptIdle.add(pkgname); 348 } 349 XmlUtils.skipCurrentTag(parser); 350 continue; 351 352 } else if ("allow-in-power-save".equals(name) && !onlyFeatures) { 353 String pkgname = parser.getAttributeValue(null, "package"); 354 if (pkgname == null) { 355 Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at " 356 + parser.getPositionDescription()); 357 } else { 358 mAllowInPowerSave.add(pkgname); 359 } 360 XmlUtils.skipCurrentTag(parser); 361 continue; 362 363 } else if ("fixed-ime-app".equals(name) && !onlyFeatures) { 364 String pkgname = parser.getAttributeValue(null, "package"); 365 if (pkgname == null) { 366 Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at " 367 + parser.getPositionDescription()); 368 } else { 369 mFixedImeApps.add(pkgname); 370 } 371 XmlUtils.skipCurrentTag(parser); 372 continue; 373 374 } else if ("app-link".equals(name)) { 375 String pkgname = parser.getAttributeValue(null, "package"); 376 if (pkgname == null) { 377 Slog.w(TAG, "<app-link> without package in " + permFile + " at " 378 + parser.getPositionDescription()); 379 } else { 380 mLinkedApps.add(pkgname); 381 } 382 XmlUtils.skipCurrentTag(parser); 383 384 } else { 385 XmlUtils.skipCurrentTag(parser); 386 continue; 387 } 388 } 389 } catch (XmlPullParserException e) { 390 Slog.w(TAG, "Got exception parsing permissions.", e); 391 } catch (IOException e) { 392 Slog.w(TAG, "Got exception parsing permissions.", e); 393 } finally { 394 IoUtils.closeQuietly(permReader); 395 } 396 397 for (String fname : mUnavailableFeatures) { 398 if (mAvailableFeatures.remove(fname) != null) { 399 Slog.d(TAG, "Removed unavailable feature " + fname); 400 } 401 } 402 } 403 404 void readPermission(XmlPullParser parser, String name) 405 throws IOException, XmlPullParserException { 406 if (mPermissions.containsKey(name)) { 407 throw new IllegalStateException("Duplicate permission definition for " + name); 408 } 409 410 final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false); 411 final PermissionEntry perm = new PermissionEntry(name, perUser); 412 mPermissions.put(name, perm); 413 414 int outerDepth = parser.getDepth(); 415 int type; 416 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 417 && (type != XmlPullParser.END_TAG 418 || parser.getDepth() > outerDepth)) { 419 if (type == XmlPullParser.END_TAG 420 || type == XmlPullParser.TEXT) { 421 continue; 422 } 423 424 String tagName = parser.getName(); 425 if ("group".equals(tagName)) { 426 String gidStr = parser.getAttributeValue(null, "gid"); 427 if (gidStr != null) { 428 int gid = Process.getGidForName(gidStr); 429 perm.gids = appendInt(perm.gids, gid); 430 } else { 431 Slog.w(TAG, "<group> without gid at " 432 + parser.getPositionDescription()); 433 } 434 } 435 XmlUtils.skipCurrentTag(parser); 436 } 437 } 438 } 439