Home | History | Annotate | Download | only in connectivity
      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.connectivity;
     18 
     19 import static android.Manifest.permission.CHANGE_NETWORK_STATE;
     20 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
     21 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
     22 import static android.Manifest.permission.NETWORK_STACK;
     23 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
     24 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
     25 import static android.content.pm.PackageManager.GET_PERMISSIONS;
     26 
     27 import android.content.BroadcastReceiver;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.content.IntentFilter;
     31 import android.content.pm.ApplicationInfo;
     32 import android.content.pm.PackageInfo;
     33 import android.content.pm.PackageManager;
     34 import android.content.pm.PackageManager.NameNotFoundException;
     35 import android.content.pm.UserInfo;
     36 import android.net.Uri;
     37 import android.os.INetworkManagementService;
     38 import android.os.RemoteException;
     39 import android.os.UserHandle;
     40 import android.os.UserManager;
     41 import android.text.TextUtils;
     42 import android.util.Log;
     43 
     44 import com.android.internal.annotations.VisibleForTesting;
     45 
     46 import java.util.ArrayList;
     47 import java.util.HashMap;
     48 import java.util.HashSet;
     49 import java.util.List;
     50 import java.util.Map.Entry;
     51 import java.util.Map;
     52 import java.util.Set;
     53 
     54 /**
     55  * A utility class to inform Netd of UID permisisons.
     56  * Does a mass update at boot and then monitors for app install/remove.
     57  *
     58  * @hide
     59  */
     60 public class PermissionMonitor {
     61     private static final String TAG = "PermissionMonitor";
     62     private static final boolean DBG = true;
     63     private static final Boolean SYSTEM = Boolean.TRUE;
     64     private static final Boolean NETWORK = Boolean.FALSE;
     65 
     66     private final Context mContext;
     67     private final PackageManager mPackageManager;
     68     private final UserManager mUserManager;
     69     private final INetworkManagementService mNetd;
     70     private final BroadcastReceiver mIntentReceiver;
     71 
     72     // Values are User IDs.
     73     private final Set<Integer> mUsers = new HashSet<>();
     74 
     75     // Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission.
     76     private final Map<Integer, Boolean> mApps = new HashMap<>();
     77 
     78     public PermissionMonitor(Context context, INetworkManagementService netd) {
     79         mContext = context;
     80         mPackageManager = context.getPackageManager();
     81         mUserManager = UserManager.get(context);
     82         mNetd = netd;
     83         mIntentReceiver = new BroadcastReceiver() {
     84             @Override
     85             public void onReceive(Context context, Intent intent) {
     86                 String action = intent.getAction();
     87                 int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
     88                 int appUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
     89                 Uri appData = intent.getData();
     90                 String appName = appData != null ? appData.getSchemeSpecificPart() : null;
     91 
     92                 if (Intent.ACTION_USER_ADDED.equals(action)) {
     93                     onUserAdded(user);
     94                 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
     95                     onUserRemoved(user);
     96                 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
     97                     onAppAdded(appName, appUid);
     98                 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
     99                     onAppRemoved(appUid);
    100                 }
    101             }
    102         };
    103     }
    104 
    105     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
    106     // receiver to monitor ongoing UID changes, so this shouldn't/needn't be called again.
    107     public synchronized void startMonitoring() {
    108         log("Monitoring");
    109 
    110         IntentFilter intentFilter = new IntentFilter();
    111         intentFilter.addAction(Intent.ACTION_USER_ADDED);
    112         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
    113         mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
    114 
    115         intentFilter = new IntentFilter();
    116         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    117         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    118         intentFilter.addDataScheme("package");
    119         mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
    120 
    121         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
    122         if (apps == null) {
    123             loge("No apps");
    124             return;
    125         }
    126 
    127         for (PackageInfo app : apps) {
    128             int uid = app.applicationInfo != null ? app.applicationInfo.uid : -1;
    129             if (uid < 0) {
    130                 continue;
    131             }
    132 
    133             boolean isNetwork = hasNetworkPermission(app);
    134             boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
    135 
    136             if (isNetwork || hasRestrictedPermission) {
    137                 Boolean permission = mApps.get(uid);
    138                 // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
    139                 // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
    140                 if (permission == null || permission == NETWORK) {
    141                     mApps.put(uid, hasRestrictedPermission);
    142                 }
    143             }
    144         }
    145 
    146         List<UserInfo> users = mUserManager.getUsers(true);  // exclude dying users
    147         if (users != null) {
    148             for (UserInfo user : users) {
    149                 mUsers.add(user.id);
    150             }
    151         }
    152 
    153         log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
    154         update(mUsers, mApps, true);
    155     }
    156 
    157     @VisibleForTesting
    158     boolean isPreinstalledSystemApp(PackageInfo app) {
    159         int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
    160         return (flags & (FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP)) != 0;
    161     }
    162 
    163     @VisibleForTesting
    164     boolean hasPermission(PackageInfo app, String permission) {
    165         if (app.requestedPermissions != null) {
    166             for (String p : app.requestedPermissions) {
    167                 if (permission.equals(p)) {
    168                     return true;
    169                 }
    170             }
    171         }
    172         return false;
    173     }
    174 
    175     private boolean hasNetworkPermission(PackageInfo app) {
    176         return hasPermission(app, CHANGE_NETWORK_STATE);
    177     }
    178 
    179     private boolean hasRestrictedNetworkPermission(PackageInfo app) {
    180         if (isPreinstalledSystemApp(app)) return true;
    181         return hasPermission(app, CONNECTIVITY_INTERNAL)
    182                 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
    183     }
    184 
    185     private boolean hasUseBackgroundNetworksPermission(PackageInfo app) {
    186         // This function defines what it means to hold the permission to use
    187         // background networks.
    188         return hasPermission(app, CHANGE_NETWORK_STATE)
    189                 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)
    190                 || hasPermission(app, CONNECTIVITY_INTERNAL)
    191                 || hasPermission(app, NETWORK_STACK)
    192                 // TODO : remove this check (b/31479477). Not all preinstalled apps should
    193                 // have access to background networks, they should just request the appropriate
    194                 // permission for their use case from the list above.
    195                 || isPreinstalledSystemApp(app);
    196     }
    197 
    198     public boolean hasUseBackgroundNetworksPermission(int uid) {
    199         final String[] names = mPackageManager.getPackagesForUid(uid);
    200         if (null == names || names.length == 0) return false;
    201         try {
    202             // Only using the first package name. There may be multiple names if multiple
    203             // apps share the same UID, but in that case they also share permissions so
    204             // querying with any of the names will return the same results.
    205             int userId = UserHandle.getUserId(uid);
    206             final PackageInfo app = mPackageManager.getPackageInfoAsUser(
    207                     names[0], GET_PERMISSIONS, userId);
    208             return hasUseBackgroundNetworksPermission(app);
    209         } catch (NameNotFoundException e) {
    210             // App not found.
    211             loge("NameNotFoundException " + names[0], e);
    212             return false;
    213         }
    214     }
    215 
    216     private int[] toIntArray(List<Integer> list) {
    217         int[] array = new int[list.size()];
    218         for (int i = 0; i < list.size(); i++) {
    219             array[i] = list.get(i);
    220         }
    221         return array;
    222     }
    223 
    224     private void update(Set<Integer> users, Map<Integer, Boolean> apps, boolean add) {
    225         List<Integer> network = new ArrayList<>();
    226         List<Integer> system = new ArrayList<>();
    227         for (Entry<Integer, Boolean> app : apps.entrySet()) {
    228             List<Integer> list = app.getValue() ? system : network;
    229             for (int user : users) {
    230                 list.add(UserHandle.getUid(user, app.getKey()));
    231             }
    232         }
    233         try {
    234             if (add) {
    235                 mNetd.setPermission("NETWORK", toIntArray(network));
    236                 mNetd.setPermission("SYSTEM", toIntArray(system));
    237             } else {
    238                 mNetd.clearPermission(toIntArray(network));
    239                 mNetd.clearPermission(toIntArray(system));
    240             }
    241         } catch (RemoteException e) {
    242             loge("Exception when updating permissions: " + e);
    243         }
    244     }
    245 
    246     private synchronized void onUserAdded(int user) {
    247         if (user < 0) {
    248             loge("Invalid user in onUserAdded: " + user);
    249             return;
    250         }
    251         mUsers.add(user);
    252 
    253         Set<Integer> users = new HashSet<>();
    254         users.add(user);
    255         update(users, mApps, true);
    256     }
    257 
    258     private synchronized void onUserRemoved(int user) {
    259         if (user < 0) {
    260             loge("Invalid user in onUserRemoved: " + user);
    261             return;
    262         }
    263         mUsers.remove(user);
    264 
    265         Set<Integer> users = new HashSet<>();
    266         users.add(user);
    267         update(users, mApps, false);
    268     }
    269 
    270 
    271     private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
    272         if (currentPermission == SYSTEM) {
    273             return currentPermission;
    274         }
    275         try {
    276             final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
    277             final boolean isNetwork = hasNetworkPermission(app);
    278             final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
    279             if (isNetwork || hasRestrictedPermission) {
    280                 currentPermission = hasRestrictedPermission;
    281             }
    282         } catch (NameNotFoundException e) {
    283             // App not found.
    284             loge("NameNotFoundException " + name);
    285         }
    286         return currentPermission;
    287     }
    288 
    289     private synchronized void onAppAdded(String appName, int appUid) {
    290         if (TextUtils.isEmpty(appName) || appUid < 0) {
    291             loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
    292             return;
    293         }
    294 
    295         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
    296         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
    297         final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
    298         if (permission != mApps.get(appUid)) {
    299             mApps.put(appUid, permission);
    300 
    301             Map<Integer, Boolean> apps = new HashMap<>();
    302             apps.put(appUid, permission);
    303             update(mUsers, apps, true);
    304         }
    305     }
    306 
    307     private synchronized void onAppRemoved(int appUid) {
    308         if (appUid < 0) {
    309             loge("Invalid app in onAppRemoved: " + appUid);
    310             return;
    311         }
    312         Map<Integer, Boolean> apps = new HashMap<>();
    313 
    314         Boolean permission = null;
    315         String[] packages = mPackageManager.getPackagesForUid(appUid);
    316         if (packages != null && packages.length > 0) {
    317             for (String name : packages) {
    318                 permission = highestPermissionForUid(permission, name);
    319                 if (permission == SYSTEM) {
    320                     // An app with this UID still has the SYSTEM permission.
    321                     // Therefore, this UID must already have the SYSTEM permission.
    322                     // Nothing to do.
    323                     return;
    324                 }
    325             }
    326         }
    327         if (permission == mApps.get(appUid)) {
    328             // The permissions of this UID have not changed. Nothing to do.
    329             return;
    330         } else if (permission != null) {
    331             mApps.put(appUid, permission);
    332             apps.put(appUid, permission);
    333             update(mUsers, apps, true);
    334         } else {
    335             mApps.remove(appUid);
    336             apps.put(appUid, NETWORK);  // doesn't matter which permission we pick here
    337             update(mUsers, apps, false);
    338         }
    339     }
    340 
    341     private static void log(String s) {
    342         if (DBG) {
    343             Log.d(TAG, s);
    344         }
    345     }
    346 
    347     private static void loge(String s) {
    348         Log.e(TAG, s);
    349     }
    350 
    351     private static void loge(String s, Throwable e) {
    352         Log.e(TAG, s, e);
    353     }
    354 }
    355