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