Send app permissions to netd.
Based largely off Robert's http://ag/546170 (thanks!) Bug: 15413737 Change-Id: I8a1f0a184923c4c0a4935e6b88895bcc05e39f02
This commit is contained in:
@@ -137,6 +137,7 @@ import com.android.server.connectivity.Nat464Xlat;
|
|||||||
import com.android.server.connectivity.NetworkAgentInfo;
|
import com.android.server.connectivity.NetworkAgentInfo;
|
||||||
import com.android.server.connectivity.NetworkMonitor;
|
import com.android.server.connectivity.NetworkMonitor;
|
||||||
import com.android.server.connectivity.PacManager;
|
import com.android.server.connectivity.PacManager;
|
||||||
|
import com.android.server.connectivity.PermissionMonitor;
|
||||||
import com.android.server.connectivity.Tethering;
|
import com.android.server.connectivity.Tethering;
|
||||||
import com.android.server.connectivity.Vpn;
|
import com.android.server.connectivity.Vpn;
|
||||||
import com.android.server.net.BaseNetworkObserver;
|
import com.android.server.net.BaseNetworkObserver;
|
||||||
@@ -225,6 +226,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
|
|
||||||
private Tethering mTethering;
|
private Tethering mTethering;
|
||||||
|
|
||||||
|
private final PermissionMonitor mPermissionMonitor;
|
||||||
|
|
||||||
private KeyStore mKeyStore;
|
private KeyStore mKeyStore;
|
||||||
|
|
||||||
@GuardedBy("mVpns")
|
@GuardedBy("mVpns")
|
||||||
@@ -702,6 +705,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
|
|
||||||
mTethering = new Tethering(mContext, mNetd, statsService, mHandler.getLooper());
|
mTethering = new Tethering(mContext, mNetd, statsService, mHandler.getLooper());
|
||||||
|
|
||||||
|
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
|
||||||
|
|
||||||
//set up the listener for user state for creating user VPNs
|
//set up the listener for user state for creating user VPNs
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(Intent.ACTION_USER_STARTING);
|
intentFilter.addAction(Intent.ACTION_USER_STARTING);
|
||||||
@@ -1484,6 +1489,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY));
|
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY));
|
||||||
|
|
||||||
|
mPermissionMonitor.startMonitoring();
|
||||||
}
|
}
|
||||||
|
|
||||||
private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
|
private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
|
||||||
|
|||||||
@@ -0,0 +1,277 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.connectivity;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
|
||||||
|
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
|
||||||
|
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
|
||||||
|
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
||||||
|
import static android.content.pm.PackageManager.GET_PERMISSIONS;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.content.pm.UserInfo;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.INetworkManagementService;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.os.UserManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class to inform Netd of UID permisisons.
|
||||||
|
* Does a mass update at boot and then monitors for app install/remove.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class PermissionMonitor {
|
||||||
|
private static final String TAG = "PermissionMonitor";
|
||||||
|
private static final boolean DBG = true;
|
||||||
|
private static final boolean SYSTEM = true;
|
||||||
|
private static final boolean NETWORK = false;
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final PackageManager mPackageManager;
|
||||||
|
private final UserManager mUserManager;
|
||||||
|
private final INetworkManagementService mNetd;
|
||||||
|
private final BroadcastReceiver mIntentReceiver;
|
||||||
|
|
||||||
|
// Values are User IDs.
|
||||||
|
private final Set<Integer> mUsers = new HashSet<Integer>();
|
||||||
|
|
||||||
|
// Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission.
|
||||||
|
private final Map<Integer, Boolean> mApps = new HashMap<Integer, Boolean>();
|
||||||
|
|
||||||
|
public PermissionMonitor(Context context, INetworkManagementService netd) {
|
||||||
|
mContext = context;
|
||||||
|
mPackageManager = context.getPackageManager();
|
||||||
|
mUserManager = UserManager.get(context);
|
||||||
|
mNetd = netd;
|
||||||
|
mIntentReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
|
||||||
|
int appUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
|
||||||
|
Uri appData = intent.getData();
|
||||||
|
String appName = appData != null ? appData.getSchemeSpecificPart() : null;
|
||||||
|
|
||||||
|
if (Intent.ACTION_USER_ADDED.equals(action)) {
|
||||||
|
onUserAdded(user);
|
||||||
|
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
|
||||||
|
onUserRemoved(user);
|
||||||
|
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
|
||||||
|
onAppAdded(appName, appUid);
|
||||||
|
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
|
||||||
|
onAppRemoved(appUid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
|
||||||
|
// receiver to monitor ongoing UID changes, so this shouldn't/needn't be called again.
|
||||||
|
public synchronized void startMonitoring() {
|
||||||
|
log("Monitoring");
|
||||||
|
|
||||||
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
|
intentFilter.addAction(Intent.ACTION_USER_ADDED);
|
||||||
|
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
|
||||||
|
mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
|
||||||
|
|
||||||
|
intentFilter = new IntentFilter();
|
||||||
|
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||||
|
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||||
|
intentFilter.addDataScheme("package");
|
||||||
|
mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
|
||||||
|
|
||||||
|
List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
|
||||||
|
if (apps == null) {
|
||||||
|
loge("No apps");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PackageInfo app : apps) {
|
||||||
|
int uid = app.applicationInfo != null ? app.applicationInfo.uid : -1;
|
||||||
|
if (uid < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isNetwork = hasNetworkPermission(app);
|
||||||
|
boolean isSystem = hasSystemPermission(app);
|
||||||
|
|
||||||
|
if (isNetwork || isSystem) {
|
||||||
|
Boolean permission = mApps.get(uid);
|
||||||
|
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
|
||||||
|
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
|
||||||
|
if (permission == null || permission == NETWORK) {
|
||||||
|
mApps.put(uid, isSystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users
|
||||||
|
if (users != null) {
|
||||||
|
for (UserInfo user : users) {
|
||||||
|
mUsers.add(user.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
|
||||||
|
update(mUsers, mApps, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPermission(PackageInfo app, String permission) {
|
||||||
|
if (app.requestedPermissions != null) {
|
||||||
|
for (String p : app.requestedPermissions) {
|
||||||
|
if (permission.equals(p)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasNetworkPermission(PackageInfo app) {
|
||||||
|
return hasPermission(app, CHANGE_NETWORK_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasSystemPermission(PackageInfo app) {
|
||||||
|
int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
|
||||||
|
if ((flags & FLAG_SYSTEM) != 0 || (flags & FLAG_UPDATED_SYSTEM_APP) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return hasPermission(app, CONNECTIVITY_INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] toIntArray(List<Integer> list) {
|
||||||
|
int[] array = new int[list.size()];
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
array[i] = list.get(i);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(Set<Integer> users, Map<Integer, Boolean> apps, boolean add) {
|
||||||
|
List<Integer> network = new ArrayList<Integer>();
|
||||||
|
List<Integer> system = new ArrayList<Integer>();
|
||||||
|
for (Entry<Integer, Boolean> app : apps.entrySet()) {
|
||||||
|
List<Integer> list = app.getValue() ? system : network;
|
||||||
|
for (int user : users) {
|
||||||
|
list.add(UserHandle.getUid(user, app.getKey()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (add) {
|
||||||
|
mNetd.setPermission(CHANGE_NETWORK_STATE, toIntArray(network));
|
||||||
|
mNetd.setPermission(CONNECTIVITY_INTERNAL, toIntArray(system));
|
||||||
|
} else {
|
||||||
|
mNetd.clearPermission(toIntArray(network));
|
||||||
|
mNetd.clearPermission(toIntArray(system));
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
loge("Exception when updating permissions: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void onUserAdded(int user) {
|
||||||
|
if (user < 0) {
|
||||||
|
loge("Invalid user in onUserAdded: " + user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mUsers.add(user);
|
||||||
|
|
||||||
|
Set<Integer> users = new HashSet<Integer>();
|
||||||
|
users.add(user);
|
||||||
|
update(users, mApps, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void onUserRemoved(int user) {
|
||||||
|
if (user < 0) {
|
||||||
|
loge("Invalid user in onUserRemoved: " + user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mUsers.remove(user);
|
||||||
|
|
||||||
|
Set<Integer> users = new HashSet<Integer>();
|
||||||
|
users.add(user);
|
||||||
|
update(users, mApps, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void onAppAdded(String appName, int appUid) {
|
||||||
|
if (TextUtils.isEmpty(appName) || appUid < 0) {
|
||||||
|
loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PackageInfo app = mPackageManager.getPackageInfo(appName, GET_PERMISSIONS);
|
||||||
|
boolean isNetwork = hasNetworkPermission(app);
|
||||||
|
boolean isSystem = hasSystemPermission(app);
|
||||||
|
if (isNetwork || isSystem) {
|
||||||
|
Boolean permission = mApps.get(appUid);
|
||||||
|
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
|
||||||
|
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
|
||||||
|
if (permission == null || permission == NETWORK) {
|
||||||
|
mApps.put(appUid, isSystem);
|
||||||
|
|
||||||
|
Map<Integer, Boolean> apps = new HashMap<Integer, Boolean>();
|
||||||
|
apps.put(appUid, isSystem);
|
||||||
|
update(mUsers, apps, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
loge("NameNotFoundException in onAppAdded: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void onAppRemoved(int appUid) {
|
||||||
|
if (appUid < 0) {
|
||||||
|
loge("Invalid app in onAppRemoved: " + appUid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mApps.remove(appUid);
|
||||||
|
|
||||||
|
Map<Integer, Boolean> apps = new HashMap<Integer, Boolean>();
|
||||||
|
apps.put(appUid, NETWORK); // doesn't matter which permission we pick here
|
||||||
|
update(mUsers, apps, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void log(String s) {
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loge(String s) {
|
||||||
|
Log.e(TAG, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user