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