• 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 static com.android.internal.util.ArrayUtils.appendInt;
20 
21 import android.app.ActivityManager;
22 import android.content.ComponentName;
23 import android.content.pm.FeatureInfo;
24 import android.content.pm.PackageManager;
25 import android.os.Environment;
26 import android.os.Process;
27 import android.os.storage.StorageManager;
28 import android.util.ArrayMap;
29 import android.util.ArraySet;
30 import android.util.Slog;
31 import android.util.SparseArray;
32 import android.util.Xml;
33 
34 import com.android.internal.util.XmlUtils;
35 
36 import libcore.io.IoUtils;
37 
38 import org.xmlpull.v1.XmlPullParser;
39 import org.xmlpull.v1.XmlPullParserException;
40 
41 import java.io.File;
42 import java.io.FileNotFoundException;
43 import java.io.FileReader;
44 import java.io.IOException;
45 import java.util.ArrayList;
46 import java.util.List;
47 
48 /**
49  * Loads global system configuration info.
50  */
51 public class SystemConfig {
52     static final String TAG = "SystemConfig";
53 
54     static SystemConfig sInstance;
55 
56     // permission flag, determines which types of configuration are allowed to be read
57     private static final int ALLOW_FEATURES = 0x01;
58     private static final int ALLOW_LIBS = 0x02;
59     private static final int ALLOW_PERMISSIONS = 0x04;
60     private static final int ALLOW_APP_CONFIGS = 0x08;
61     private static final int ALLOW_ALL = ~0;
62 
63     // Group-ids that are given to all packages as read from etc/permissions/*.xml.
64     int[] mGlobalGids;
65 
66     // These are the built-in uid -> permission mappings that were read from the
67     // system configuration files.
68     final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>();
69 
70     // These are the built-in shared libraries that were read from the
71     // system configuration files.  Keys are the library names; strings are the
72     // paths to the libraries.
73     final ArrayMap<String, String> mSharedLibraries  = new ArrayMap<>();
74 
75     // These are the features this devices supports that were read from the
76     // system configuration files.
77     final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();
78 
79     // These are the features which this device doesn't support; the OEM
80     // partition uses these to opt-out of features from the system image.
81     final ArraySet<String> mUnavailableFeatures = new ArraySet<>();
82 
83     public static final class PermissionEntry {
84         public final String name;
85         public int[] gids;
86         public boolean perUser;
87 
PermissionEntry(String name, boolean perUser)88         PermissionEntry(String name, boolean perUser) {
89             this.name = name;
90             this.perUser = perUser;
91         }
92     }
93 
94     // These are the permission -> gid mappings that were read from the
95     // system configuration files.
96     final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();
97 
98     // These are the packages that are white-listed to be able to run in the
99     // background while in power save mode (but not whitelisted from device idle modes),
100     // as read from the configuration files.
101     final ArraySet<String> mAllowInPowerSaveExceptIdle = new ArraySet<>();
102 
103     // These are the packages that are white-listed to be able to run in the
104     // background while in power save mode, as read from the configuration files.
105     final ArraySet<String> mAllowInPowerSave = new ArraySet<>();
106 
107     // These are the packages that are white-listed to be able to run in the
108     // background while in data-usage save mode, as read from the configuration files.
109     final ArraySet<String> mAllowInDataUsageSave = new ArraySet<>();
110 
111     // These are the package names of apps which should be in the 'always'
112     // URL-handling state upon factory reset.
113     final ArraySet<String> mLinkedApps = new ArraySet<>();
114 
115     // These are the packages that are whitelisted to be able to run as system user
116     final ArraySet<String> mSystemUserWhitelistedApps = new ArraySet<>();
117 
118     // These are the packages that should not run under system user
119     final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
120 
121     // These are the components that are enabled by default as VR mode listener services.
122     final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>();
123 
124     // These are the permitted backup transport service components
125     final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
126 
127     // These are the packages of carrier-associated apps which should be disabled until used until
128     // a SIM is inserted which grants carrier privileges to that carrier app.
129     final ArrayMap<String, List<String>> mDisabledUntilUsedPreinstalledCarrierAssociatedApps =
130             new ArrayMap<>();
131 
getInstance()132     public static SystemConfig getInstance() {
133         synchronized (SystemConfig.class) {
134             if (sInstance == null) {
135                 sInstance = new SystemConfig();
136             }
137             return sInstance;
138         }
139     }
140 
getGlobalGids()141     public int[] getGlobalGids() {
142         return mGlobalGids;
143     }
144 
getSystemPermissions()145     public SparseArray<ArraySet<String>> getSystemPermissions() {
146         return mSystemPermissions;
147     }
148 
getSharedLibraries()149     public ArrayMap<String, String> getSharedLibraries() {
150         return mSharedLibraries;
151     }
152 
getAvailableFeatures()153     public ArrayMap<String, FeatureInfo> getAvailableFeatures() {
154         return mAvailableFeatures;
155     }
156 
getPermissions()157     public ArrayMap<String, PermissionEntry> getPermissions() {
158         return mPermissions;
159     }
160 
getAllowInPowerSaveExceptIdle()161     public ArraySet<String> getAllowInPowerSaveExceptIdle() {
162         return mAllowInPowerSaveExceptIdle;
163     }
164 
getAllowInPowerSave()165     public ArraySet<String> getAllowInPowerSave() {
166         return mAllowInPowerSave;
167     }
168 
getAllowInDataUsageSave()169     public ArraySet<String> getAllowInDataUsageSave() {
170         return mAllowInDataUsageSave;
171     }
172 
getLinkedApps()173     public ArraySet<String> getLinkedApps() {
174         return mLinkedApps;
175     }
176 
getSystemUserWhitelistedApps()177     public ArraySet<String> getSystemUserWhitelistedApps() {
178         return mSystemUserWhitelistedApps;
179     }
180 
getSystemUserBlacklistedApps()181     public ArraySet<String> getSystemUserBlacklistedApps() {
182         return mSystemUserBlacklistedApps;
183     }
184 
getDefaultVrComponents()185     public ArraySet<ComponentName> getDefaultVrComponents() {
186         return mDefaultVrComponents;
187     }
188 
getBackupTransportWhitelist()189     public ArraySet<ComponentName> getBackupTransportWhitelist() {
190         return mBackupTransportWhitelist;
191     }
192 
getDisabledUntilUsedPreinstalledCarrierAssociatedApps()193     public ArrayMap<String, List<String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps() {
194         return mDisabledUntilUsedPreinstalledCarrierAssociatedApps;
195     }
196 
SystemConfig()197     SystemConfig() {
198         // Read configuration from system
199         readPermissions(Environment.buildPath(
200                 Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
201         // Read configuration from the old permissions dir
202         readPermissions(Environment.buildPath(
203                 Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
204         // Allow ODM to customize system configs around libs, features and apps
205         int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;
206         readPermissions(Environment.buildPath(
207                 Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
208         readPermissions(Environment.buildPath(
209                 Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
210         // Only allow OEM to customize features
211         readPermissions(Environment.buildPath(
212                 Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES);
213         readPermissions(Environment.buildPath(
214                 Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES);
215     }
216 
readPermissions(File libraryDir, int permissionFlag)217     void readPermissions(File libraryDir, int permissionFlag) {
218         // Read permissions from given directory.
219         if (!libraryDir.exists() || !libraryDir.isDirectory()) {
220             if (permissionFlag == ALLOW_ALL) {
221                 Slog.w(TAG, "No directory " + libraryDir + ", skipping");
222             }
223             return;
224         }
225         if (!libraryDir.canRead()) {
226             Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
227             return;
228         }
229 
230         // Iterate over the files in the directory and scan .xml files
231         File platformFile = null;
232         for (File f : libraryDir.listFiles()) {
233             // We'll read platform.xml last
234             if (f.getPath().endsWith("etc/permissions/platform.xml")) {
235                 platformFile = f;
236                 continue;
237             }
238 
239             if (!f.getPath().endsWith(".xml")) {
240                 Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
241                 continue;
242             }
243             if (!f.canRead()) {
244                 Slog.w(TAG, "Permissions library file " + f + " cannot be read");
245                 continue;
246             }
247 
248             readPermissionsFromXml(f, permissionFlag);
249         }
250 
251         // Read platform permissions last so it will take precedence
252         if (platformFile != null) {
253             readPermissionsFromXml(platformFile, permissionFlag);
254         }
255     }
256 
readPermissionsFromXml(File permFile, int permissionFlag)257     private void readPermissionsFromXml(File permFile, int permissionFlag) {
258         FileReader permReader = null;
259         try {
260             permReader = new FileReader(permFile);
261         } catch (FileNotFoundException e) {
262             Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
263             return;
264         }
265 
266         final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
267 
268         try {
269             XmlPullParser parser = Xml.newPullParser();
270             parser.setInput(permReader);
271 
272             int type;
273             while ((type=parser.next()) != parser.START_TAG
274                        && type != parser.END_DOCUMENT) {
275                 ;
276             }
277 
278             if (type != parser.START_TAG) {
279                 throw new XmlPullParserException("No start tag found");
280             }
281 
282             if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
283                 throw new XmlPullParserException("Unexpected start tag in " + permFile
284                         + ": found " + parser.getName() + ", expected 'permissions' or 'config'");
285             }
286 
287             boolean allowAll = permissionFlag == ALLOW_ALL;
288             boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
289             boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
290             boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
291             boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
292             while (true) {
293                 XmlUtils.nextElement(parser);
294                 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
295                     break;
296                 }
297 
298                 String name = parser.getName();
299                 if ("group".equals(name) && allowAll) {
300                     String gidStr = parser.getAttributeValue(null, "gid");
301                     if (gidStr != null) {
302                         int gid = android.os.Process.getGidForName(gidStr);
303                         mGlobalGids = appendInt(mGlobalGids, gid);
304                     } else {
305                         Slog.w(TAG, "<group> without gid in " + permFile + " at "
306                                 + parser.getPositionDescription());
307                     }
308 
309                     XmlUtils.skipCurrentTag(parser);
310                     continue;
311                 } else if ("permission".equals(name) && allowPermissions) {
312                     String perm = parser.getAttributeValue(null, "name");
313                     if (perm == null) {
314                         Slog.w(TAG, "<permission> without name in " + permFile + " at "
315                                 + parser.getPositionDescription());
316                         XmlUtils.skipCurrentTag(parser);
317                         continue;
318                     }
319                     perm = perm.intern();
320                     readPermission(parser, perm);
321 
322                 } else if ("assign-permission".equals(name) && allowPermissions) {
323                     String perm = parser.getAttributeValue(null, "name");
324                     if (perm == null) {
325                         Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
326                                 + parser.getPositionDescription());
327                         XmlUtils.skipCurrentTag(parser);
328                         continue;
329                     }
330                     String uidStr = parser.getAttributeValue(null, "uid");
331                     if (uidStr == null) {
332                         Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
333                                 + parser.getPositionDescription());
334                         XmlUtils.skipCurrentTag(parser);
335                         continue;
336                     }
337                     int uid = Process.getUidForName(uidStr);
338                     if (uid < 0) {
339                         Slog.w(TAG, "<assign-permission> with unknown uid \""
340                                 + uidStr + "  in " + permFile + " at "
341                                 + parser.getPositionDescription());
342                         XmlUtils.skipCurrentTag(parser);
343                         continue;
344                     }
345                     perm = perm.intern();
346                     ArraySet<String> perms = mSystemPermissions.get(uid);
347                     if (perms == null) {
348                         perms = new ArraySet<String>();
349                         mSystemPermissions.put(uid, perms);
350                     }
351                     perms.add(perm);
352                     XmlUtils.skipCurrentTag(parser);
353 
354                 } else if ("library".equals(name) && allowLibs) {
355                     String lname = parser.getAttributeValue(null, "name");
356                     String lfile = parser.getAttributeValue(null, "file");
357                     if (lname == null) {
358                         Slog.w(TAG, "<library> without name in " + permFile + " at "
359                                 + parser.getPositionDescription());
360                     } else if (lfile == null) {
361                         Slog.w(TAG, "<library> without file in " + permFile + " at "
362                                 + parser.getPositionDescription());
363                     } else {
364                         //Log.i(TAG, "Got library " + lname + " in " + lfile);
365                         mSharedLibraries.put(lname, lfile);
366                     }
367                     XmlUtils.skipCurrentTag(parser);
368                     continue;
369 
370                 } else if ("feature".equals(name) && allowFeatures) {
371                     String fname = parser.getAttributeValue(null, "name");
372                     int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
373                     boolean allowed;
374                     if (!lowRam) {
375                         allowed = true;
376                     } else {
377                         String notLowRam = parser.getAttributeValue(null, "notLowRam");
378                         allowed = !"true".equals(notLowRam);
379                     }
380                     if (fname == null) {
381                         Slog.w(TAG, "<feature> without name in " + permFile + " at "
382                                 + parser.getPositionDescription());
383                     } else if (allowed) {
384                         addFeature(fname, fversion);
385                     }
386                     XmlUtils.skipCurrentTag(parser);
387                     continue;
388 
389                 } else if ("unavailable-feature".equals(name) && allowFeatures) {
390                     String fname = parser.getAttributeValue(null, "name");
391                     if (fname == null) {
392                         Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
393                                 + parser.getPositionDescription());
394                     } else {
395                         mUnavailableFeatures.add(fname);
396                     }
397                     XmlUtils.skipCurrentTag(parser);
398                     continue;
399 
400                 } else if ("allow-in-power-save-except-idle".equals(name) && allowAll) {
401                     String pkgname = parser.getAttributeValue(null, "package");
402                     if (pkgname == null) {
403                         Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
404                                 + permFile + " at " + parser.getPositionDescription());
405                     } else {
406                         mAllowInPowerSaveExceptIdle.add(pkgname);
407                     }
408                     XmlUtils.skipCurrentTag(parser);
409                     continue;
410 
411                 } else if ("allow-in-power-save".equals(name) && allowAll) {
412                     String pkgname = parser.getAttributeValue(null, "package");
413                     if (pkgname == null) {
414                         Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
415                                 + parser.getPositionDescription());
416                     } else {
417                         mAllowInPowerSave.add(pkgname);
418                     }
419                     XmlUtils.skipCurrentTag(parser);
420                     continue;
421 
422                 } else if ("allow-in-data-usage-save".equals(name) && allowAll) {
423                     String pkgname = parser.getAttributeValue(null, "package");
424                     if (pkgname == null) {
425                         Slog.w(TAG, "<allow-in-data-usage-save> without package in " + permFile
426                                 + " at " + parser.getPositionDescription());
427                     } else {
428                         mAllowInDataUsageSave.add(pkgname);
429                     }
430                     XmlUtils.skipCurrentTag(parser);
431                     continue;
432 
433                 } else if ("app-link".equals(name) && allowAppConfigs) {
434                     String pkgname = parser.getAttributeValue(null, "package");
435                     if (pkgname == null) {
436                         Slog.w(TAG, "<app-link> without package in " + permFile + " at "
437                                 + parser.getPositionDescription());
438                     } else {
439                         mLinkedApps.add(pkgname);
440                     }
441                     XmlUtils.skipCurrentTag(parser);
442                 } else if ("system-user-whitelisted-app".equals(name) && allowAppConfigs) {
443                     String pkgname = parser.getAttributeValue(null, "package");
444                     if (pkgname == null) {
445                         Slog.w(TAG, "<system-user-whitelisted-app> without package in " + permFile
446                                 + " at " + parser.getPositionDescription());
447                     } else {
448                         mSystemUserWhitelistedApps.add(pkgname);
449                     }
450                     XmlUtils.skipCurrentTag(parser);
451                 } else if ("system-user-blacklisted-app".equals(name) && allowAppConfigs) {
452                     String pkgname = parser.getAttributeValue(null, "package");
453                     if (pkgname == null) {
454                         Slog.w(TAG, "<system-user-blacklisted-app without package in " + permFile
455                                 + " at " + parser.getPositionDescription());
456                     } else {
457                         mSystemUserBlacklistedApps.add(pkgname);
458                     }
459                     XmlUtils.skipCurrentTag(parser);
460                 } else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) {
461                     String pkgname = parser.getAttributeValue(null, "package");
462                     String clsname = parser.getAttributeValue(null, "class");
463                     if (pkgname == null) {
464                         Slog.w(TAG, "<default-enabled-vr-app without package in " + permFile
465                                 + " at " + parser.getPositionDescription());
466                     } else if (clsname == null) {
467                         Slog.w(TAG, "<default-enabled-vr-app without class in " + permFile
468                                 + " at " + parser.getPositionDescription());
469                     } else {
470                         mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
471                     }
472                     XmlUtils.skipCurrentTag(parser);
473                 } else if ("backup-transport-whitelisted-service".equals(name) && allowFeatures) {
474                     String serviceName = parser.getAttributeValue(null, "service");
475                     if (serviceName == null) {
476                         Slog.w(TAG, "<backup-transport-whitelisted-service> without service in "
477                                 + permFile + " at " + parser.getPositionDescription());
478                     } else {
479                         ComponentName cn = ComponentName.unflattenFromString(serviceName);
480                         if (cn == null) {
481                             Slog.w(TAG,
482                                     "<backup-transport-whitelisted-service> with invalid service name "
483                                     + serviceName + " in "+ permFile
484                                     + " at " + parser.getPositionDescription());
485                         } else {
486                             mBackupTransportWhitelist.add(cn);
487                         }
488                     }
489                     XmlUtils.skipCurrentTag(parser);
490                 } else if ("disabled-until-used-preinstalled-carrier-associated-app".equals(name)
491                         && allowAppConfigs) {
492                     String pkgname = parser.getAttributeValue(null, "package");
493                     String carrierPkgname = parser.getAttributeValue(null, "carrierAppPackage");
494                     if (pkgname == null || carrierPkgname == null) {
495                         Slog.w(TAG, "<disabled-until-used-preinstalled-carrier-associated-app"
496                                 + " without package or carrierAppPackage in " + permFile + " at "
497                                 + parser.getPositionDescription());
498                     } else {
499                         List<String> associatedPkgs =
500                                 mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
501                                         carrierPkgname);
502                         if (associatedPkgs == null) {
503                             associatedPkgs = new ArrayList<>();
504                             mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
505                                     carrierPkgname, associatedPkgs);
506                         }
507                         associatedPkgs.add(pkgname);
508                     }
509                     XmlUtils.skipCurrentTag(parser);
510                 } else {
511                     XmlUtils.skipCurrentTag(parser);
512                     continue;
513                 }
514             }
515         } catch (XmlPullParserException e) {
516             Slog.w(TAG, "Got exception parsing permissions.", e);
517         } catch (IOException e) {
518             Slog.w(TAG, "Got exception parsing permissions.", e);
519         } finally {
520             IoUtils.closeQuietly(permReader);
521         }
522 
523         // Some devices can be field-converted to FBE, so offer to splice in
524         // those features if not already defined by the static config
525         if (StorageManager.isFileEncryptedNativeOnly()) {
526             addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION, 0);
527             addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0);
528         }
529 
530         for (String featureName : mUnavailableFeatures) {
531             removeFeature(featureName);
532         }
533     }
534 
addFeature(String name, int version)535     private void addFeature(String name, int version) {
536         FeatureInfo fi = mAvailableFeatures.get(name);
537         if (fi == null) {
538             fi = new FeatureInfo();
539             fi.name = name;
540             fi.version = version;
541             mAvailableFeatures.put(name, fi);
542         } else {
543             fi.version = Math.max(fi.version, version);
544         }
545     }
546 
removeFeature(String name)547     private void removeFeature(String name) {
548         if (mAvailableFeatures.remove(name) != null) {
549             Slog.d(TAG, "Removed unavailable feature " + name);
550         }
551     }
552 
readPermission(XmlPullParser parser, String name)553     void readPermission(XmlPullParser parser, String name)
554             throws IOException, XmlPullParserException {
555         if (mPermissions.containsKey(name)) {
556             throw new IllegalStateException("Duplicate permission definition for " + name);
557         }
558 
559         final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
560         final PermissionEntry perm = new PermissionEntry(name, perUser);
561         mPermissions.put(name, perm);
562 
563         int outerDepth = parser.getDepth();
564         int type;
565         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
566                && (type != XmlPullParser.END_TAG
567                        || parser.getDepth() > outerDepth)) {
568             if (type == XmlPullParser.END_TAG
569                     || type == XmlPullParser.TEXT) {
570                 continue;
571             }
572 
573             String tagName = parser.getName();
574             if ("group".equals(tagName)) {
575                 String gidStr = parser.getAttributeValue(null, "gid");
576                 if (gidStr != null) {
577                     int gid = Process.getGidForName(gidStr);
578                     perm.gids = appendInt(perm.gids, gid);
579                 } else {
580                     Slog.w(TAG, "<group> without gid at "
581                             + parser.getPositionDescription());
582                 }
583             }
584             XmlUtils.skipCurrentTag(parser);
585         }
586     }
587 }
588