Loading services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java +23 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -80,6 +82,7 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { return; } try { mService.init(); mService.initIpConnectivityMetrics(); mService.startWatchlistLogging(); } catch (RemoteException e) { Loading Loading @@ -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)); Loading @@ -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) { Loading services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java 0 → 100644 +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 , ""); } } services/core/java/com/android/server/net/watchlist/WatchlistConfig.java +36 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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]; Loading Loading @@ -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; Loading Loading @@ -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); Loading services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java +27 −3 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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; Loading Loading
services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java +23 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -80,6 +82,7 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { return; } try { mService.init(); mService.initIpConnectivityMetrics(); mService.startWatchlistLogging(); } catch (RemoteException e) { Loading Loading @@ -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)); Loading @@ -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) { Loading
services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java 0 → 100644 +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 , ""); } }
services/core/java/com/android/server/net/watchlist/WatchlistConfig.java +36 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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]; Loading Loading @@ -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; Loading Loading @@ -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); Loading
services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java +27 −3 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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; Loading