DO NOT MERGE [DPM] DO can start network logging and listen for events

This CL adds:
1) Setter and getter in DPM to manipulate logging switch (retrieval
method to come in a subsequent CL(s)).
2) A way for DPM to register to listen for events.
3) Skeleton of NetworkLogger class (more to come in subsequent CL(s)).

Bug: 29748723
Change-Id: I5c04662ccc6febd2ba294b0eaca1ed1da9c16e47
This commit is contained in:
Michal Karpinski
2016-10-12 14:59:26 +01:00
parent c5700918aa
commit c3abd34cfe
10 changed files with 386 additions and 5 deletions

View File

@@ -202,6 +202,7 @@ LOCAL_SRC_FILES += \
core/java/android/net/IIpConnectivityMetrics.aidl \
core/java/android/net/IEthernetManager.aidl \
core/java/android/net/IEthernetServiceListener.aidl \
core/java/android/net/INetdEventCallback.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
core/java/android/net/INetworkPolicyListener.aidl \
core/java/android/net/INetworkPolicyManager.aidl \

View File

@@ -6609,4 +6609,46 @@ public class DevicePolicyManager {
throw re.rethrowFromSystemServer();
}
}
/**
* Called by a device owner to control the network logging feature. Logging can only be
* enabled on single user devices where the sole user is managed by the device owner. If a new
* user is added on the device, logging is disabled.
*
* <p> Network logs contain DNS lookup and connect() library call events.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled whether network logging should be enabled or not.
* @throws {@link SecurityException} if {@code admin} is not a device owner.
* @throws {@link RemoteException} if network logging could not be enabled or disabled due to
* the logging service not being available
*
* @hide
*/
public void setNetworkLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
throwIfParentInstance("setNetworkLoggingEnabled");
try {
mService.setNetworkLoggingEnabled(admin, enabled);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
/**
* Return whether network logging is enabled by a device owner.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return {@code true} if network logging is enabled by device owner, {@code false} otherwise.
* @throws {@link SecurityException} if {@code admin} is not a device owner.
*
* @hide
*/
public boolean isNetworkLoggingEnabled(@NonNull ComponentName admin) {
throwIfParentInstance("isNetworkLoggingEnabled");
try {
return mService.isNetworkLoggingEnabled(admin);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
}

View File

@@ -311,4 +311,7 @@ interface IDevicePolicyManager {
void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
boolean isBackupServiceEnabled(in ComponentName admin);
void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled);
boolean isNetworkLoggingEnabled(in ComponentName admin);
}

View File

@@ -18,6 +18,7 @@ package android.net;
import android.os.Parcelable;
import android.net.ConnectivityMetricsEvent;
import android.net.INetdEventCallback;
/** {@hide} */
interface IIpConnectivityMetrics {
@@ -27,4 +28,13 @@ interface IIpConnectivityMetrics {
* or -1 if the event was dropped due to rate limiting.
*/
int logEvent(in ConnectivityMetricsEvent event);
/**
* At most one callback can be registered (by DevicePolicyManager).
* @return status {@code true} if registering/unregistering of the callback was successful,
* {@code false} otherwise (might happen if IIpConnectivityMetrics is not available,
* if it happens make sure you call it when the service is up in the caller)
*/
boolean registerNetdEventCallback(in INetdEventCallback callback);
boolean unregisterNetdEventCallback();
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2016 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 android.net;
/** {@hide} */
oneway interface INetdEventCallback {
/**
* Reports a single DNS lookup function call.
* This method must not block or perform long-running operations.
*
* @param hostname the name that was looked up.
* @param ipAddresses (possibly a subset of) the IP addresses returned.
* At most {@link #DNS_REPORTED_IP_ADDRESSES_LIMIT} addresses are logged.
* @param ipAddressesCount the number of IP addresses returned. May be different from the length
* of ipAddresses if there were too many addresses to log.
* @param timestamp the timestamp at which the query was reported by netd.
* @param uid the UID of the application that performed the query.
*/
void onDnsEvent(String hostname, in String[] ipAddresses, int ipAddressesCount, long timestamp,
int uid);
/**
* Reports a single connect library call.
* This method must not block or perform long-running operations.
*
* @param ipAddr destination IP address.
* @param port destination port number.
* @param timestamp the timestamp at which the call was reported by netd.
* @param uid the UID of the application that performed the connection.
*/
void onConnectEvent(String ipAddr, int port, long timestamp, int uid);
}

View File

@@ -19,10 +19,13 @@ package com.android.server.connectivity;
import android.content.Context;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
import android.net.INetdEventCallback;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.IpConnectivityLog;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.Process;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -260,6 +263,33 @@ final public class IpConnectivityMetrics extends SystemService {
private void enforcePermission(String what) {
getContext().enforceCallingOrSelfPermission(what, "IpConnectivityMetrics");
}
private void enforceNetdEventListeningPermission() {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
throw new SecurityException(String.format("Uid %d has no permission to listen for"
+ " netd events.", uid));
}
}
@Override
public boolean registerNetdEventCallback(INetdEventCallback callback) {
enforceNetdEventListeningPermission();
if (mNetdListener == null) {
return false;
}
return mNetdListener.registerNetdEventCallback(callback);
}
@Override
public boolean unregisterNetdEventCallback() {
enforceNetdEventListeningPermission();
if (mNetdListener == null) {
// if the service is null, we aren't registered anyway
return true;
}
return mNetdListener.unregisterNetdEventCallback();
}
};
private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> {

View File

@@ -20,10 +20,12 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.INetdEventCallback;
import android.net.NetworkRequest;
import android.net.metrics.DnsEvent;
import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -119,6 +121,21 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
}
};
// Callback should only be registered/unregistered when logging is being enabled/disabled in DPM
// by the device owner. It's DevicePolicyManager's responsibility to ensure that.
@GuardedBy("this")
private INetdEventCallback mNetdEventCallback;
public synchronized boolean registerNetdEventCallback(INetdEventCallback callback) {
mNetdEventCallback = callback;
return true;
}
public synchronized boolean unregisterNetdEventCallback() {
mNetdEventCallback = null;
return true;
}
public NetdEventListenerService(Context context) {
this(context.getSystemService(ConnectivityManager.class), new IpConnectivityLog());
}
@@ -136,7 +153,8 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
// Called concurrently by multiple binder threads.
// This method must not block or perform long-running operations.
public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
String hostname, String[] ipAddresses, int ipAddressesCount, int uid) {
String hostname, String[] ipAddresses, int ipAddressesCount, int uid)
throws RemoteException {
maybeVerboseLog(String.format("onDnsEvent(%d, %d, %d, %d)",
netId, eventType, returnCode, latencyMs));
@@ -146,14 +164,23 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
mEventBatches.put(netId, batch);
}
batch.addResult((byte) eventType, (byte) returnCode, latencyMs);
if (mNetdEventCallback != null) {
mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount,
System.currentTimeMillis(), uid);
}
}
@Override
// Called concurrently by multiple binder threads.
// This method must not block or perform long-running operations.
public synchronized void onConnectEvent(int netId, int latencyMs, String ipAddr, int port,
int uid) {
int uid) throws RemoteException {
maybeVerboseLog(String.format("onConnectEvent(%d, %d)", netId, latencyMs));
if (mNetdEventCallback != null) {
mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid);
}
}
public synchronized void dump(PrintWriter writer) {

View File

@@ -77,8 +77,10 @@ import android.graphics.Color;
import android.media.AudioManager;
import android.media.IAudioService;
import android.net.ConnectivityManager;
import android.net.IIpConnectivityMetrics;
import android.net.ProxyInfo;
import android.net.Uri;
import android.net.metrics.IpConnectivityLog;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
@@ -352,6 +354,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
boolean mIsWatch;
private final SecurityLogMonitor mSecurityLogMonitor;
private NetworkLogger mNetworkLogger;
private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
@@ -478,6 +481,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
getSendingUserId());
/*
* Network logging would ideally be started in setDeviceOwnerSystemPropertyLocked(),
* however it's too early in the boot process to register with IIpConnectivityMetrics
* to listen for events.
*/
if (Intent.ACTION_USER_STARTED.equals(action)
&& userHandle == mOwners.getDeviceOwnerUserId()
&& isNetworkLoggingEnabledInternal()) {
setNetworkLoggingActiveInternal(true);
}
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
&& userHandle == mOwners.getDeviceOwnerUserId()
&& getDeviceOwnerRemoteBugreportUri() != null) {
@@ -549,6 +562,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
private static final String TAG_REQUIRE_AUTO_TIME = "require_auto_time";
private static final String TAG_FORCE_EPHEMERAL_USERS = "force_ephemeral_users";
private static final String TAG_IS_NETWORK_LOGGING_ENABLED = "is_network_logging_enabled";
private static final String TAG_ACCOUNT_TYPE = "account-type";
private static final String TAG_PERMITTED_ACCESSIBILITY_SERVICES
= "permitted-accessiblity-services";
@@ -643,6 +657,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
boolean requireAutoTime = false; // Can only be set by a device owner.
boolean forceEphemeralUsers = false; // Can only be set by a device owner.
boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner.
ActiveAdmin parentAdmin;
final boolean isParent;
@@ -851,6 +866,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.attribute(null, ATTR_VALUE, Boolean.toString(forceEphemeralUsers));
out.endTag(null, TAG_FORCE_EPHEMERAL_USERS);
}
if (isNetworkLoggingEnabled) {
out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
out.attribute(null, ATTR_VALUE, Boolean.toString(isNetworkLoggingEnabled));
out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
}
if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
@@ -1037,6 +1057,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else if (TAG_FORCE_EPHEMERAL_USERS.equals(tag)) {
forceEphemeralUsers = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_IS_NETWORK_LOGGING_ENABLED.equals(tag)) {
isNetworkLoggingEnabled = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
disabledKeyguardFeatures = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
@@ -1277,6 +1300,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
pw.println(requireAutoTime);
pw.print(prefix); pw.print("forceEphemeralUsers=");
pw.println(forceEphemeralUsers);
pw.print(prefix); pw.print("isNetworkLoggingEnabled=");
pw.println(isNetworkLoggingEnabled);
pw.print(prefix); pw.print("disabledKeyguardFeatures=");
pw.println(disabledKeyguardFeatures);
pw.print(prefix); pw.print("crossProfileWidgetProviders=");
@@ -1403,6 +1428,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return mContext.getSystemService(NotificationManager.class);
}
IIpConnectivityMetrics getIIpConnectivityMetrics() {
return (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
}
PowerManagerInternal getPowerManagerInternal() {
return LocalServices.getService(PowerManagerInternal.class);
}
@@ -9040,6 +9070,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!isDeviceOwnerManagedSingleUserDevice()) {
mInjector.securityLogSetLoggingEnabledProperty(false);
Slog.w(LOG_TAG, "Security logging turned off as it's no longer a single user device.");
getDeviceOwnerAdminLocked().isNetworkLoggingEnabled = false;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
setNetworkLoggingActiveInternal(false);
Slog.w(LOG_TAG, "Network logging turned off as it's no longer a single user"
+ " device.");
if (mOwners.hasDeviceOwner()) {
setBackupServiceEnabledInternal(false);
Slog.w(LOG_TAG, "Backup is off as it's a managed device that has more that one user.");
@@ -9386,4 +9423,62 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
@Override
public synchronized void setNetworkLoggingEnabled(ComponentName admin, boolean enabled) {
if (!mHasFeature) {
return;
}
Preconditions.checkNotNull(admin);
ensureDeviceOwnerManagingSingleUser(admin);
if (enabled == isNetworkLoggingEnabledInternal()) {
// already in the requested state
return;
}
getDeviceOwnerAdminLocked().isNetworkLoggingEnabled = enabled;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
setNetworkLoggingActiveInternal(enabled);
}
private synchronized void setNetworkLoggingActiveInternal(boolean active) {
final long callingIdentity = mInjector.binderClearCallingIdentity();
try {
if (active) {
mNetworkLogger = new NetworkLogger(this, mInjector.getPackageManagerInternal());
if (!mNetworkLogger.startNetworkLogging()) {
mNetworkLogger = null;
Slog.wtf(LOG_TAG, "Network logging could not be started due to the logging"
+ " service not being available yet.");
}
} else {
if (mNetworkLogger != null && !mNetworkLogger.stopNetworkLogging()) {
mNetworkLogger = null;
Slog.wtf(LOG_TAG, "Network logging could not be stopped due to the logging"
+ " service not being available yet.");
}
mNetworkLogger = null;
}
} finally {
mInjector.binderRestoreCallingIdentity(callingIdentity);
}
}
@Override
public boolean isNetworkLoggingEnabled(ComponentName admin) {
if (!mHasFeature) {
return false;
}
Preconditions.checkNotNull(admin);
synchronized (this) {
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
return isNetworkLoggingEnabledInternal();
}
}
private synchronized boolean isNetworkLoggingEnabledInternal() {
ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
return (deviceOwner != null) && deviceOwner.isNetworkLoggingEnabled;
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2016 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.devicepolicy;
import android.content.pm.PackageManagerInternal;
import android.net.IIpConnectivityMetrics;
import android.net.INetdEventCallback;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
import com.android.server.ServiceThread;
import java.util.ArrayList;
import java.util.List;
/**
* A class for managing network logging.
* This class is not thread-safe, callers should synchronize access.
*/
final class NetworkLogger {
private static final String TAG = NetworkLogger.class.getSimpleName();
private final DevicePolicyManagerService mDpm;
private final PackageManagerInternal mPm;
private IIpConnectivityMetrics mIpConnectivityMetrics;
private boolean mIsLoggingEnabled;
private final INetdEventCallback mNetdEventCallback = new INetdEventCallback.Stub() {
@Override
public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
long timestamp, int uid) {
if (!mIsLoggingEnabled) {
return;
}
// TODO(mkarpinski): send msg with data to Handler
}
@Override
public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
if (!mIsLoggingEnabled) {
return;
}
// TODO(mkarpinski): send msg with data to Handler
}
};
NetworkLogger(DevicePolicyManagerService dpm, PackageManagerInternal pm) {
mDpm = dpm;
mPm = pm;
}
private boolean checkIpConnectivityMetricsService() {
if (mIpConnectivityMetrics != null) {
return true;
}
final IIpConnectivityMetrics service = mDpm.mInjector.getIIpConnectivityMetrics();
if (service == null) {
return false;
}
mIpConnectivityMetrics = service;
return true;
}
boolean startNetworkLogging() {
Log.d(TAG, "Starting network logging.");
if (!checkIpConnectivityMetricsService()) {
// the IIpConnectivityMetrics service should have been present at this point
Slog.wtf(TAG, "Failed to register callback with IIpConnectivityMetrics.");
return false;
}
try {
if (mIpConnectivityMetrics.registerNetdEventCallback(mNetdEventCallback)) {
// TODO(mkarpinski): start a new ServiceThread, instantiate a Handler etc.
mIsLoggingEnabled = true;
return true;
} else {
return false;
}
} catch (RemoteException re) {
Slog.wtf(TAG, "Failed to make remote calls to register the callback", re);
return false;
}
}
boolean stopNetworkLogging() {
Log.d(TAG, "Stopping network logging");
// stop the logging regardless of whether we failed to unregister listener
mIsLoggingEnabled = false;
try {
if (!checkIpConnectivityMetricsService()) {
// the IIpConnectivityMetrics service should have been present at this point
Slog.wtf(TAG, "Failed to unregister callback with IIpConnectivityMetrics.");
// logging is forcefully disabled even if unregistering fails
return true;
}
return mIpConnectivityMetrics.unregisterNetdEventCallback();
} catch (RemoteException re) {
Slog.wtf(TAG, "Failed to make remote calls to unregister the callback", re);
} finally {
// TODO(mkarpinski): quitSafely() the Handler
return true;
}
}
}

View File

@@ -22,6 +22,7 @@ import android.net.Network;
import android.net.metrics.DnsEvent;
import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
import android.os.RemoteException;
import junit.framework.TestCase;
import org.junit.Before;
@@ -157,9 +158,13 @@ public class NetdEventListenerServiceTest extends TestCase {
}
void log(int netId, int[] latencies) {
for (int l : latencies) {
mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null, 0,
0);
try {
for (int l : latencies) {
mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null,
0, 0);
}
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}