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