• Home
  • 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.ComponentName;
21 import android.content.pm.FeatureInfo;
22 import android.os.*;
23 import android.os.Process;
24 import android.util.ArrayMap;
25 import android.util.ArraySet;
26 import android.util.Slog;
27 import android.util.SparseArray;
28 import android.util.Xml;
29 
30 import libcore.io.IoUtils;
31 
32 import com.android.internal.util.XmlUtils;
33 
34 import org.xmlpull.v1.XmlPullParser;
35 import org.xmlpull.v1.XmlPullParserException;
36 
37 import java.io.File;
38 import java.io.FileNotFoundException;
39 import java.io.FileReader;
40 import java.io.IOException;
41 
42 import static com.android.internal.util.ArrayUtils.appendInt;
43 
44 /**
45  * Loads global system configuration info.
46  */
47 public class SystemConfig {
48     static final String TAG = "SystemConfig";
49 
50     static SystemConfig sInstance;
51 
52     // Group-ids that are given to all packages as read from etc/permissions/*.xml.
53     int[] mGlobalGids;
54 
55     // These are the built-in uid -> permission mappings that were read from the
56     // system configuration files.
57     final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>();
58 
59     // These are the built-in shared libraries that were read from the
60     // system configuration files.  Keys are the library names; strings are the
61     // paths to the libraries.
62     final ArrayMap<String, String> mSharedLibraries  = new ArrayMap<>();
63 
64     // These are the features this devices supports that were read from the
65     // system configuration files.
66     final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();
67 
68     // These are the features which this device doesn't support; the OEM
69     // partition uses these to opt-out of features from the system image.
70     final ArraySet<String> mUnavailableFeatures = new ArraySet<>();
71 
72     public static final class PermissionEntry {
73         public final String name;
74         public int[] gids;
75         public boolean perUser;
76 
PermissionEntry(String name, boolean perUser)77         PermissionEntry(String name, boolean perUser) {
78             this.name = name;
79             this.perUser = perUser;
80         }
81     }
82 
83     // These are the permission -> gid mappings that were read from the
84     // system configuration files.
85     final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();
86 
87     // These are the packages that are white-listed to be able to run in the
88     // background while in power save mode (but not whitelisted from device idle modes),
89     // as read from the configuration files.
90     final ArraySet<String> mAllowInPowerSaveExceptIdle = new ArraySet<>();
91 
92     // These are the packages that are white-listed to be able to run in the
93     // background while in power save mode, as read from the configuration files.
94     final ArraySet<String> mAllowInPowerSave = new ArraySet<>();
95 
96     // These are the app package names that should not allow IME switching.
97     final ArraySet<String> mFixedImeApps = new ArraySet<>();
98 
99     // These are the package names of apps which should be in the 'always'
100     // URL-handling state upon factory reset.
101     final ArraySet<String> mLinkedApps = new ArraySet<>();
102 
103     // These are the permitted backup transport service components
104     final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
105 
getInstance()106     public static SystemConfig getInstance() {
107         synchronized (SystemConfig.class) {
108             if (sInstance == null) {
109                 sInstance = new SystemConfig();
110             }
111             return sInstance;
112         }
113     }
114 
getGlobalGids()115     public int[] getGlobalGids() {
116         return mGlobalGids;
117     }
118 
getSystemPermissions()119     public SparseArray<ArraySet<String>> getSystemPermissions() {
120         return mSystemPermissions;
121     }
122 
getSharedLibraries()123     public ArrayMap<String, String> getSharedLibraries() {
124         return mSharedLibraries;
125     }
126 
getAvailableFeatures()127     public ArrayMap<String, FeatureInfo> getAvailableFeatures() {
128         return mAvailableFeatures;
129     }
130 
getPermissions()131     public ArrayMap<String, PermissionEntry> getPermissions() {
132         return mPermissions;
133     }
134 
getAllowInPowerSaveExceptIdle()135     public ArraySet<String> getAllowInPowerSaveExceptIdle() {
136         return mAllowInPowerSaveExceptIdle;
137     }
138 
getAllowInPowerSave()139     public ArraySet<String> getAllowInPowerSave() {
140         return mAllowInPowerSave;
141     }
142 
getFixedImeApps()143     public ArraySet<String> getFixedImeApps() {
144         return mFixedImeApps;
145     }
146 
getLinkedApps()147     public ArraySet<String> getLinkedApps() {
148         return mLinkedApps;
149     }
150 
getBackupTransportWhitelist()151     public ArraySet<ComponentName> getBackupTransportWhitelist() {
152         return mBackupTransportWhitelist;
153     }
154 
SystemConfig()155     SystemConfig() {
156         // Read configuration from system
157         readPermissions(Environment.buildPath(
158                 Environment.getRootDirectory(), "etc", "sysconfig"), false);
159         // Read configuration from the old permissions dir
160         readPermissions(Environment.buildPath(
161                 Environment.getRootDirectory(), "etc", "permissions"), false);
162         // Only read features from OEM config
163         readPermissions(Environment.buildPath(
164                 Environment.getOemDirectory(), "etc", "sysconfig"), true);
165         readPermissions(Environment.buildPath(
166                 Environment.getOemDirectory(), "etc", "permissions"), true);
167     }
168 
readPermissions(File libraryDir, boolean onlyFeatures)169     void readPermissions(File libraryDir, boolean onlyFeatures) {
170         // Read permissions from given directory.
171         if (!libraryDir.exists() || !libraryDir.isDirectory()) {
172             if (!onlyFeatures) {
173                 Slog.w(TAG, "No directory " + libraryDir + ", skipping");
174             }
175             return;
176         }
177         if (!libraryDir.canRead()) {
178             Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
179             return;
180         }
181 
182         // Iterate over the files in the directory and scan .xml files
183         File platformFile = null;
184         for (File f : libraryDir.listFiles()) {
185             // We'll read platform.xml last
186             if (f.getPath().endsWith("etc/permissions/platform.xml")) {
187                 platformFile = f;
188                 continue;
189             }
190 
191             if (!f.getPath().endsWith(".xml")) {
192                 Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
193                 continue;
194             }
195             if (!f.canRead()) {
196                 Slog.w(TAG, "Permissions library file " + f + " cannot be read");
197                 continue;
198             }
199 
200             readPermissionsFromXml(f, onlyFeatures);
201         }
202 
203         // Read platform permissions last so it will take precedence
204         if (platformFile != null) {
205             readPermissionsFromXml(platformFile, onlyFeatures);
206         }
207     }
208 
readPermissionsFromXml(File permFile, boolean onlyFeatures)209     private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
210         FileReader permReader = null;
211         try {
212             permReader = new FileReader(permFile);
213         } catch (FileNotFoundException e) {
214             Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
215             return;
216         }
217 
218         final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
219 
220         try {
221             XmlPullParser parser = Xml.newPullParser();
222             parser.setInput(permReader);
223 
224             int type;
225             while ((type=parser.next()) != parser.START_TAG
226                        && type != parser.END_DOCUMENT) {
227                 ;
228             }
229 
230             if (type != parser.START_TAG) {
231                 throw new XmlPullParserException("No start tag found");
232             }
233 
234             if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
235                 throw new XmlPullParserException("Unexpected start tag in " + permFile
236                         + ": found " + parser.getName() + ", expected 'permissions' or 'config'");
237             }
238 
239             while (true) {
240                 XmlUtils.nextElement(parser);
241                 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
242                     break;
243                 }
244 
245                 String name = parser.getName();
246                 if ("group".equals(name) && !onlyFeatures) {
247                     String gidStr = parser.getAttributeValue(null, "gid");
248                     if (gidStr != null) {
249                         int gid = android.os.Process.getGidForName(gidStr);
250                         mGlobalGids = appendInt(mGlobalGids, gid);
251                     } else {
252                         Slog.w(TAG, "<group> without gid in " + permFile + " at "
253                                 + parser.getPositionDescription());
254                     }
255 
256                     XmlUtils.skipCurrentTag(parser);
257                     continue;
258                 } else if ("permission".equals(name) && !onlyFeatures) {
259                     String perm = parser.getAttributeValue(null, "name");
260                     if (perm == null) {
261                         Slog.w(TAG, "<permission> without name in " + permFile + " at "
262                                 + parser.getPositionDescription());
263                         XmlUtils.skipCurrentTag(parser);
264                         continue;
265                     }
266                     perm = perm.intern();
267                     readPermission(parser, perm);
268 
269                 } else if ("assign-permission".equals(name) && !onlyFeatures) {
270                     String perm = parser.getAttributeValue(null, "name");
271                     if (perm == null) {
272                         Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
273                                 + parser.getPositionDescription());
274                         XmlUtils.skipCurrentTag(parser);
275                         continue;
276                     }
277                     String uidStr = parser.getAttributeValue(null, "uid");
278                     if (uidStr == null) {
279                         Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
280                                 + parser.getPositionDescription());
281                         XmlUtils.skipCurrentTag(parser);
282                         continue;
283                     }
284                     int uid = Process.getUidForName(uidStr);
285                     if (uid < 0) {
286                         Slog.w(TAG, "<assign-permission> with unknown uid \""
287                                 + uidStr + "  in " + permFile + " at "
288                                 + parser.getPositionDescription());
289                         XmlUtils.skipCurrentTag(parser);
290                         continue;
291                     }
292                     perm = perm.intern();
293                     ArraySet<String> perms = mSystemPermissions.get(uid);
294                     if (perms == null) {
295                         perms = new ArraySet<String>();
296                         mSystemPermissions.put(uid, perms);
297                     }
298                     perms.add(perm);
299                     XmlUtils.skipCurrentTag(parser);
300 
301                 } else if ("library".equals(name) && !onlyFeatures) {
302                     String lname = parser.getAttributeValue(null, "name");
303                     String lfile = parser.getAttributeValue(null, "file");
304                     if (lname == null) {
305                         Slog.w(TAG, "<library> without name in " + permFile + " at "
306                                 + parser.getPositionDescription());
307                     } else if (lfile == null) {
308                         Slog.w(TAG, "<library> without file in " + permFile + " at "
309                                 + parser.getPositionDescription());
310                     } else {
311                         //Log.i(TAG, "Got library " + lname + " in " + lfile);
312                         mSharedLibraries.put(lname, lfile);
313                     }
314                     XmlUtils.skipCurrentTag(parser);
315                     continue;
316 
317                 } else if ("feature".equals(name)) {
318                     String fname = parser.getAttributeValue(null, "name");
319                     boolean allowed;
320                     if (!lowRam) {
321                         allowed = true;
322                     } else {
323                         String notLowRam = parser.getAttributeValue(null, "notLowRam");
324                         allowed = !"true".equals(notLowRam);
325                     }
326                     if (fname == null) {
327                         Slog.w(TAG, "<feature> without name in " + permFile + " at "
328                                 + parser.getPositionDescription());
329                     } else if (allowed) {
330                         //Log.i(TAG, "Got feature " + fname);
331                         FeatureInfo fi = new FeatureInfo();
332                         fi.name = fname;
333                         mAvailableFeatures.put(fname, fi);
334                     }
335                     XmlUtils.skipCurrentTag(parser);
336                     continue;
337 
338                 } else if ("unavailable-feature".equals(name)) {
339                     String fname = parser.getAttributeValue(null, "name");
340                     if (fname == null) {
341                         Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
342                                 + parser.getPositionDescription());
343                     } else {
344                         mUnavailableFeatures.add(fname);
345                     }
346                     XmlUtils.skipCurrentTag(parser);
347                     continue;
348 
349                 } else if ("allow-in-power-save-except-idle".equals(name) && !onlyFeatures) {
350                     String pkgname = parser.getAttributeValue(null, "package");
351                     if (pkgname == null) {
352                         Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
353                                 + permFile + " at " + parser.getPositionDescription());
354                     } else {
355                         mAllowInPowerSaveExceptIdle.add(pkgname);
356                     }
357                     XmlUtils.skipCurrentTag(parser);
358                     continue;
359 
360                 } else if ("allow-in-power-save".equals(name) && !onlyFeatures) {
361                     String pkgname = parser.getAttributeValue(null, "package");
362                     if (pkgname == null) {
363                         Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
364                                 + parser.getPositionDescription());
365                     } else {
366                         mAllowInPowerSave.add(pkgname);
367                     }
368                     XmlUtils.skipCurrentTag(parser);
369                     continue;
370 
371                 } else if ("fixed-ime-app".equals(name) && !onlyFeatures) {
372                     String pkgname = parser.getAttributeValue(null, "package");
373                     if (pkgname == null) {
374                         Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at "
375                                 + parser.getPositionDescription());
376                     } else {
377                         mFixedImeApps.add(pkgname);
378                     }
379                     XmlUtils.skipCurrentTag(parser);
380                     continue;
381 
382                 } else if ("app-link".equals(name)) {
383                     String pkgname = parser.getAttributeValue(null, "package");
384                     if (pkgname == null) {
385                         Slog.w(TAG, "<app-link> without package in " + permFile + " at "
386                                 + parser.getPositionDescription());
387                     } else {
388                         mLinkedApps.add(pkgname);
389                     }
390                     XmlUtils.skipCurrentTag(parser);
391                 } else if ("backup-transport-whitelisted-service".equals(name)) {
392                     String serviceName = parser.getAttributeValue(null, "service");
393                     if (serviceName == null) {
394                         Slog.w(TAG, "<backup-transport-whitelisted-service> without service in "
395                                 + permFile + " at " + parser.getPositionDescription());
396                     } else {
397                         ComponentName cn = ComponentName.unflattenFromString(serviceName);
398                         if (cn == null) {
399                             Slog.w(TAG,
400                                     "<backup-transport-whitelisted-service> with invalid service name "
401                                     + serviceName + " in "+ permFile
402                                     + " at " + parser.getPositionDescription());
403                         } else {
404                             mBackupTransportWhitelist.add(cn);
405                         }
406                     }
407                     XmlUtils.skipCurrentTag(parser);
408 
409                 } else {
410                     XmlUtils.skipCurrentTag(parser);
411                     continue;
412                 }
413             }
414         } catch (XmlPullParserException e) {
415             Slog.w(TAG, "Got exception parsing permissions.", e);
416         } catch (IOException e) {
417             Slog.w(TAG, "Got exception parsing permissions.", e);
418         } finally {
419             IoUtils.closeQuietly(permReader);
420         }
421 
422         for (String fname : mUnavailableFeatures) {
423             if (mAvailableFeatures.remove(fname) != null) {
424                 Slog.d(TAG, "Removed unavailable feature " + fname);
425             }
426         }
427     }
428 
readPermission(XmlPullParser parser, String name)429     void readPermission(XmlPullParser parser, String name)
430             throws IOException, XmlPullParserException {
431         if (mPermissions.containsKey(name)) {
432             throw new IllegalStateException("Duplicate permission definition for " + name);
433         }
434 
435         final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
436         final PermissionEntry perm = new PermissionEntry(name, perUser);
437         mPermissions.put(name, perm);
438 
439         int outerDepth = parser.getDepth();
440         int type;
441         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
442                && (type != XmlPullParser.END_TAG
443                        || parser.getDepth() > outerDepth)) {
444             if (type == XmlPullParser.END_TAG
445                     || type == XmlPullParser.TEXT) {
446                 continue;
447             }
448 
449             String tagName = parser.getName();
450             if ("group".equals(tagName)) {
451                 String gidStr = parser.getAttributeValue(null, "gid");
452                 if (gidStr != null) {
453                     int gid = Process.getGidForName(gidStr);
454                     perm.gids = appendInt(perm.gids, gid);
455                 } else {
456                     Slog.w(TAG, "<group> without gid at "
457                             + parser.getPositionDescription());
458                 }
459             }
460             XmlUtils.skipCurrentTag(parser);
461         }
462     }
463 }
464