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