blob: f35e6ec92dae8d4d95432429222eed3cf72a13cf [file] [log] [blame]
Ricky Wai1a6e6672017-10-27 14:46:01 +01001/*
2 * Copyright (C) 2017 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
17package com.android.server.net.watchlist;
18
19import android.content.Context;
20import android.content.pm.PackageManager;
21import android.content.pm.PackageManager.NameNotFoundException;
22import android.net.IIpConnectivityMetrics;
23import android.net.INetdEventCallback;
24import android.net.NetworkWatchlistManager;
25import android.net.metrics.IpConnectivityLog;
26import android.os.Binder;
27import android.os.Process;
28import android.os.SharedMemory;
29import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.os.SystemProperties;
32import android.text.TextUtils;
33import android.util.Slog;
34
35import com.android.internal.R;
Ricky Waiebe36242017-10-27 14:36:43 +010036import com.android.internal.annotations.GuardedBy;
Ricky Wai1a6e6672017-10-27 14:46:01 +010037import com.android.internal.annotations.VisibleForTesting;
38import com.android.internal.util.DumpUtils;
39import com.android.internal.net.INetworkWatchlistManager;
40import com.android.server.ServiceThread;
41import com.android.server.SystemService;
42
43import java.io.FileDescriptor;
44import java.io.IOException;
45import java.io.PrintWriter;
46import java.util.List;
47
48/**
49 * Implementation of network watchlist service.
50 */
51public class NetworkWatchlistService extends INetworkWatchlistManager.Stub {
52
53 private static final String TAG = NetworkWatchlistService.class.getSimpleName();
54 static final boolean DEBUG = false;
55
56 private static final String PROPERTY_NETWORK_WATCHLIST_ENABLED =
57 "ro.network_watchlist_enabled";
58
59 private static final int MAX_NUM_OF_WATCHLIST_DIGESTS = 10000;
60
61 public static class Lifecycle extends SystemService {
62 private NetworkWatchlistService mService;
63
64 public Lifecycle(Context context) {
65 super(context);
66 }
67
68 @Override
69 public void onStart() {
70 if (!SystemProperties.getBoolean(PROPERTY_NETWORK_WATCHLIST_ENABLED, false)) {
71 // Watchlist service is disabled
72 return;
73 }
74 mService = new NetworkWatchlistService(getContext());
75 publishBinderService(Context.NETWORK_WATCHLIST_SERVICE, mService);
76 }
77
78 @Override
79 public void onBootPhase(int phase) {
80 if (!SystemProperties.getBoolean(PROPERTY_NETWORK_WATCHLIST_ENABLED, false)) {
81 // Watchlist service is disabled
82 return;
83 }
84 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
85 try {
86 mService.initIpConnectivityMetrics();
87 mService.startWatchlistLogging();
88 } catch (RemoteException e) {
89 // Should not happen
90 }
91 ReportWatchlistJobService.schedule(getContext());
92 }
93 }
94 }
95
Ricky Waiebe36242017-10-27 14:36:43 +010096 @GuardedBy("mLoggingSwitchLock")
Ricky Wai1a6e6672017-10-27 14:46:01 +010097 private volatile boolean mIsLoggingEnabled = false;
98 private final Object mLoggingSwitchLock = new Object();
99
100 private final WatchlistSettings mSettings;
101 private final Context mContext;
102
103 // Separate thread to handle expensive watchlist logging work.
104 private final ServiceThread mHandlerThread;
105
106 @VisibleForTesting
107 IIpConnectivityMetrics mIpConnectivityMetrics;
108 @VisibleForTesting
109 WatchlistLoggingHandler mNetworkWatchlistHandler;
110
111 public NetworkWatchlistService(Context context) {
112 mContext = context;
113 mSettings = WatchlistSettings.getInstance();
114 mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
115 /* allowIo */ false);
116 mHandlerThread.start();
117 mNetworkWatchlistHandler = new WatchlistLoggingHandler(mContext,
118 mHandlerThread.getLooper());
119 mNetworkWatchlistHandler.reportWatchlistIfNecessary();
120 }
121
122 // For testing only
123 @VisibleForTesting
124 NetworkWatchlistService(Context context, ServiceThread handlerThread,
125 WatchlistLoggingHandler handler, IIpConnectivityMetrics ipConnectivityMetrics) {
126 mContext = context;
127 mSettings = WatchlistSettings.getInstance();
128 mHandlerThread = handlerThread;
129 mNetworkWatchlistHandler = handler;
130 mIpConnectivityMetrics = ipConnectivityMetrics;
131 }
132
133 private void initIpConnectivityMetrics() {
134 mIpConnectivityMetrics = (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
135 ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
136 }
137
138 private final INetdEventCallback mNetdEventCallback = new INetdEventCallback.Stub() {
139 @Override
140 public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
141 long timestamp, int uid) {
142 if (!mIsLoggingEnabled) {
143 return;
144 }
145 mNetworkWatchlistHandler.asyncNetworkEvent(hostname, ipAddresses, uid);
146 }
147
148 @Override
149 public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
150 if (!mIsLoggingEnabled) {
151 return;
152 }
153 mNetworkWatchlistHandler.asyncNetworkEvent(null, new String[]{ipAddr}, uid);
154 }
155 };
156
157 @VisibleForTesting
158 protected boolean startWatchlistLoggingImpl() throws RemoteException {
159 if (DEBUG) {
160 Slog.i(TAG, "Starting watchlist logging.");
161 }
162 synchronized (mLoggingSwitchLock) {
163 if (mIsLoggingEnabled) {
164 Slog.w(TAG, "Watchlist logging is already running");
165 return true;
166 }
167 try {
168 if (mIpConnectivityMetrics.addNetdEventCallback(
169 INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST, mNetdEventCallback)) {
170 mIsLoggingEnabled = true;
171 return true;
172 } else {
173 return false;
174 }
175 } catch (RemoteException re) {
176 // Should not happen
177 return false;
178 }
179 }
180 }
181
182 @Override
183 public boolean startWatchlistLogging() throws RemoteException {
184 enforceWatchlistLoggingPermission();
185 return startWatchlistLoggingImpl();
186 }
187
188 @VisibleForTesting
189 protected boolean stopWatchlistLoggingImpl() {
190 if (DEBUG) {
191 Slog.i(TAG, "Stopping watchlist logging");
192 }
193 synchronized (mLoggingSwitchLock) {
194 if (!mIsLoggingEnabled) {
195 Slog.w(TAG, "Watchlist logging is not running");
196 return true;
197 }
198 // stop the logging regardless of whether we fail to unregister listener
199 mIsLoggingEnabled = false;
200
201 try {
202 return mIpConnectivityMetrics.removeNetdEventCallback(
203 INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST);
204 } catch (RemoteException re) {
205 // Should not happen
206 return false;
207 }
208 }
209 }
210
211 @Override
212 public boolean stopWatchlistLogging() throws RemoteException {
213 enforceWatchlistLoggingPermission();
214 return stopWatchlistLoggingImpl();
215 }
216
217 private void enforceWatchlistLoggingPermission() {
218 final int uid = Binder.getCallingUid();
219 if (uid != Process.SYSTEM_UID) {
220 throw new SecurityException(String.format("Uid %d has no permission to change watchlist"
221 + " setting.", uid));
222 }
223 }
224
Ricky Waiebe36242017-10-27 14:36:43 +0100225 @Override
226 public void reloadWatchlist() throws RemoteException {
227 enforceWatchlistLoggingPermission();
228 Slog.i(TAG, "Reloading watchlist");
229 mSettings.reloadSettings();
Ricky Wai1a6e6672017-10-27 14:46:01 +0100230 }
231
232 @Override
233 public void reportWatchlistIfNecessary() {
234 // Allow any apps to trigger report event, as we won't run it if it's too early.
235 mNetworkWatchlistHandler.reportWatchlistIfNecessary();
236 }
237
238 @Override
239 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
240 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
241 mSettings.dump(fd, pw, args);
242 }
243
244}