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