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