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