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