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