1 /* 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 17 package com.android.server.net.watchlist; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.net.IIpConnectivityMetrics; 22 import android.net.INetdEventCallback; 23 import android.net.metrics.IpConnectivityLog; 24 import android.os.Binder; 25 import android.os.Process; 26 import android.os.ResultReceiver; 27 import android.os.RemoteException; 28 import android.os.ServiceManager; 29 import android.os.ShellCallback; 30 import android.os.SystemProperties; 31 import android.provider.Settings; 32 import android.text.TextUtils; 33 import android.util.Slog; 34 35 import com.android.internal.R; 36 import com.android.internal.annotations.GuardedBy; 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.internal.util.DumpUtils; 39 import com.android.internal.net.INetworkWatchlistManager; 40 import com.android.server.ServiceThread; 41 import com.android.server.SystemService; 42 import com.android.server.net.BaseNetdEventCallback; 43 44 import java.io.FileDescriptor; 45 import java.io.PrintWriter; 46 47 /** 48 * Implementation of network watchlist service. 49 */ 50 public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { 51 52 private static final String TAG = NetworkWatchlistService.class.getSimpleName(); 53 static final boolean DEBUG = false; 54 55 private static final int MAX_NUM_OF_WATCHLIST_DIGESTS = 10000; 56 57 public static class Lifecycle extends SystemService { 58 private NetworkWatchlistService mService; 59 60 public Lifecycle(Context context) { 61 super(context); 62 } 63 64 @Override 65 public void onStart() { 66 if (Settings.Global.getInt(getContext().getContentResolver(), 67 Settings.Global.NETWORK_WATCHLIST_ENABLED, 1) == 0) { 68 // Watchlist service is disabled 69 Slog.i(TAG, "Network Watchlist service is disabled"); 70 return; 71 } 72 mService = new NetworkWatchlistService(getContext()); 73 publishBinderService(Context.NETWORK_WATCHLIST_SERVICE, mService); 74 } 75 76 @Override 77 public void onBootPhase(int phase) { 78 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { 79 if (Settings.Global.getInt(getContext().getContentResolver(), 80 Settings.Global.NETWORK_WATCHLIST_ENABLED, 1) == 0) { 81 // Watchlist service is disabled 82 Slog.i(TAG, "Network Watchlist service is disabled"); 83 return; 84 } 85 try { 86 mService.init(); 87 mService.initIpConnectivityMetrics(); 88 mService.startWatchlistLogging(); 89 } catch (RemoteException e) { 90 // Should not happen 91 } 92 ReportWatchlistJobService.schedule(getContext()); 93 } 94 } 95 } 96 97 @GuardedBy("mLoggingSwitchLock") 98 private volatile boolean mIsLoggingEnabled = false; 99 private final Object mLoggingSwitchLock = new Object(); 100 101 private final WatchlistConfig mConfig; 102 private final Context mContext; 103 104 // Separate thread to handle expensive watchlist logging work. 105 private final ServiceThread mHandlerThread; 106 107 @VisibleForTesting 108 IIpConnectivityMetrics mIpConnectivityMetrics; 109 @VisibleForTesting 110 WatchlistLoggingHandler mNetworkWatchlistHandler; 111 112 public NetworkWatchlistService(Context context) { 113 mContext = context; 114 mConfig = WatchlistConfig.getInstance(); 115 mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, 116 /* allowIo */ false); 117 mHandlerThread.start(); 118 mNetworkWatchlistHandler = new WatchlistLoggingHandler(mContext, 119 mHandlerThread.getLooper()); 120 mNetworkWatchlistHandler.reportWatchlistIfNecessary(); 121 } 122 123 // For testing only 124 @VisibleForTesting 125 NetworkWatchlistService(Context context, ServiceThread handlerThread, 126 WatchlistLoggingHandler handler, IIpConnectivityMetrics ipConnectivityMetrics) { 127 mContext = context; 128 mConfig = WatchlistConfig.getInstance(); 129 mHandlerThread = handlerThread; 130 mNetworkWatchlistHandler = handler; 131 mIpConnectivityMetrics = ipConnectivityMetrics; 132 } 133 134 private void init() { 135 mConfig.removeTestModeConfig(); 136 } 137 138 private void initIpConnectivityMetrics() { 139 mIpConnectivityMetrics = (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface( 140 ServiceManager.getService(IpConnectivityLog.SERVICE_NAME)); 141 } 142 143 private final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() { 144 @Override 145 public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount, 146 long timestamp, int uid) { 147 if (!mIsLoggingEnabled) { 148 return; 149 } 150 mNetworkWatchlistHandler.asyncNetworkEvent(hostname, ipAddresses, uid); 151 } 152 153 @Override 154 public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) { 155 if (!mIsLoggingEnabled) { 156 return; 157 } 158 mNetworkWatchlistHandler.asyncNetworkEvent(null, new String[]{ipAddr}, uid); 159 } 160 }; 161 162 private boolean isCallerShell() { 163 final int callingUid = Binder.getCallingUid(); 164 return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; 165 } 166 167 @Override 168 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 169 String[] args, ShellCallback callback, ResultReceiver resultReceiver) { 170 if (!isCallerShell()) { 171 Slog.w(TAG, "Only shell is allowed to call network watchlist shell commands"); 172 return; 173 } 174 (new NetworkWatchlistShellCommand(this, mContext)).exec(this, in, out, err, args, callback, 175 resultReceiver); 176 } 177 178 @VisibleForTesting 179 protected boolean startWatchlistLoggingImpl() throws RemoteException { 180 if (DEBUG) { 181 Slog.i(TAG, "Starting watchlist logging."); 182 } 183 synchronized (mLoggingSwitchLock) { 184 if (mIsLoggingEnabled) { 185 Slog.w(TAG, "Watchlist logging is already running"); 186 return true; 187 } 188 try { 189 if (mIpConnectivityMetrics.addNetdEventCallback( 190 INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST, mNetdEventCallback)) { 191 mIsLoggingEnabled = true; 192 return true; 193 } else { 194 return false; 195 } 196 } catch (RemoteException re) { 197 // Should not happen 198 return false; 199 } 200 } 201 } 202 203 @Override 204 public boolean startWatchlistLogging() throws RemoteException { 205 enforceWatchlistLoggingPermission(); 206 return startWatchlistLoggingImpl(); 207 } 208 209 @VisibleForTesting 210 protected boolean stopWatchlistLoggingImpl() { 211 if (DEBUG) { 212 Slog.i(TAG, "Stopping watchlist logging"); 213 } 214 synchronized (mLoggingSwitchLock) { 215 if (!mIsLoggingEnabled) { 216 Slog.w(TAG, "Watchlist logging is not running"); 217 return true; 218 } 219 // stop the logging regardless of whether we fail to unregister listener 220 mIsLoggingEnabled = false; 221 222 try { 223 return mIpConnectivityMetrics.removeNetdEventCallback( 224 INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST); 225 } catch (RemoteException re) { 226 // Should not happen 227 return false; 228 } 229 } 230 } 231 232 @Override 233 public boolean stopWatchlistLogging() throws RemoteException { 234 enforceWatchlistLoggingPermission(); 235 return stopWatchlistLoggingImpl(); 236 } 237 238 @Nullable 239 @Override 240 public byte[] getWatchlistConfigHash() { 241 return mConfig.getWatchlistConfigHash(); 242 } 243 244 private void enforceWatchlistLoggingPermission() { 245 final int uid = Binder.getCallingUid(); 246 if (uid != Process.SYSTEM_UID) { 247 throw new SecurityException(String.format("Uid %d has no permission to change watchlist" 248 + " setting.", uid)); 249 } 250 } 251 252 @Override 253 public void reloadWatchlist() throws RemoteException { 254 enforceWatchlistLoggingPermission(); 255 Slog.i(TAG, "Reloading watchlist"); 256 mConfig.reloadConfig(); 257 } 258 259 @Override 260 public void reportWatchlistIfNecessary() { 261 // Allow any apps to trigger report event, as we won't run it if it's too early. 262 mNetworkWatchlistHandler.reportWatchlistIfNecessary(); 263 } 264 265 /** 266 * Force generate watchlist report for testing. 267 * 268 * @param lastReportTime Watchlist report will cotain all records before this time. 269 * @return True if operation success. 270 */ 271 public boolean forceReportWatchlistForTest(long lastReportTime) { 272 if (mConfig.isConfigSecure()) { 273 // Should not force generate report under production config. 274 return false; 275 } 276 mNetworkWatchlistHandler.forceReportWatchlistForTest(lastReportTime); 277 return true; 278 } 279 280 @Override 281 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 282 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 283 mConfig.dump(fd, pw, args); 284 } 285 286 } 287