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 PermissionEntry(String _name)75 PermissionEntry(String _name) { 76 name = _name; 77 } 78 } 79 80 // These are the permission -> gid mappings that were read from the 81 // system configuration files. 82 final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>(); 83 84 // These are the packages that are white-listed to be able to run in the 85 // background while in power save mode, as read from the configuration files. 86 final ArraySet<String> mAllowInPowerSave = new ArraySet<>(); 87 88 // These are the app package names that should not allow IME switching. 89 final ArraySet<String> mFixedImeApps = new ArraySet<>(); 90 getInstance()91 public static SystemConfig getInstance() { 92 synchronized (SystemConfig.class) { 93 if (sInstance == null) { 94 sInstance = new SystemConfig(); 95 } 96 return sInstance; 97 } 98 } 99 getGlobalGids()100 public int[] getGlobalGids() { 101 return mGlobalGids; 102 } 103 getSystemPermissions()104 public SparseArray<ArraySet<String>> getSystemPermissions() { 105 return mSystemPermissions; 106 } 107 getSharedLibraries()108 public ArrayMap<String, String> getSharedLibraries() { 109 return mSharedLibraries; 110 } 111 getAvailableFeatures()112 public ArrayMap<String, FeatureInfo> getAvailableFeatures() { 113 return mAvailableFeatures; 114 } 115 getPermissions()116 public ArrayMap<String, PermissionEntry> getPermissions() { 117 return mPermissions; 118 } 119 getAllowInPowerSave()120 public ArraySet<String> getAllowInPowerSave() { 121 return mAllowInPowerSave; 122 } 123 getFixedImeApps()124 public ArraySet<String> getFixedImeApps() { 125 return mFixedImeApps; 126 } 127 SystemConfig()128 SystemConfig() { 129 // Read configuration from system 130 readPermissions(Environment.buildPath( 131 Environment.getRootDirectory(), "etc", "sysconfig"), false); 132 // Read configuration from the old permissions dir 133 readPermissions(Environment.buildPath( 134 Environment.getRootDirectory(), "etc", "permissions"), false); 135 // Only read features from OEM config 136 readPermissions(Environment.buildPath( 137 Environment.getOemDirectory(), "etc", "sysconfig"), true); 138 readPermissions(Environment.buildPath( 139 Environment.getOemDirectory(), "etc", "permissions"), true); 140 } 141 readPermissions(File libraryDir, boolean onlyFeatures)142 void readPermissions(File libraryDir, boolean onlyFeatures) { 143 // Read permissions from given directory. 144 if (!libraryDir.exists() || !libraryDir.isDirectory()) { 145 if (!onlyFeatures) { 146 Slog.w(TAG, "No directory " + libraryDir + ", skipping"); 147 } 148 return; 149 } 150 if (!libraryDir.canRead()) { 151 Slog.w(TAG, "Directory " + libraryDir + " cannot be read"); 152 return; 153 } 154 155 // Iterate over the files in the directory and scan .xml files 156 File platformFile = null; 157 for (File f : libraryDir.listFiles()) { 158 // We'll read platform.xml last 159 if (f.getPath().endsWith("etc/permissions/platform.xml")) { 160 platformFile = f; 161 continue; 162 } 163 164 if (!f.getPath().endsWith(".xml")) { 165 Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); 166 continue; 167 } 168 if (!f.canRead()) { 169 Slog.w(TAG, "Permissions library file " + f + " cannot be read"); 170 continue; 171 } 172 173 readPermissionsFromXml(f, onlyFeatures); 174 } 175 176 // Read platform permissions last so it will take precedence 177 if (platformFile != null) { 178 readPermissionsFromXml(platformFile, onlyFeatures); 179 } 180 } 181 readPermissionsFromXml(File permFile, boolean onlyFeatures)182 private void readPermissionsFromXml(File permFile, boolean onlyFeatures) { 183 FileReader permReader = null; 184 try { 185 permReader = new FileReader(permFile); 186 } catch (FileNotFoundException e) { 187 Slog.w(TAG, "Couldn't find or open permissions file " + permFile); 188 return; 189 } 190 191 final boolean lowRam = ActivityManager.isLowRamDeviceStatic(); 192 193 try { 194 XmlPullParser parser = Xml.newPullParser(); 195 parser.setInput(permReader); 196 197 int type; 198 while ((type=parser.next()) != parser.START_TAG 199 && type != parser.END_DOCUMENT) { 200 ; 201 } 202 203 if (type != parser.START_TAG) { 204 throw new XmlPullParserException("No start tag found"); 205 } 206 207 if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) { 208 throw new XmlPullParserException("Unexpected start tag in " + permFile 209 + ": found " + parser.getName() + ", expected 'permissions' or 'config'"); 210 } 211 212 while (true) { 213 XmlUtils.nextElement(parser); 214 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { 215 break; 216 } 217 218 String name = parser.getName(); 219 if ("group".equals(name) && !onlyFeatures) { 220 String gidStr = parser.getAttributeValue(null, "gid"); 221 if (gidStr != null) { 222 int gid = android.os.Process.getGidForName(gidStr); 223 mGlobalGids = appendInt(mGlobalGids, gid); 224 } else { 225 Slog.w(TAG, "<group> without gid in " + permFile + " at " 226 + parser.getPositionDescription()); 227 } 228 229 XmlUtils.skipCurrentTag(parser); 230 continue; 231 } else if ("permission".equals(name) && !onlyFeatures) { 232 String perm = parser.getAttributeValue(null, "name"); 233 if (perm == null) { 234 Slog.w(TAG, "<permission> without name in " + permFile + " at " 235 + parser.getPositionDescription()); 236 XmlUtils.skipCurrentTag(parser); 237 continue; 238 } 239 perm = perm.intern(); 240 readPermission(parser, perm); 241 242 } else if ("assign-permission".equals(name) && !onlyFeatures) { 243 String perm = parser.getAttributeValue(null, "name"); 244 if (perm == null) { 245 Slog.w(TAG, "<assign-permission> without name in " + permFile + " at " 246 + parser.getPositionDescription()); 247 XmlUtils.skipCurrentTag(parser); 248 continue; 249 } 250 String uidStr = parser.getAttributeValue(null, "uid"); 251 if (uidStr == null) { 252 Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at " 253 + parser.getPositionDescription()); 254 XmlUtils.skipCurrentTag(parser); 255 continue; 256 } 257 int uid = Process.getUidForName(uidStr); 258 if (uid < 0) { 259 Slog.w(TAG, "<assign-permission> with unknown uid \"" 260 + uidStr + " in " + permFile + " at " 261 + parser.getPositionDescription()); 262 XmlUtils.skipCurrentTag(parser); 263 continue; 264 } 265 perm = perm.intern(); 266 ArraySet<String> perms = mSystemPermissions.get(uid); 267 if (perms == null) { 268 perms = new ArraySet<String>(); 269 mSystemPermissions.put(uid, perms); 270 } 271 perms.add(perm); 272 XmlUtils.skipCurrentTag(parser); 273 274 } else if ("library".equals(name) && !onlyFeatures) { 275 String lname = parser.getAttributeValue(null, "name"); 276 String lfile = parser.getAttributeValue(null, "file"); 277 if (lname == null) { 278 Slog.w(TAG, "<library> without name in " + permFile + " at " 279 + parser.getPositionDescription()); 280 } else if (lfile == null) { 281 Slog.w(TAG, "<library> without file in " + permFile + " at " 282 + parser.getPositionDescription()); 283 } else { 284 //Log.i(TAG, "Got library " + lname + " in " + lfile); 285 mSharedLibraries.put(lname, lfile); 286 } 287 XmlUtils.skipCurrentTag(parser); 288 continue; 289 290 } else if ("feature".equals(name)) { 291 String fname = parser.getAttributeValue(null, "name"); 292 boolean allowed; 293 if (!lowRam) { 294 allowed = true; 295 } else { 296 String notLowRam = parser.getAttributeValue(null, "notLowRam"); 297 allowed = !"true".equals(notLowRam); 298 } 299 if (fname == null) { 300 Slog.w(TAG, "<feature> without name in " + permFile + " at " 301 + parser.getPositionDescription()); 302 } else if (allowed) { 303 //Log.i(TAG, "Got feature " + fname); 304 FeatureInfo fi = new FeatureInfo(); 305 fi.name = fname; 306 mAvailableFeatures.put(fname, fi); 307 } 308 XmlUtils.skipCurrentTag(parser); 309 continue; 310 311 } else if ("unavailable-feature".equals(name)) { 312 String fname = parser.getAttributeValue(null, "name"); 313 if (fname == null) { 314 Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at " 315 + parser.getPositionDescription()); 316 } else { 317 mUnavailableFeatures.add(fname); 318 } 319 XmlUtils.skipCurrentTag(parser); 320 continue; 321 322 } else if ("allow-in-power-save".equals(name) && !onlyFeatures) { 323 String pkgname = parser.getAttributeValue(null, "package"); 324 if (pkgname == null) { 325 Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at " 326 + parser.getPositionDescription()); 327 } else { 328 mAllowInPowerSave.add(pkgname); 329 } 330 XmlUtils.skipCurrentTag(parser); 331 continue; 332 333 } else if ("fixed-ime-app".equals(name) && !onlyFeatures) { 334 String pkgname = parser.getAttributeValue(null, "package"); 335 if (pkgname == null) { 336 Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at " 337 + parser.getPositionDescription()); 338 } else { 339 mFixedImeApps.add(pkgname); 340 } 341 XmlUtils.skipCurrentTag(parser); 342 continue; 343 344 } else { 345 XmlUtils.skipCurrentTag(parser); 346 continue; 347 } 348 } 349 } catch (XmlPullParserException e) { 350 Slog.w(TAG, "Got exception parsing permissions.", e); 351 } catch (IOException e) { 352 Slog.w(TAG, "Got exception parsing permissions.", e); 353 } finally { 354 IoUtils.closeQuietly(permReader); 355 } 356 357 for (String fname : mUnavailableFeatures) { 358 if (mAvailableFeatures.remove(fname) != null) { 359 Slog.d(TAG, "Removed unavailable feature " + fname); 360 } 361 } 362 } 363 readPermission(XmlPullParser parser, String name)364 void readPermission(XmlPullParser parser, String name) 365 throws IOException, XmlPullParserException { 366 367 name = name.intern(); 368 369 PermissionEntry perm = mPermissions.get(name); 370 if (perm == null) { 371 perm = new PermissionEntry(name); 372 mPermissions.put(name, perm); 373 } 374 int outerDepth = parser.getDepth(); 375 int type; 376 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 377 && (type != XmlPullParser.END_TAG 378 || parser.getDepth() > outerDepth)) { 379 if (type == XmlPullParser.END_TAG 380 || type == XmlPullParser.TEXT) { 381 continue; 382 } 383 384 String tagName = parser.getName(); 385 if ("group".equals(tagName)) { 386 String gidStr = parser.getAttributeValue(null, "gid"); 387 if (gidStr != null) { 388 int gid = Process.getGidForName(gidStr); 389 perm.gids = appendInt(perm.gids, gid); 390 } else { 391 Slog.w(TAG, "<group> without gid at " 392 + parser.getPositionDescription()); 393 } 394 } 395 XmlUtils.skipCurrentTag(parser); 396 } 397 } 398 } 399