blob: 00859311af847bedb61e24161972155650693975 [file] [log] [blame]
/*
* 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.app.admin.ConnectEvent;
import android.app.admin.DnsEvent;
import android.app.admin.NetworkEvent;
import android.content.pm.PackageManagerInternal;
import android.net.IIpConnectivityMetrics;
import android.net.INetdEventCallback;
import android.os.Bundle;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
import com.android.server.ServiceThread;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 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 final AtomicBoolean mIsLoggingEnabled = new AtomicBoolean(false);
private IIpConnectivityMetrics mIpConnectivityMetrics;
private ServiceThread mHandlerThread;
private NetworkLoggingHandler mNetworkLoggingHandler;
private final INetdEventCallback mNetdEventCallback = new INetdEventCallback.Stub() {
@Override
public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
long timestamp, int uid) {
if (!mIsLoggingEnabled.get()) {
return;
}
DnsEvent dnsEvent = new DnsEvent(hostname, ipAddresses, ipAddressesCount,
mPm.getNameForUid(uid), timestamp);
sendNetworkEvent(dnsEvent);
}
@Override
public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
if (!mIsLoggingEnabled.get()) {
return;
}
ConnectEvent connectEvent = new ConnectEvent(ipAddr, port, mPm.getNameForUid(uid),
timestamp);
sendNetworkEvent(connectEvent);
}
private void sendNetworkEvent(NetworkEvent event) {
Message msg = mNetworkLoggingHandler.obtainMessage(
NetworkLoggingHandler.LOG_NETWORK_EVENT_MSG);
Bundle bundle = new Bundle();
bundle.putParcelable(NetworkLoggingHandler.NETWORK_EVENT_KEY, event);
msg.setData(bundle);
mNetworkLoggingHandler.sendMessage(msg);
}
};
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)) {
mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
/* allowIo */ false);
mHandlerThread.start();
mNetworkLoggingHandler = new NetworkLoggingHandler(mHandlerThread.getLooper(),
mDpm);
mNetworkLoggingHandler.scheduleBatchFinalization();
mIsLoggingEnabled.set(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 fail to unregister listener
mIsLoggingEnabled.set(false);
discardLogs();
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);
return true;
} finally {
if (mHandlerThread != null) {
mHandlerThread.quitSafely();
}
}
}
/**
* If logs are being collected, keep collecting them but stop notifying the device owner that
* new logs are available (since they cannot be retrieved)
*/
void pause() {
if (mNetworkLoggingHandler != null) {
mNetworkLoggingHandler.pause();
}
}
/**
* If logs are being collected, start notifying the device owner when logs are ready to be
* collected again (if it was paused).
* <p>If logging is enabled and there are logs ready to be retrieved, this method will attempt
* to notify the device owner. Therefore calling identity should be cleared before calling it
* (in case the method is called from a user other than the DO's user).
*/
void resume() {
if (mNetworkLoggingHandler != null) {
mNetworkLoggingHandler.resume();
}
}
/**
* Discard all collected logs.
*/
void discardLogs() {
if (mNetworkLoggingHandler != null) {
mNetworkLoggingHandler.discardLogs();
}
}
List<NetworkEvent> retrieveLogs(long batchToken) {
return mNetworkLoggingHandler.retrieveFullLogBatch(batchToken);
}
}