• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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