blob: 4c9d7d3ba3b2b381deb09b750f43a7928fae824e [file] [log] [blame]
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -07001/*
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
17package com.android.server;
18
Dianne Hackbornc0e4aaa2014-11-14 10:55:50 -080019import android.app.ActivityManager;
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070020import android.content.pm.FeatureInfo;
21import android.os.*;
22import android.os.Process;
23import android.util.ArrayMap;
24import android.util.ArraySet;
25import android.util.Slog;
26import android.util.SparseArray;
27import android.util.Xml;
Jeff Sharkey1c4ae802014-12-19 11:08:55 -080028
29import libcore.io.IoUtils;
30
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070031import com.android.internal.util.XmlUtils;
Jeff Sharkey1c4ae802014-12-19 11:08:55 -080032
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070033import org.xmlpull.v1.XmlPullParser;
34import org.xmlpull.v1.XmlPullParserException;
35
36import java.io.File;
37import java.io.FileNotFoundException;
38import java.io.FileReader;
39import java.io.IOException;
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070040
41import static com.android.internal.util.ArrayUtils.appendInt;
42
43/**
44 * Loads global system configuration info.
45 */
46public 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.
Jeff Sharkey9f837a92014-10-24 12:07:24 -070056 final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>();
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070057
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.
Dianne Hackbornbb8aa5a2014-09-17 13:20:38 -070061 final ArrayMap<String, String> mSharedLibraries = new ArrayMap<>();
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070062
63 // These are the features this devices supports that were read from the
64 // system configuration files.
Jeff Sharkey9f837a92014-10-24 12:07:24 -070065 final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070066
Jeff Sharkey1c4ae802014-12-19 11:08:55 -080067 // 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
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070071 public static final class PermissionEntry {
72 public final String name;
73 public int[] gids;
Jeff Sharkey00f39042015-03-23 16:51:22 -070074 public boolean perUser;
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070075
Jeff Sharkey00f39042015-03-23 16:51:22 -070076 PermissionEntry(String name, boolean perUser) {
77 this.name = name;
78 this.perUser = perUser;
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070079 }
80 }
81
82 // These are the permission -> gid mappings that were read from the
83 // system configuration files.
Dianne Hackbornbb8aa5a2014-09-17 13:20:38 -070084 final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070085
86 // These are the packages that are white-listed to be able to run in the
87 // background while in power save mode, as read from the configuration files.
Dianne Hackbornbb8aa5a2014-09-17 13:20:38 -070088 final ArraySet<String> mAllowInPowerSave = new ArraySet<>();
89
90 // These are the app package names that should not allow IME switching.
91 final ArraySet<String> mFixedImeApps = new ArraySet<>();
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -070092
93 public static SystemConfig getInstance() {
94 synchronized (SystemConfig.class) {
95 if (sInstance == null) {
96 sInstance = new SystemConfig();
97 }
98 return sInstance;
99 }
100 }
101
102 public int[] getGlobalGids() {
103 return mGlobalGids;
104 }
105
Jeff Sharkey9f837a92014-10-24 12:07:24 -0700106 public SparseArray<ArraySet<String>> getSystemPermissions() {
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700107 return mSystemPermissions;
108 }
109
110 public ArrayMap<String, String> getSharedLibraries() {
111 return mSharedLibraries;
112 }
113
Jeff Sharkey9f837a92014-10-24 12:07:24 -0700114 public ArrayMap<String, FeatureInfo> getAvailableFeatures() {
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700115 return mAvailableFeatures;
116 }
117
118 public ArrayMap<String, PermissionEntry> getPermissions() {
119 return mPermissions;
120 }
121
122 public ArraySet<String> getAllowInPowerSave() {
123 return mAllowInPowerSave;
124 }
125
Dianne Hackbornbb8aa5a2014-09-17 13:20:38 -0700126 public ArraySet<String> getFixedImeApps() {
127 return mFixedImeApps;
128 }
129
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700130 SystemConfig() {
131 // Read configuration from system
132 readPermissions(Environment.buildPath(
133 Environment.getRootDirectory(), "etc", "sysconfig"), false);
134 // Read configuration from the old permissions dir
135 readPermissions(Environment.buildPath(
136 Environment.getRootDirectory(), "etc", "permissions"), false);
137 // Only read features from OEM config
138 readPermissions(Environment.buildPath(
139 Environment.getOemDirectory(), "etc", "sysconfig"), true);
140 readPermissions(Environment.buildPath(
141 Environment.getOemDirectory(), "etc", "permissions"), true);
142 }
143
144 void readPermissions(File libraryDir, boolean onlyFeatures) {
145 // Read permissions from given directory.
146 if (!libraryDir.exists() || !libraryDir.isDirectory()) {
147 if (!onlyFeatures) {
148 Slog.w(TAG, "No directory " + libraryDir + ", skipping");
149 }
150 return;
151 }
152 if (!libraryDir.canRead()) {
153 Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
154 return;
155 }
156
157 // Iterate over the files in the directory and scan .xml files
Jeff Sharkey1c4ae802014-12-19 11:08:55 -0800158 File platformFile = null;
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700159 for (File f : libraryDir.listFiles()) {
160 // We'll read platform.xml last
161 if (f.getPath().endsWith("etc/permissions/platform.xml")) {
Jeff Sharkey1c4ae802014-12-19 11:08:55 -0800162 platformFile = f;
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700163 continue;
164 }
165
166 if (!f.getPath().endsWith(".xml")) {
167 Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
168 continue;
169 }
170 if (!f.canRead()) {
171 Slog.w(TAG, "Permissions library file " + f + " cannot be read");
172 continue;
173 }
174
175 readPermissionsFromXml(f, onlyFeatures);
176 }
177
Jeff Sharkey1c4ae802014-12-19 11:08:55 -0800178 // Read platform permissions last so it will take precedence
179 if (platformFile != null) {
180 readPermissionsFromXml(platformFile, onlyFeatures);
181 }
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700182 }
183
184 private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
185 FileReader permReader = null;
186 try {
187 permReader = new FileReader(permFile);
188 } catch (FileNotFoundException e) {
189 Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
190 return;
191 }
192
Dianne Hackbornc0e4aaa2014-11-14 10:55:50 -0800193 final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
194
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700195 try {
196 XmlPullParser parser = Xml.newPullParser();
197 parser.setInput(permReader);
198
199 int type;
200 while ((type=parser.next()) != parser.START_TAG
201 && type != parser.END_DOCUMENT) {
202 ;
203 }
204
205 if (type != parser.START_TAG) {
206 throw new XmlPullParserException("No start tag found");
207 }
208
209 if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800210 throw new XmlPullParserException("Unexpected start tag in " + permFile
211 + ": found " + parser.getName() + ", expected 'permissions' or 'config'");
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700212 }
213
214 while (true) {
215 XmlUtils.nextElement(parser);
216 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
217 break;
218 }
219
220 String name = parser.getName();
221 if ("group".equals(name) && !onlyFeatures) {
222 String gidStr = parser.getAttributeValue(null, "gid");
223 if (gidStr != null) {
224 int gid = android.os.Process.getGidForName(gidStr);
225 mGlobalGids = appendInt(mGlobalGids, gid);
226 } else {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800227 Slog.w(TAG, "<group> without gid in " + permFile + " at "
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700228 + parser.getPositionDescription());
229 }
230
231 XmlUtils.skipCurrentTag(parser);
232 continue;
233 } else if ("permission".equals(name) && !onlyFeatures) {
234 String perm = parser.getAttributeValue(null, "name");
235 if (perm == null) {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800236 Slog.w(TAG, "<permission> without name in " + permFile + " at "
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700237 + parser.getPositionDescription());
238 XmlUtils.skipCurrentTag(parser);
239 continue;
240 }
241 perm = perm.intern();
242 readPermission(parser, perm);
243
244 } else if ("assign-permission".equals(name) && !onlyFeatures) {
245 String perm = parser.getAttributeValue(null, "name");
246 if (perm == null) {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800247 Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700248 + parser.getPositionDescription());
249 XmlUtils.skipCurrentTag(parser);
250 continue;
251 }
252 String uidStr = parser.getAttributeValue(null, "uid");
253 if (uidStr == null) {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800254 Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700255 + parser.getPositionDescription());
256 XmlUtils.skipCurrentTag(parser);
257 continue;
258 }
259 int uid = Process.getUidForName(uidStr);
260 if (uid < 0) {
261 Slog.w(TAG, "<assign-permission> with unknown uid \""
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800262 + uidStr + " in " + permFile + " at "
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700263 + parser.getPositionDescription());
264 XmlUtils.skipCurrentTag(parser);
265 continue;
266 }
267 perm = perm.intern();
Jeff Sharkey9f837a92014-10-24 12:07:24 -0700268 ArraySet<String> perms = mSystemPermissions.get(uid);
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700269 if (perms == null) {
Jeff Sharkey9f837a92014-10-24 12:07:24 -0700270 perms = new ArraySet<String>();
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700271 mSystemPermissions.put(uid, perms);
272 }
273 perms.add(perm);
274 XmlUtils.skipCurrentTag(parser);
275
276 } else if ("library".equals(name) && !onlyFeatures) {
277 String lname = parser.getAttributeValue(null, "name");
278 String lfile = parser.getAttributeValue(null, "file");
279 if (lname == null) {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800280 Slog.w(TAG, "<library> without name in " + permFile + " at "
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700281 + parser.getPositionDescription());
282 } else if (lfile == null) {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800283 Slog.w(TAG, "<library> without file in " + permFile + " at "
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700284 + parser.getPositionDescription());
285 } else {
286 //Log.i(TAG, "Got library " + lname + " in " + lfile);
287 mSharedLibraries.put(lname, lfile);
288 }
289 XmlUtils.skipCurrentTag(parser);
290 continue;
291
292 } else if ("feature".equals(name)) {
293 String fname = parser.getAttributeValue(null, "name");
Dianne Hackbornc0e4aaa2014-11-14 10:55:50 -0800294 boolean allowed;
295 if (!lowRam) {
296 allowed = true;
297 } else {
298 String notLowRam = parser.getAttributeValue(null, "notLowRam");
299 allowed = !"true".equals(notLowRam);
300 }
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700301 if (fname == null) {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800302 Slog.w(TAG, "<feature> without name in " + permFile + " at "
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700303 + parser.getPositionDescription());
Dianne Hackbornc0e4aaa2014-11-14 10:55:50 -0800304 } else if (allowed) {
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700305 //Log.i(TAG, "Got feature " + fname);
306 FeatureInfo fi = new FeatureInfo();
307 fi.name = fname;
308 mAvailableFeatures.put(fname, fi);
309 }
310 XmlUtils.skipCurrentTag(parser);
311 continue;
312
Jeff Sharkey1c4ae802014-12-19 11:08:55 -0800313 } else if ("unavailable-feature".equals(name)) {
314 String fname = parser.getAttributeValue(null, "name");
315 if (fname == null) {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800316 Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
Jeff Sharkey1c4ae802014-12-19 11:08:55 -0800317 + parser.getPositionDescription());
318 } else {
319 mUnavailableFeatures.add(fname);
320 }
321 XmlUtils.skipCurrentTag(parser);
322 continue;
323
324 } else if ("allow-in-power-save".equals(name) && !onlyFeatures) {
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700325 String pkgname = parser.getAttributeValue(null, "package");
326 if (pkgname == null) {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800327 Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700328 + parser.getPositionDescription());
329 } else {
330 mAllowInPowerSave.add(pkgname);
331 }
332 XmlUtils.skipCurrentTag(parser);
333 continue;
334
Jeff Sharkey1c4ae802014-12-19 11:08:55 -0800335 } else if ("fixed-ime-app".equals(name) && !onlyFeatures) {
Dianne Hackbornbb8aa5a2014-09-17 13:20:38 -0700336 String pkgname = parser.getAttributeValue(null, "package");
337 if (pkgname == null) {
Dianne Hackbornb3d4cb32015-01-09 09:54:06 -0800338 Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at "
Dianne Hackbornbb8aa5a2014-09-17 13:20:38 -0700339 + parser.getPositionDescription());
340 } else {
341 mFixedImeApps.add(pkgname);
342 }
343 XmlUtils.skipCurrentTag(parser);
344 continue;
345
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700346 } else {
347 XmlUtils.skipCurrentTag(parser);
348 continue;
349 }
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700350 }
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700351 } catch (XmlPullParserException e) {
Jeff Sharkey1c4ae802014-12-19 11:08:55 -0800352 Slog.w(TAG, "Got exception parsing permissions.", e);
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700353 } catch (IOException e) {
Jeff Sharkey1c4ae802014-12-19 11:08:55 -0800354 Slog.w(TAG, "Got exception parsing permissions.", e);
355 } finally {
356 IoUtils.closeQuietly(permReader);
357 }
358
359 for (String fname : mUnavailableFeatures) {
360 if (mAvailableFeatures.remove(fname) != null) {
361 Slog.d(TAG, "Removed unavailable feature " + fname);
362 }
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700363 }
364 }
365
366 void readPermission(XmlPullParser parser, String name)
367 throws IOException, XmlPullParserException {
Jeff Sharkey00f39042015-03-23 16:51:22 -0700368 if (mPermissions.containsKey(name)) {
369 throw new IllegalStateException("Duplicate permission definition for " + name);
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700370 }
Jeff Sharkey00f39042015-03-23 16:51:22 -0700371
372 final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
373 final PermissionEntry perm = new PermissionEntry(name, perUser);
374 mPermissions.put(name, perm);
375
Dianne Hackbornbe7c50e2014-06-30 14:43:28 -0700376 int outerDepth = parser.getDepth();
377 int type;
378 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
379 && (type != XmlPullParser.END_TAG
380 || parser.getDepth() > outerDepth)) {
381 if (type == XmlPullParser.END_TAG
382 || type == XmlPullParser.TEXT) {
383 continue;
384 }
385
386 String tagName = parser.getName();
387 if ("group".equals(tagName)) {
388 String gidStr = parser.getAttributeValue(null, "gid");
389 if (gidStr != null) {
390 int gid = Process.getGidForName(gidStr);
391 perm.gids = appendInt(perm.gids, gid);
392 } else {
393 Slog.w(TAG, "<group> without gid at "
394 + parser.getPositionDescription());
395 }
396 }
397 XmlUtils.skipCurrentTag(parser);
398 }
399 }
400}