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.content.pm.ApplicationInfo.FLAG_SYSTEM;
     22 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
     23 import static android.content.pm.PackageManager.GET_PERMISSIONS;
     24 
     25 import android.content.BroadcastReceiver;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.pm.PackageInfo;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.PackageManager.NameNotFoundException;
     32 import android.content.pm.UserInfo;
     33 import android.net.Uri;
     34 import android.os.INetworkManagementService;
     35 import android.os.RemoteException;
     36 import android.os.UserHandle;
     37 import android.os.UserManager;
     38 import android.text.TextUtils;
     39 import android.util.Log;
     40 
     41 import java.util.ArrayList;
     42 import java.util.HashMap;
     43 import java.util.HashSet;
     44 import java.util.List;
     45 import java.util.Map.Entry;
     46 import java.util.Map;
     47 import java.util.Set;
     48 
     49 /**
     50  * A utility class to inform Netd of UID permisisons.
     51  * Does a mass update at boot and then monitors for app install/remove.
     52  *
     53  * @hide
     54  */
     55 public class PermissionMonitor {
     56     private static final String TAG = "PermissionMonitor";
     57     private static final boolean DBG = true;
     58     private static final boolean SYSTEM = true;
     59     private static final boolean NETWORK = false;
     60 
     61     private final Context mContext;
     62     private final PackageManager mPackageManager;
     63     private final UserManager mUserManager;
     64     private final INetworkManagementService mNetd;
     65     private final BroadcastReceiver mIntentReceiver;
     66 
     67     // Values are User IDs.
     68     private final Set<Integer> mUsers = new HashSet<Integer>();
     69 
     70     // Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission.
     71     private final Map<Integer, Boolean> mApps = new HashMap<Integer, Boolean>();
     72 
     73     public PermissionMonitor(Context context, INetworkManagementService netd) {
     74         mContext = context;
     75         mPackageManager = context.getPackageManager();
     76         mUserManager = UserManager.get(context);
     77         mNetd = netd;
     78         mIntentReceiver = new BroadcastReceiver() {
     79             @Override
     80             public void onReceive(Context context, Intent intent) {
     81                 String action = intent.getAction();
     82                 int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
     83                 int appUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
     84                 Uri appData = intent.getData();
     85                 String appName = appData != null ? appData.getSchemeSpecificPart() : null;
     86 
     87                 if (Intent.ACTION_USER_ADDED.equals(action)) {
     88                     onUserAdded(user);
     89                 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
     90                     onUserRemoved(user);
     91                 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
     92                     onAppAdded(appName, appUid);
     93                 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
     94                     onAppRemoved(appUid);
     95                 }
     96             }
     97         };
     98     }
     99 
    100     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
    101     // receiver to monitor ongoing UID changes, so this shouldn't/needn't be called again.
    102     public synchronized void startMonitoring() {
    103         log("Monitoring");
    104 
    105         IntentFilter intentFilter = new IntentFilter();
    106         intentFilter.addAction(Intent.ACTION_USER_ADDED);
    107         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
    108         mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
    109 
    110         intentFilter = new IntentFilter();
    111         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    112         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    113         intentFilter.addDataScheme("package");
    114         mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
    115 
    116         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
    117         if (apps == null) {
    118             loge("No apps");
    119             return;
    120         }
    121 
    122         for (PackageInfo app : apps) {
    123             int uid = app.applicationInfo != null ? app.applicationInfo.uid : -1;
    124             if (uid < 0) {
    125                 continue;
    126             }
    127 
    128             boolean isNetwork = hasNetworkPermission(app);
    129             boolean isSystem = hasSystemPermission(app);
    130 
    131             if (isNetwork || isSystem) {
    132                 Boolean permission = mApps.get(uid);
    133                 // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
    134                 // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
    135                 if (permission == null || permission == NETWORK) {
    136                     mApps.put(uid, isSystem);
    137                 }
    138             }
    139         }
    140 
    141         List<UserInfo> users = mUserManager.getUsers(true);  // exclude dying users
    142         if (users != null) {
    143             for (UserInfo user : users) {
    144                 mUsers.add(user.id);
    145             }
    146         }
    147 
    148         log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
    149         update(mUsers, mApps, true);
    150     }
    151 
    152     private boolean hasPermission(PackageInfo app, String permission) {
    153         if (app.requestedPermissions != null) {
    154             for (String p : app.requestedPermissions) {
    155                 if (permission.equals(p)) {
    156                     return true;
    157                 }
    158             }
    159         }
    160         return false;
    161     }
    162 
    163     private boolean hasNetworkPermission(PackageInfo app) {
    164         return hasPermission(app, CHANGE_NETWORK_STATE);
    165     }
    166 
    167     private boolean hasSystemPermission(PackageInfo app) {
    168         int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
    169         if ((flags & FLAG_SYSTEM) != 0 || (flags & FLAG_UPDATED_SYSTEM_APP) != 0) {
    170             return true;
    171         }
    172         return hasPermission(app, CONNECTIVITY_INTERNAL);
    173     }
    174 
    175     private int[] toIntArray(List<Integer> list) {
    176         int[] array = new int[list.size()];
    177         for (int i = 0; i < list.size(); i++) {
    178             array[i] = list.get(i);
    179         }
    180         return array;
    181     }
    182 
    183     private void update(Set<Integer> users, Map<Integer, Boolean> apps, boolean add) {
    184         List<Integer> network = new ArrayList<Integer>();
    185         List<Integer> system = new ArrayList<Integer>();
    186         for (Entry<Integer, Boolean> app : apps.entrySet()) {
    187             List<Integer> list = app.getValue() ? system : network;
    188             for (int user : users) {
    189                 list.add(UserHandle.getUid(user, app.getKey()));
    190             }
    191         }
    192         try {
    193             if (add) {
    194                 mNetd.setPermission("NETWORK", toIntArray(network));
    195                 mNetd.setPermission("SYSTEM", toIntArray(system));
    196             } else {
    197                 mNetd.clearPermission(toIntArray(network));
    198                 mNetd.clearPermission(toIntArray(system));
    199             }
    200         } catch (RemoteException e) {
    201             loge("Exception when updating permissions: " + e);
    202         }
    203     }
    204 
    205     private synchronized void onUserAdded(int user) {
    206         if (user < 0) {
    207             loge("Invalid user in onUserAdded: " + user);
    208             return;
    209         }
    210         mUsers.add(user);
    211 
    212         Set<Integer> users = new HashSet<Integer>();
    213         users.add(user);
    214         update(users, mApps, true);
    215     }
    216 
    217     private synchronized void onUserRemoved(int user) {
    218         if (user < 0) {
    219             loge("Invalid user in onUserRemoved: " + user);
    220             return;
    221         }
    222         mUsers.remove(user);
    223 
    224         Set<Integer> users = new HashSet<Integer>();
    225         users.add(user);
    226         update(users, mApps, false);
    227     }
    228 
    229     private synchronized void onAppAdded(String appName, int appUid) {
    230         if (TextUtils.isEmpty(appName) || appUid < 0) {
    231             loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
    232             return;
    233         }
    234 
    235         try {
    236             PackageInfo app = mPackageManager.getPackageInfo(appName, GET_PERMISSIONS);
    237             boolean isNetwork = hasNetworkPermission(app);
    238             boolean isSystem = hasSystemPermission(app);
    239             if (isNetwork || isSystem) {
    240                 Boolean permission = mApps.get(appUid);
    241                 // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
    242                 // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
    243                 if (permission == null || permission == NETWORK) {
    244                     mApps.put(appUid, isSystem);
    245 
    246                     Map<Integer, Boolean> apps = new HashMap<Integer, Boolean>();
    247                     apps.put(appUid, isSystem);
    248                     update(mUsers, apps, true);
    249                 }
    250             }
    251         } catch (NameNotFoundException e) {
    252             loge("NameNotFoundException in onAppAdded: " + e);
    253         }
    254     }
    255 
    256     private synchronized void onAppRemoved(int appUid) {
    257         if (appUid < 0) {
    258             loge("Invalid app in onAppRemoved: " + appUid);
    259             return;
    260         }
    261         mApps.remove(appUid);
    262 
    263         Map<Integer, Boolean> apps = new HashMap<Integer, Boolean>();
    264         apps.put(appUid, NETWORK);  // doesn't matter which permission we pick here
    265         update(mUsers, apps, false);
    266     }
    267 
    268     private static void log(String s) {
    269         if (DBG) {
    270             Log.d(TAG, s);
    271         }
    272     }
    273 
    274     private static void loge(String s) {
    275         Log.e(TAG, s);
    276     }
    277 }
    278