Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3603722f authored by Ricky Wai's avatar Ricky Wai Committed by Android (Google) Code Review
Browse files

Merge "Add Network Watchlist adb test commands"

parents 27e35bf6 f9890f4e
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -23,8 +23,10 @@ import android.net.INetdEventCallback;
import android.net.metrics.IpConnectivityLog;
import android.os.Binder;
import android.os.Process;
import android.os.ResultReceiver;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemProperties;
import android.provider.Settings;
import android.text.TextUtils;
@@ -80,6 +82,7 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub {
                    return;
                }
                try {
                    mService.init();
                    mService.initIpConnectivityMetrics();
                    mService.startWatchlistLogging();
                } catch (RemoteException e) {
@@ -127,6 +130,10 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub {
        mIpConnectivityMetrics = ipConnectivityMetrics;
    }

    private void init() {
        mConfig.removeTestModeConfig();
    }

    private void initIpConnectivityMetrics() {
        mIpConnectivityMetrics = (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
                ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
@@ -151,6 +158,22 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub {
        }
    };

    private boolean isCallerShell() {
        final int callingUid = Binder.getCallingUid();
        return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
    }

    @Override
    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
        if (!isCallerShell()) {
            Slog.w(TAG, "Only shell is allowed to call network watchlist shell commands");
            return;
        }
        (new NetworkWatchlistShellCommand(mContext)).exec(this, in, out, err, args, callback,
                resultReceiver);
    }

    @VisibleForTesting
    protected boolean startWatchlistLoggingImpl() throws RemoteException {
        if (DEBUG) {
+94 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.net.watchlist;

import android.content.Context;
import android.content.Intent;
import android.net.NetworkWatchlistManager;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ShellCommand;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

/**
 * Network watchlist shell commands class, to provide a way to set temporary watchlist config for
 * testing in shell, so CTS / GTS can use it to verify if watchlist feature is working properly.
 */
class NetworkWatchlistShellCommand extends ShellCommand {

    final NetworkWatchlistManager mNetworkWatchlistManager;

    NetworkWatchlistShellCommand(Context context) {
        mNetworkWatchlistManager = new NetworkWatchlistManager(context);
    }

    @Override
    public int onCommand(String cmd) {
        if (cmd == null) {
            return handleDefaultCommands(cmd);
        }

        final PrintWriter pw = getOutPrintWriter();
        try {
            switch(cmd) {
                case "set-test-config":
                    return runSetTestConfig();
                default:
                    return handleDefaultCommands(cmd);
            }
        } catch (RemoteException e) {
            pw.println("Remote exception: " + e);
        }
        return -1;
    }

    /**
     * Method to get fd from input xml path, and set it as temporary watchlist config.
     */
    private int runSetTestConfig() throws RemoteException {
        final PrintWriter pw = getOutPrintWriter();
        try {
            final String configXmlPath = getNextArgRequired();
            final ParcelFileDescriptor pfd = openFileForSystem(configXmlPath, "r");
            if (pfd != null) {
                final InputStream fileStream = new FileInputStream(pfd.getFileDescriptor());
                WatchlistConfig.getInstance().setTestMode(fileStream);
            }
            pw.println("Success!");
        } catch (RuntimeException | IOException ex) {
            pw.println("Error: " + ex.toString());
            return -1;
        }
        return 0;
    }

    @Override
    public void onHelp() {
        final PrintWriter pw = getOutPrintWriter();
        pw.println("Network watchlist manager commands:");
        pw.println("  help");
        pw.println("    Print this help text.");
        pw.println("");
        pw.println("  set-test-config your_watchlist_config.xml");
        pw.println();
        Intent.printIntentArgsHelp(pw , "");
    }
}
+36 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.net.watchlist;

import android.os.FileUtils;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -32,6 +33,7 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@@ -50,6 +52,8 @@ class WatchlistConfig {
    // Watchlist config that pushed by ConfigUpdater.
    private static final String NETWORK_WATCHLIST_DB_PATH =
            "/data/misc/network_watchlist/network_watchlist.xml";
    private static final String NETWORK_WATCHLIST_DB_FOR_TEST_PATH =
            "/data/misc/network_watchlist/network_watchlist_for_test.xml";

    // Hash for null / unknown config, a 32 byte array filled with content 0x00
    private static final byte[] UNKNOWN_CONFIG_HASH = new byte[32];
@@ -80,7 +84,7 @@ class WatchlistConfig {
    private boolean mIsSecureConfig = true;

    private final static WatchlistConfig sInstance = new WatchlistConfig();
    private final File mXmlFile;
    private File mXmlFile;

    private volatile CrcShaDigests mDomainDigests;
    private volatile CrcShaDigests mIpDigests;
@@ -232,7 +236,38 @@ class WatchlistConfig {
        return UNKNOWN_CONFIG_HASH;
    }

    /**
     * This method will copy temporary test config and temporary override network watchlist config
     * in memory. When device is rebooted, temporary test config will be removed, and system will
     * use back the original watchlist config.
     * Also, as temporary network watchlist config is not secure, we will mark it as insecure
     * config and will be applied to testOnly applications only.
     */
    public void setTestMode(InputStream testConfigInputStream) throws IOException {
        Log.i(TAG, "Setting watchlist testing config");
        // Copy test config
        FileUtils.copyToFileOrThrow(testConfigInputStream,
                new File(NETWORK_WATCHLIST_DB_FOR_TEST_PATH));
        // Mark config as insecure, so it will be applied to testOnly applications only
        mIsSecureConfig = false;
        // Reload watchlist config using test config file
        mXmlFile = new File(NETWORK_WATCHLIST_DB_FOR_TEST_PATH);
        reloadConfig();
    }

    public void removeTestModeConfig() {
        try {
            final File f = new File(NETWORK_WATCHLIST_DB_FOR_TEST_PATH);
            if (f.exists()) {
                f.delete();
            }
        } catch (Exception e) {
            Log.e(TAG, "Unable to delete test config");
        }
    }

    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println("Watchlist config hash: " + HexDump.toHexString(getWatchlistConfigHash()));
        pw.println("Domain CRC32 digest list:");
        if (mDomainDigests != null) {
            mDomainDigests.crc32Digests.dump(fd, pw, args);
+27 −3
Original line number Diff line number Diff line
@@ -117,6 +117,25 @@ class WatchlistLoggingHandler extends Handler {
        }
    }

    /**
     * Return if a given package has testOnly is true.
     */
    private boolean isPackageTestOnly(int uid) {
        final ApplicationInfo ai;
        try {
            final String[] packageNames = mPm.getPackagesForUid(uid);
            if (packageNames == null || packageNames.length == 0) {
                Slog.e(TAG, "Couldn't find package: " + packageNames);
                return false;
            }
            ai = mPm.getApplicationInfo(packageNames[0],0);
        } catch (NameNotFoundException e) {
            // Should not happen.
            return false;
        }
        return (ai.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0;
    }

     /**
     * Report network watchlist records if we collected enough data.
     */
@@ -146,16 +165,21 @@ class WatchlistLoggingHandler extends Handler {
        }
        final String cncDomain = searchAllSubDomainsInWatchlist(hostname);
        if (cncDomain != null) {
            insertRecord(getDigestFromUid(uid), cncDomain, timestamp);
            insertRecord(uid, cncDomain, timestamp);
        } else {
            final String cncIp = searchIpInWatchlist(ipAddresses);
            if (cncIp != null) {
                insertRecord(getDigestFromUid(uid), cncIp, timestamp);
                insertRecord(uid, cncIp, timestamp);
            }
        }
    }

    private boolean insertRecord(byte[] digest, String cncHost, long timestamp) {
    private boolean insertRecord(int uid, String cncHost, long timestamp) {
        if (!mConfig.isConfigSecure() && !isPackageTestOnly(uid)) {
            // Skip package if config is not secure and package is not TestOnly app.
            return true;
        }
        final byte[] digest = getDigestFromUid(uid);
        final boolean result = mDbHelper.insertNewRecord(digest, cncHost, timestamp);
        tryAggregateRecords();
        return result;