Home | History | Annotate | Download | only in watchlist
      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