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