1 /*
2  * Copyright (C) 2016 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.devicepolicy;
18 
19 import android.app.admin.ConnectEvent;
20 import android.app.admin.DnsEvent;
21 import android.app.admin.NetworkEvent;
22 import android.content.pm.PackageManagerInternal;
23 import android.net.IIpConnectivityMetrics;
24 import android.net.INetdEventCallback;
25 import android.os.Bundle;
26 import android.os.Message;
27 import android.os.Process;
28 import android.os.RemoteException;
29 import android.util.Log;
30 import android.util.Slog;
31 
32 import com.android.server.ServiceThread;
33 import com.android.server.net.BaseNetdEventCallback;
34 
35 import java.util.List;
36 import java.util.concurrent.atomic.AtomicBoolean;
37 
38 /**
39  * A class for managing network logging.
40  * This class is not thread-safe, callers should synchronize access.
41  */
42 final class NetworkLogger {
43 
44     private static final String TAG = NetworkLogger.class.getSimpleName();
45 
46     private final DevicePolicyManagerService mDpm;
47     private final PackageManagerInternal mPm;
48     private final AtomicBoolean mIsLoggingEnabled = new AtomicBoolean(false);
49 
50     private IIpConnectivityMetrics mIpConnectivityMetrics;
51     private ServiceThread mHandlerThread;
52     private NetworkLoggingHandler mNetworkLoggingHandler;
53 
54     private final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
55         @Override
56         public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
57                 long timestamp, int uid) {
58             if (!mIsLoggingEnabled.get()) {
59                 return;
60             }
61             DnsEvent dnsEvent = new DnsEvent(hostname, ipAddresses, ipAddressesCount,
62                     mPm.getNameForUid(uid), timestamp);
63             sendNetworkEvent(dnsEvent);
64         }
65 
66         @Override
67         public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
68             if (!mIsLoggingEnabled.get()) {
69                 return;
70             }
71             ConnectEvent connectEvent = new ConnectEvent(ipAddr, port, mPm.getNameForUid(uid),
72                     timestamp);
73             sendNetworkEvent(connectEvent);
74         }
75 
76         private void sendNetworkEvent(NetworkEvent event) {
77             Message msg = mNetworkLoggingHandler.obtainMessage(
78                     NetworkLoggingHandler.LOG_NETWORK_EVENT_MSG);
79             Bundle bundle = new Bundle();
80             bundle.putParcelable(NetworkLoggingHandler.NETWORK_EVENT_KEY, event);
81             msg.setData(bundle);
82             mNetworkLoggingHandler.sendMessage(msg);
83         }
84     };
85 
NetworkLogger(DevicePolicyManagerService dpm, PackageManagerInternal pm)86     NetworkLogger(DevicePolicyManagerService dpm, PackageManagerInternal pm) {
87         mDpm = dpm;
88         mPm = pm;
89     }
90 
checkIpConnectivityMetricsService()91     private boolean checkIpConnectivityMetricsService() {
92         if (mIpConnectivityMetrics != null) {
93             return true;
94         }
95         final IIpConnectivityMetrics service = mDpm.mInjector.getIIpConnectivityMetrics();
96         if (service == null) {
97             return false;
98         }
99         mIpConnectivityMetrics = service;
100         return true;
101     }
102 
startNetworkLogging()103     boolean startNetworkLogging() {
104         Log.d(TAG, "Starting network logging.");
105         if (!checkIpConnectivityMetricsService()) {
106             // the IIpConnectivityMetrics service should have been present at this point
107             Slog.wtf(TAG, "Failed to register callback with IIpConnectivityMetrics.");
108             return false;
109         }
110         try {
111            if (mIpConnectivityMetrics.addNetdEventCallback(
112                    INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY, mNetdEventCallback)) {
113                 mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
114                         /* allowIo */ false);
115                 mHandlerThread.start();
116                 mNetworkLoggingHandler = new NetworkLoggingHandler(mHandlerThread.getLooper(),
117                         mDpm);
118                 mNetworkLoggingHandler.scheduleBatchFinalization();
119                 mIsLoggingEnabled.set(true);
120                 return true;
121             } else {
122                 return false;
123             }
124         } catch (RemoteException re) {
125             Slog.wtf(TAG, "Failed to make remote calls to register the callback", re);
126             return false;
127         }
128     }
129 
stopNetworkLogging()130     boolean stopNetworkLogging() {
131         Log.d(TAG, "Stopping network logging");
132         // stop the logging regardless of whether we fail to unregister listener
133         mIsLoggingEnabled.set(false);
134         discardLogs();
135 
136         try {
137             if (!checkIpConnectivityMetricsService()) {
138                 // the IIpConnectivityMetrics service should have been present at this point
139                 Slog.wtf(TAG, "Failed to unregister callback with IIpConnectivityMetrics.");
140                 // logging is forcefully disabled even if unregistering fails
141                 return true;
142             }
143             return mIpConnectivityMetrics.removeNetdEventCallback(
144                     INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY);
145         } catch (RemoteException re) {
146             Slog.wtf(TAG, "Failed to make remote calls to unregister the callback", re);
147             return true;
148         } finally {
149             if (mHandlerThread != null) {
150                 mHandlerThread.quitSafely();
151             }
152         }
153     }
154 
155     /**
156      * If logs are being collected, keep collecting them but stop notifying the device owner that
157      * new logs are available (since they cannot be retrieved)
158      */
pause()159     void pause() {
160         if (mNetworkLoggingHandler != null) {
161             mNetworkLoggingHandler.pause();
162         }
163     }
164 
165     /**
166      * If logs are being collected, start notifying the device owner when logs are ready to be
167      * collected again (if it was paused).
168      * <p>If logging is enabled and there are logs ready to be retrieved, this method will attempt
169      * to notify the device owner. Therefore calling identity should be cleared before calling it
170      * (in case the method is called from a user other than the DO's user).
171      */
resume()172     void resume() {
173         if (mNetworkLoggingHandler != null) {
174             mNetworkLoggingHandler.resume();
175         }
176     }
177 
178     /**
179      * Discard all collected logs.
180      */
discardLogs()181     void discardLogs() {
182         if (mNetworkLoggingHandler != null) {
183             mNetworkLoggingHandler.discardLogs();
184         }
185     }
186 
retrieveLogs(long batchToken)187     List<NetworkEvent> retrieveLogs(long batchToken) {
188         return mNetworkLoggingHandler.retrieveFullLogBatch(batchToken);
189     }
190 }
191