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 static com.android.internal.util.ArrayUtils.appendInt; 20 21 import android.app.ActivityManager; 22 import android.content.ComponentName; 23 import android.content.pm.FeatureInfo; 24 import android.content.pm.PackageManager; 25 import android.os.Environment; 26 import android.os.Process; 27 import android.os.storage.StorageManager; 28 import android.util.ArrayMap; 29 import android.util.ArraySet; 30 import android.util.Slog; 31 import android.util.SparseArray; 32 import android.util.Xml; 33 34 import com.android.internal.util.XmlUtils; 35 36 import libcore.io.IoUtils; 37 38 import org.xmlpull.v1.XmlPullParser; 39 import org.xmlpull.v1.XmlPullParserException; 40 41 import java.io.File; 42 import java.io.FileNotFoundException; 43 import java.io.FileReader; 44 import java.io.IOException; 45 46 /** 47 * Loads global system configuration info. 48 */ 49 public class SystemConfig { 50 static final String TAG = "SystemConfig"; 51 52 static SystemConfig sInstance; 53 54 // permission flag, determines which types of configuration are allowed to be read 55 private static final int ALLOW_FEATURES = 0x01; 56 private static final int ALLOW_LIBS = 0x02; 57 private static final int ALLOW_PERMISSIONS = 0x04; 58 private static final int ALLOW_APP_CONFIGS = 0x08; 59 private static final int ALLOW_ALL = ~0; 60 61 // Group-ids that are given to all packages as read from etc/permissions/*.xml. 62 int[] mGlobalGids; 63 64 // These are the built-in uid -> permission mappings that were read from the 65 // system configuration files. 66 final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>(); 67 68 // These are the built-in shared libraries that were read from the 69 // system configuration files. Keys are the library names; strings are the 70 // paths to the libraries. 71 final ArrayMap<String, String> mSharedLibraries = new ArrayMap<>(); 72 73 // These are the features this devices supports that were read from the 74 // system configuration files. 75 final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>(); 76 77 // These are the features which this device doesn't support; the OEM 78 // partition uses these to opt-out of features from the system image. 79 final ArraySet<String> mUnavailableFeatures = new ArraySet<>(); 80 81 public static final class PermissionEntry { 82 public final String name; 83 public int[] gids; 84 public boolean perUser; 85 PermissionEntry(String name, boolean perUser)86 PermissionEntry(String name, boolean perUser) { 87 this.name = name; 88 this.perUser = perUser; 89 } 90 } 91 92 // These are the permission -> gid mappings that were read from the 93 // system configuration files. 94 final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>(); 95 96 // These are the packages that are white-listed to be able to run in the 97 // background while in power save mode (but not whitelisted from device idle modes), 98 // as read from the configuration files. 99 final ArraySet<String> mAllowInPowerSaveExceptIdle = new ArraySet<>(); 100 101 // These are the packages that are white-listed to be able to run in the 102 // background while in power save mode, as read from the configuration files. 103 final ArraySet<String> mAllowInPowerSave = new ArraySet<>(); 104 105 // These are the packages that are white-listed to be able to run in the 106 // background while in data-usage save mode, as read from the configuration files. 107 final ArraySet<String> mAllowInDataUsageSave = new ArraySet<>(); 108 109 // These are the package names of apps which should be in the 'always' 110 // URL-handling state upon factory reset. 111 final ArraySet<String> mLinkedApps = new ArraySet<>(); 112 113 // These are the packages that are whitelisted to be able to run as system user 114 final ArraySet<String> mSystemUserWhitelistedApps = new ArraySet<>(); 115 116 // These are the packages that should not run under system user 117 final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>(); 118 119 // These are the components that are enabled by default as VR mode listener services. 120 final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>(); 121 122 // These are the permitted backup transport service components 123 final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>(); 124 getInstance()125 public static SystemConfig getInstance() { 126 synchronized (SystemConfig.class) { 127 if (sInstance == null) { 128 sInstance = new SystemConfig(); 129 } 130 return sInstance; 131 } 132 } 133 getGlobalGids()134 public int[] getGlobalGids() { 135 return mGlobalGids; 136 } 137 getSystemPermissions()138 public SparseArray<ArraySet<String>> getSystemPermissions() { 139 return mSystemPermissions; 140 } 141 getSharedLibraries()142 public ArrayMap<String, String> getSharedLibraries() { 143 return mSharedLibraries; 144 } 145 getAvailableFeatures()146 public ArrayMap<String, FeatureInfo> getAvailableFeatures() { 147 return mAvailableFeatures; 148 } 149 getPermissions()150 public ArrayMap<String, PermissionEntry> getPermissions() { 151 return mPermissions; 152 } 153 getAllowInPowerSaveExceptIdle()154 public ArraySet<String> getAllowInPowerSaveExceptIdle() { 155 return mAllowInPowerSaveExceptIdle; 156 } 157 getAllowInPowerSave()158 public ArraySet<String> getAllowInPowerSave() { 159 return mAllowInPowerSave; 160 } 161 getAllowInDataUsageSave()162 public ArraySet<String> getAllowInDataUsageSave() { 163 return mAllowInDataUsageSave; 164 } 165 getLinkedApps()166 public ArraySet<String> getLinkedApps() { 167 return mLinkedApps; 168 } 169 getSystemUserWhitelistedApps()170 public ArraySet<String> getSystemUserWhitelistedApps() { 171 return mSystemUserWhitelistedApps; 172 } 173 getSystemUserBlacklistedApps()174 public ArraySet<String> getSystemUserBlacklistedApps() { 175 return mSystemUserBlacklistedApps; 176 } 177 getDefaultVrComponents()178 public ArraySet<ComponentName> getDefaultVrComponents() { 179 return mDefaultVrComponents; 180 } 181 getBackupTransportWhitelist()182 public ArraySet<ComponentName> getBackupTransportWhitelist() { 183 return mBackupTransportWhitelist; 184 } 185 SystemConfig()186 SystemConfig() { 187 // Read configuration from system 188 readPermissions(Environment.buildPath( 189 Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL); 190 // Read configuration from the old permissions dir 191 readPermissions(Environment.buildPath( 192 Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL); 193 // Allow ODM to customize system configs around libs, features and apps 194 int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS; 195 readPermissions(Environment.buildPath( 196 Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag); 197 readPermissions(Environment.buildPath( 198 Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag); 199 // Only allow OEM to customize features 200 readPermissions(Environment.buildPath( 201 Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES); 202 readPermissions(Environment.buildPath( 203 Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES); 204 } 205 readPermissions(File libraryDir, int permissionFlag)206 void readPermissions(File libraryDir, int permissionFlag) { 207 // Read permissions from given directory. 208 if (!libraryDir.exists() || !libraryDir.isDirectory()) { 209 if (permissionFlag == ALLOW_ALL) { 210 Slog.w(TAG, "No directory " + libraryDir + ", skipping"); 211 } 212 return; 213 } 214 if (!libraryDir.canRead()) { 215 Slog.w(TAG, "Directory " + libraryDir + " cannot be read"); 216 return; 217 } 218 219 // Iterate over the files in the directory and scan .xml files 220 File platformFile = null; 221 for (File f : libraryDir.listFiles()) { 222 // We'll read platform.xml last 223 if (f.getPath().endsWith("etc/permissions/platform.xml")) { 224 platformFile = f; 225 continue; 226 } 227 228 if (!f.getPath().endsWith(".xml")) { 229 Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); 230 continue; 231 } 232 if (!f.canRead()) { 233 Slog.w(TAG, "Permissions library file " + f + " cannot be read"); 234 continue; 235 } 236 237 readPermissionsFromXml(f, permissionFlag); 238 } 239 240 // Read platform permissions last so it will take precedence 241 if (platformFile != null) { 242 readPermissionsFromXml(platformFile, permissionFlag); 243 } 244 } 245 readPermissionsFromXml(File permFile, int permissionFlag)246 private void readPermissionsFromXml(File permFile, int permissionFlag) { 247 FileReader permReader = null; 248 try { 249 permReader = new FileReader(permFile); 250 } catch (FileNotFoundException e) { 251 Slog.w(TAG, "Couldn't find or open permissions file " + permFile); 252 return; 253 } 254 255 final boolean lowRam = ActivityManager.isLowRamDeviceStatic(); 256 257 try { 258 XmlPullParser parser = Xml.newPullParser(); 259 parser.setInput(permReader); 260 261 int type; 262 while ((type=parser.next()) != parser.START_TAG 263 && type != parser.END_DOCUMENT) { 264 ; 265 } 266 267 if (type != parser.START_TAG) { 268 throw new XmlPullParserException("No start tag found"); 269 } 270 271 if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) { 272 throw new XmlPullParserException("Unexpected start tag in " + permFile 273 + ": found " + parser.getName() + ", expected 'permissions' or 'config'"); 274 } 275 276 boolean allowAll = permissionFlag == ALLOW_ALL; 277 boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0; 278 boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0; 279 boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0; 280 boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0; 281 while (true) { 282 XmlUtils.nextElement(parser); 283 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { 284 break; 285 } 286 287 String name = parser.getName(); 288 if ("group".equals(name) && allowAll) { 289 String gidStr = parser.getAttributeValue(null, "gid"); 290 if (gidStr != null) { 291 int gid = android.os.Process.getGidForName(gidStr); 292 mGlobalGids = appendInt(mGlobalGids, gid); 293 } else { 294 Slog.w(TAG, "<group> without gid in " + permFile + " at " 295 + parser.getPositionDescription()); 296 } 297 298 XmlUtils.skipCurrentTag(parser); 299 continue; 300 } else if ("permission".equals(name) && allowPermissions) { 301 String perm = parser.getAttributeValue(null, "name"); 302 if (perm == null) { 303 Slog.w(TAG, "<permission> without name in " + permFile + " at " 304 + parser.getPositionDescription()); 305 XmlUtils.skipCurrentTag(parser); 306 continue; 307 } 308 perm = perm.intern(); 309 readPermission(parser, perm); 310 311 } else if ("assign-permission".equals(name) && allowPermissions) { 312 String perm = parser.getAttributeValue(null, "name"); 313 if (perm == null) { 314 Slog.w(TAG, "<assign-permission> without name in " + permFile + " at " 315 + parser.getPositionDescription()); 316 XmlUtils.skipCurrentTag(parser); 317 continue; 318 } 319 String uidStr = parser.getAttributeValue(null, "uid"); 320 if (uidStr == null) { 321 Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at " 322 + parser.getPositionDescription()); 323 XmlUtils.skipCurrentTag(parser); 324 continue; 325 } 326 int uid = Process.getUidForName(uidStr); 327 if (uid < 0) { 328 Slog.w(TAG, "<assign-permission> with unknown uid \"" 329 + uidStr + " in " + permFile + " at " 330 + parser.getPositionDescription()); 331 XmlUtils.skipCurrentTag(parser); 332 continue; 333 } 334 perm = perm.intern(); 335 ArraySet<String> perms = mSystemPermissions.get(uid); 336 if (perms == null) { 337 perms = new ArraySet<String>(); 338 mSystemPermissions.put(uid, perms); 339 } 340 perms.add(perm); 341 XmlUtils.skipCurrentTag(parser); 342 343 } else if ("library".equals(name) && allowLibs) { 344 String lname = parser.getAttributeValue(null, "name"); 345 String lfile = parser.getAttributeValue(null, "file"); 346 if (lname == null) { 347 Slog.w(TAG, "<library> without name in " + permFile + " at " 348 + parser.getPositionDescription()); 349 } else if (lfile == null) { 350 Slog.w(TAG, "<library> without file in " + permFile + " at " 351 + parser.getPositionDescription()); 352 } else { 353 //Log.i(TAG, "Got library " + lname + " in " + lfile); 354 mSharedLibraries.put(lname, lfile); 355 } 356 XmlUtils.skipCurrentTag(parser); 357 continue; 358 359 } else if ("feature".equals(name) && allowFeatures) { 360 String fname = parser.getAttributeValue(null, "name"); 361 int fversion = XmlUtils.readIntAttribute(parser, "version", 0); 362 boolean allowed; 363 if (!lowRam) { 364 allowed = true; 365 } else { 366 String notLowRam = parser.getAttributeValue(null, "notLowRam"); 367 allowed = !"true".equals(notLowRam); 368 } 369 if (fname == null) { 370 Slog.w(TAG, "<feature> without name in " + permFile + " at " 371 + parser.getPositionDescription()); 372 } else if (allowed) { 373 addFeature(fname, fversion); 374 } 375 XmlUtils.skipCurrentTag(parser); 376 continue; 377 378 } else if ("unavailable-feature".equals(name) && allowFeatures) { 379 String fname = parser.getAttributeValue(null, "name"); 380 if (fname == null) { 381 Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at " 382 + parser.getPositionDescription()); 383 } else { 384 mUnavailableFeatures.add(fname); 385 } 386 XmlUtils.skipCurrentTag(parser); 387 continue; 388 389 } else if ("allow-in-power-save-except-idle".equals(name) && allowAll) { 390 String pkgname = parser.getAttributeValue(null, "package"); 391 if (pkgname == null) { 392 Slog.w(TAG, "<allow-in-power-save-except-idle> without package in " 393 + permFile + " at " + parser.getPositionDescription()); 394 } else { 395 mAllowInPowerSaveExceptIdle.add(pkgname); 396 } 397 XmlUtils.skipCurrentTag(parser); 398 continue; 399 400 } else if ("allow-in-power-save".equals(name) && allowAll) { 401 String pkgname = parser.getAttributeValue(null, "package"); 402 if (pkgname == null) { 403 Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at " 404 + parser.getPositionDescription()); 405 } else { 406 mAllowInPowerSave.add(pkgname); 407 } 408 XmlUtils.skipCurrentTag(parser); 409 continue; 410 411 } else if ("allow-in-data-usage-save".equals(name) && allowAll) { 412 String pkgname = parser.getAttributeValue(null, "package"); 413 if (pkgname == null) { 414 Slog.w(TAG, "<allow-in-data-usage-save> without package in " + permFile 415 + " at " + parser.getPositionDescription()); 416 } else { 417 mAllowInDataUsageSave.add(pkgname); 418 } 419 XmlUtils.skipCurrentTag(parser); 420 continue; 421 422 } else if ("app-link".equals(name) && allowAppConfigs) { 423 String pkgname = parser.getAttributeValue(null, "package"); 424 if (pkgname == null) { 425 Slog.w(TAG, "<app-link> without package in " + permFile + " at " 426 + parser.getPositionDescription()); 427 } else { 428 mLinkedApps.add(pkgname); 429 } 430 XmlUtils.skipCurrentTag(parser); 431 } else if ("system-user-whitelisted-app".equals(name) && allowAppConfigs) { 432 String pkgname = parser.getAttributeValue(null, "package"); 433 if (pkgname == null) { 434 Slog.w(TAG, "<system-user-whitelisted-app> without package in " + permFile 435 + " at " + parser.getPositionDescription()); 436 } else { 437 mSystemUserWhitelistedApps.add(pkgname); 438 } 439 XmlUtils.skipCurrentTag(parser); 440 } else if ("system-user-blacklisted-app".equals(name) && allowAppConfigs) { 441 String pkgname = parser.getAttributeValue(null, "package"); 442 if (pkgname == null) { 443 Slog.w(TAG, "<system-user-blacklisted-app without package in " + permFile 444 + " at " + parser.getPositionDescription()); 445 } else { 446 mSystemUserBlacklistedApps.add(pkgname); 447 } 448 XmlUtils.skipCurrentTag(parser); 449 } else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) { 450 String pkgname = parser.getAttributeValue(null, "package"); 451 String clsname = parser.getAttributeValue(null, "class"); 452 if (pkgname == null) { 453 Slog.w(TAG, "<default-enabled-vr-app without package in " + permFile 454 + " at " + parser.getPositionDescription()); 455 } else if (clsname == null) { 456 Slog.w(TAG, "<default-enabled-vr-app without class in " + permFile 457 + " at " + parser.getPositionDescription()); 458 } else { 459 mDefaultVrComponents.add(new ComponentName(pkgname, clsname)); 460 } 461 XmlUtils.skipCurrentTag(parser); 462 } else if ("backup-transport-whitelisted-service".equals(name) && allowFeatures) { 463 String serviceName = parser.getAttributeValue(null, "service"); 464 if (serviceName == null) { 465 Slog.w(TAG, "<backup-transport-whitelisted-service> without service in " 466 + permFile + " at " + parser.getPositionDescription()); 467 } else { 468 ComponentName cn = ComponentName.unflattenFromString(serviceName); 469 if (cn == null) { 470 Slog.w(TAG, 471 "<backup-transport-whitelisted-service> with invalid service name " 472 + serviceName + " in "+ permFile 473 + " at " + parser.getPositionDescription()); 474 } else { 475 mBackupTransportWhitelist.add(cn); 476 } 477 } 478 XmlUtils.skipCurrentTag(parser); 479 } else { 480 XmlUtils.skipCurrentTag(parser); 481 continue; 482 } 483 } 484 } catch (XmlPullParserException e) { 485 Slog.w(TAG, "Got exception parsing permissions.", e); 486 } catch (IOException e) { 487 Slog.w(TAG, "Got exception parsing permissions.", e); 488 } finally { 489 IoUtils.closeQuietly(permReader); 490 } 491 492 // Some devices can be field-converted to FBE, so offer to splice in 493 // those features if not already defined by the static config 494 if (StorageManager.isFileEncryptedNativeOnly()) { 495 addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION, 0); 496 addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0); 497 } 498 499 for (String featureName : mUnavailableFeatures) { 500 removeFeature(featureName); 501 } 502 } 503 addFeature(String name, int version)504 private void addFeature(String name, int version) { 505 FeatureInfo fi = mAvailableFeatures.get(name); 506 if (fi == null) { 507 fi = new FeatureInfo(); 508 fi.name = name; 509 fi.version = version; 510 mAvailableFeatures.put(name, fi); 511 } else { 512 fi.version = Math.max(fi.version, version); 513 } 514 } 515 removeFeature(String name)516 private void removeFeature(String name) { 517 if (mAvailableFeatures.remove(name) != null) { 518 Slog.d(TAG, "Removed unavailable feature " + name); 519 } 520 } 521 readPermission(XmlPullParser parser, String name)522 void readPermission(XmlPullParser parser, String name) 523 throws IOException, XmlPullParserException { 524 if (mPermissions.containsKey(name)) { 525 throw new IllegalStateException("Duplicate permission definition for " + name); 526 } 527 528 final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false); 529 final PermissionEntry perm = new PermissionEntry(name, perUser); 530 mPermissions.put(name, perm); 531 532 int outerDepth = parser.getDepth(); 533 int type; 534 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 535 && (type != XmlPullParser.END_TAG 536 || parser.getDepth() > outerDepth)) { 537 if (type == XmlPullParser.END_TAG 538 || type == XmlPullParser.TEXT) { 539 continue; 540 } 541 542 String tagName = parser.getName(); 543 if ("group".equals(tagName)) { 544 String gidStr = parser.getAttributeValue(null, "gid"); 545 if (gidStr != null) { 546 int gid = Process.getGidForName(gidStr); 547 perm.gids = appendInt(perm.gids, gid); 548 } else { 549 Slog.w(TAG, "<group> without gid at " 550 + parser.getPositionDescription()); 551 } 552 } 553 XmlUtils.skipCurrentTag(parser); 554 } 555 } 556 } 557