Make Network watchlist use ConfigUpdater to get watchlist
Bug: 63908748
Test: test_network_watchlist_full.sh samples/network-watchlist-sample.xml
Change-Id: I9dcca568d3f19f2937786c9c184324b43dc08ff2
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
index 42e43c8..5425bf5 100644
--- a/core/java/android/net/NetworkWatchlistManager.java
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -59,8 +59,8 @@
/**
* Report network watchlist records if necessary.
*
- * Watchlist report process will run summarize records into a single report, then the
- * report will be processed by differential privacy framework and store it on disk.
+ * Watchlist report process will summarize records into a single report, then the
+ * report will be processed by differential privacy framework and stored on disk.
*
* @hide
*/
@@ -72,4 +72,18 @@
e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Reload network watchlist.
+ *
+ * @hide
+ */
+ public void reloadWatchlist() {
+ try {
+ mNetworkWatchlistManager.reloadWatchlist();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to reload watchlist");
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
index 7e88369..ee01a23 100644
--- a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
+++ b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
@@ -22,5 +22,6 @@
interface INetworkWatchlistManager {
boolean startWatchlistLogging();
boolean stopWatchlistLogging();
+ void reloadWatchlist();
void reportWatchlistIfNecessary();
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 13fedfe..1df5298 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3856,6 +3856,14 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.updates.NetworkWatchlistInstallReceiver"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_NETWORK_WATCHLIST" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
<receiver android:name="com.android.server.updates.ApnDbInstallReceiver"
android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index 171703a..f35e6ec 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -33,6 +33,7 @@
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.net.INetworkWatchlistManager;
@@ -92,6 +93,7 @@
}
}
+ @GuardedBy("mLoggingSwitchLock")
private volatile boolean mIsLoggingEnabled = false;
private final Object mLoggingSwitchLock = new Object();
@@ -220,36 +222,11 @@
}
}
- /**
- * Set a new network watchlist.
- * This method should be called by ConfigUpdater only.
- *
- * @return True if network watchlist is updated.
- */
- public boolean setNetworkSecurityWatchlist(List<byte[]> domainsCrc32Digests,
- List<byte[]> domainsSha256Digests,
- List<byte[]> ipAddressesCrc32Digests,
- List<byte[]> ipAddressesSha256Digests) {
- Slog.i(TAG, "Setting network watchlist");
- if (domainsCrc32Digests == null || domainsSha256Digests == null
- || ipAddressesCrc32Digests == null || ipAddressesSha256Digests == null) {
- Slog.e(TAG, "Parameters cannot be null");
- return false;
- }
- if (domainsCrc32Digests.size() != domainsSha256Digests.size()
- || ipAddressesCrc32Digests.size() != ipAddressesSha256Digests.size()) {
- Slog.e(TAG, "Must need to have the same number of CRC32 and SHA256 digests");
- return false;
- }
- if (domainsSha256Digests.size() + ipAddressesSha256Digests.size()
- > MAX_NUM_OF_WATCHLIST_DIGESTS) {
- Slog.e(TAG, "Total watchlist size cannot exceed " + MAX_NUM_OF_WATCHLIST_DIGESTS);
- return false;
- }
- mSettings.writeSettingsToDisk(domainsCrc32Digests, domainsSha256Digests,
- ipAddressesCrc32Digests, ipAddressesSha256Digests);
- Slog.i(TAG, "Set network watchlist: Success");
- return true;
+ @Override
+ public void reloadWatchlist() throws RemoteException {
+ enforceWatchlistLoggingPermission();
+ Slog.i(TAG, "Reloading watchlist");
+ mSettings.reloadSettings();
}
@Override
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
index f48463f..838aa53 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
@@ -21,10 +21,12 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Environment;
import android.util.Pair;
import com.android.internal.util.HexDump;
+import java.io.File;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
@@ -83,9 +85,12 @@
HashMap<String, String> appDigestCNCList;
}
+ static File getSystemWatchlistDbFile() {
+ return new File(Environment.getDataSystemDirectory(), NAME);
+ }
+
private WatchlistReportDbHelper(Context context) {
- super(context, WatchlistSettings.getSystemWatchlistFile(NAME).getAbsolutePath(),
- null, VERSION);
+ super(context, getSystemWatchlistDbFile().getAbsolutePath(), null, VERSION);
// Memory optimization - close idle connections after 30s of inactivity
setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
index c50f0d5..70002ea 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
@@ -19,8 +19,10 @@
import android.os.Environment;
import android.util.AtomicFile;
import android.util.Log;
+import android.util.Slog;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.HexDump;
@@ -51,10 +53,9 @@
class WatchlistSettings {
private static final String TAG = "WatchlistSettings";
- // Settings xml will be stored in /data/system/network_watchlist/watchlist_settings.xml
- static final String SYSTEM_WATCHLIST_DIR = "network_watchlist";
-
- private static final String WATCHLIST_XML_FILE = "watchlist_settings.xml";
+ // Watchlist config that pushed by ConfigUpdater.
+ private static final String NETWORK_WATCHLIST_DB_PATH =
+ "/data/misc/network_watchlist/network_watchlist.xml";
private static class XmlTags {
private static final String WATCHLIST_SETTINGS = "watchlist-settings";
@@ -65,86 +66,74 @@
private static final String HASH = "hash";
}
- private static WatchlistSettings sInstance = new WatchlistSettings();
- private final AtomicFile mXmlFile;
- private final Object mLock = new Object();
- private HarmfulDigests mCrc32DomainDigests = new HarmfulDigests(new ArrayList<>());
- private HarmfulDigests mSha256DomainDigests = new HarmfulDigests(new ArrayList<>());
- private HarmfulDigests mCrc32IpDigests = new HarmfulDigests(new ArrayList<>());
- private HarmfulDigests mSha256IpDigests = new HarmfulDigests(new ArrayList<>());
+ private static class CrcShaDigests {
+ final HarmfulDigests crc32Digests;
+ final HarmfulDigests sha256Digests;
- public static synchronized WatchlistSettings getInstance() {
+ public CrcShaDigests(HarmfulDigests crc32Digests, HarmfulDigests sha256Digests) {
+ this.crc32Digests = crc32Digests;
+ this.sha256Digests = sha256Digests;
+ }
+ }
+
+ private final static WatchlistSettings sInstance = new WatchlistSettings();
+ private final AtomicFile mXmlFile;
+
+ private volatile CrcShaDigests mDomainDigests;
+ private volatile CrcShaDigests mIpDigests;
+
+ public static WatchlistSettings getInstance() {
return sInstance;
}
private WatchlistSettings() {
- this(getSystemWatchlistFile(WATCHLIST_XML_FILE));
+ this(new File(NETWORK_WATCHLIST_DB_PATH));
}
@VisibleForTesting
protected WatchlistSettings(File xmlFile) {
mXmlFile = new AtomicFile(xmlFile);
- readSettingsLocked();
+ reloadSettings();
}
- static File getSystemWatchlistFile(String filename) {
- final File dataSystemDir = Environment.getDataSystemDirectory();
- final File systemWatchlistDir = new File(dataSystemDir, SYSTEM_WATCHLIST_DIR);
- systemWatchlistDir.mkdirs();
- return new File(systemWatchlistDir, filename);
- }
-
- private void readSettingsLocked() {
- synchronized (mLock) {
- FileInputStream stream;
- try {
- stream = mXmlFile.openRead();
- } catch (FileNotFoundException e) {
- Log.i(TAG, "No watchlist settings: " + mXmlFile.getBaseFile().getAbsolutePath());
- return;
- }
+ public void reloadSettings() {
+ try (FileInputStream stream = mXmlFile.openRead()){
final List<byte[]> crc32DomainList = new ArrayList<>();
final List<byte[]> sha256DomainList = new ArrayList<>();
final List<byte[]> crc32IpList = new ArrayList<>();
final List<byte[]> sha256IpList = new ArrayList<>();
- try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
- parser.nextTag();
- parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_SETTINGS);
- while (parser.nextTag() == XmlPullParser.START_TAG) {
- String tagName = parser.getName();
- switch (tagName) {
- case XmlTags.CRC32_DOMAIN:
- parseHash(parser, tagName, crc32DomainList);
- break;
- case XmlTags.CRC32_IP:
- parseHash(parser, tagName, crc32IpList);
- break;
- case XmlTags.SHA256_DOMAIN:
- parseHash(parser, tagName, sha256DomainList);
- break;
- case XmlTags.SHA256_IP:
- parseHash(parser, tagName, sha256IpList);
- break;
- default:
- Log.w(TAG, "Unknown element: " + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
- parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_SETTINGS);
- writeSettingsToMemory(crc32DomainList, sha256DomainList, crc32IpList, sha256IpList);
- } catch (IllegalStateException | NullPointerException | NumberFormatException |
- XmlPullParserException | IOException | IndexOutOfBoundsException e) {
- Log.w(TAG, "Failed parsing " + e);
- } finally {
- try {
- stream.close();
- } catch (IOException e) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+ parser.nextTag();
+ parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_SETTINGS);
+ while (parser.nextTag() == XmlPullParser.START_TAG) {
+ String tagName = parser.getName();
+ switch (tagName) {
+ case XmlTags.CRC32_DOMAIN:
+ parseHash(parser, tagName, crc32DomainList);
+ break;
+ case XmlTags.CRC32_IP:
+ parseHash(parser, tagName, crc32IpList);
+ break;
+ case XmlTags.SHA256_DOMAIN:
+ parseHash(parser, tagName, sha256DomainList);
+ break;
+ case XmlTags.SHA256_IP:
+ parseHash(parser, tagName, sha256IpList);
+ break;
+ default:
+ Log.w(TAG, "Unknown element: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
}
}
+ parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_SETTINGS);
+ writeSettingsToMemory(crc32DomainList, sha256DomainList, crc32IpList, sha256IpList);
+ Log.i(TAG, "Reload watchlist done");
+ } catch (IllegalStateException | NullPointerException | NumberFormatException |
+ XmlPullParserException | IOException | IndexOutOfBoundsException e) {
+ Slog.e(TAG, "Failed parsing xml", e);
}
}
@@ -161,101 +150,61 @@
}
/**
- * Write network watchlist settings to disk.
- * Adb should not use it, should use writeSettingsToMemory directly instead.
- */
- public void writeSettingsToDisk(List<byte[]> newCrc32DomainList,
- List<byte[]> newSha256DomainList,
- List<byte[]> newCrc32IpList,
- List<byte[]> newSha256IpList) {
- synchronized (mLock) {
- FileOutputStream stream;
- try {
- stream = mXmlFile.startWrite();
- } catch (IOException e) {
- Log.w(TAG, "Failed to write display settings: " + e);
- return;
- }
-
- try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
- out.startDocument(null, true);
- out.startTag(null, XmlTags.WATCHLIST_SETTINGS);
-
- writeHashSetToXml(out, XmlTags.SHA256_DOMAIN, newSha256DomainList);
- writeHashSetToXml(out, XmlTags.SHA256_IP, newSha256IpList);
- writeHashSetToXml(out, XmlTags.CRC32_DOMAIN, newCrc32DomainList);
- writeHashSetToXml(out, XmlTags.CRC32_IP, newCrc32IpList);
-
- out.endTag(null, XmlTags.WATCHLIST_SETTINGS);
- out.endDocument();
- mXmlFile.finishWrite(stream);
- writeSettingsToMemory(newCrc32DomainList, newSha256DomainList, newCrc32IpList,
- newSha256IpList);
- } catch (IOException e) {
- Log.w(TAG, "Failed to write display settings, restoring backup.", e);
- mXmlFile.failWrite(stream);
- }
- }
- }
-
- /**
* Write network watchlist settings to memory.
*/
public void writeSettingsToMemory(List<byte[]> newCrc32DomainList,
List<byte[]> newSha256DomainList,
List<byte[]> newCrc32IpList,
List<byte[]> newSha256IpList) {
- synchronized (mLock) {
- mCrc32DomainDigests = new HarmfulDigests(newCrc32DomainList);
- mCrc32IpDigests = new HarmfulDigests(newCrc32IpList);
- mSha256DomainDigests = new HarmfulDigests(newSha256DomainList);
- mSha256IpDigests = new HarmfulDigests(newSha256IpList);
- }
- }
-
- private static void writeHashSetToXml(XmlSerializer out, String tagName, List<byte[]> hashSet)
- throws IOException {
- out.startTag(null, tagName);
- for (byte[] hash : hashSet) {
- out.startTag(null, XmlTags.HASH);
- out.text(HexDump.toHexString(hash));
- out.endTag(null, XmlTags.HASH);
- }
- out.endTag(null, tagName);
+ mDomainDigests = new CrcShaDigests(new HarmfulDigests(newCrc32DomainList),
+ new HarmfulDigests(newSha256DomainList));
+ mIpDigests = new CrcShaDigests(new HarmfulDigests(newCrc32IpList),
+ new HarmfulDigests(newSha256IpList));
}
public boolean containsDomain(String domain) {
+ final CrcShaDigests domainDigests = mDomainDigests;
+ if (domainDigests == null) {
+ Slog.wtf(TAG, "domainDigests should not be null");
+ return false;
+ }
// First it does a quick CRC32 check.
final byte[] crc32 = getCrc32(domain);
- if (!mCrc32DomainDigests.contains(crc32)) {
+ if (!domainDigests.crc32Digests.contains(crc32)) {
return false;
}
// Now we do a slow SHA256 check.
final byte[] sha256 = getSha256(domain);
- return mSha256DomainDigests.contains(sha256);
+ return domainDigests.sha256Digests.contains(sha256);
}
public boolean containsIp(String ip) {
+ final CrcShaDigests ipDigests = mIpDigests;
+ if (ipDigests == null) {
+ Slog.wtf(TAG, "ipDigests should not be null");
+ return false;
+ }
// First it does a quick CRC32 check.
final byte[] crc32 = getCrc32(ip);
- if (!mCrc32IpDigests.contains(crc32)) {
+ if (!ipDigests.crc32Digests.contains(crc32)) {
return false;
}
// Now we do a slow SHA256 check.
final byte[] sha256 = getSha256(ip);
- return mSha256IpDigests.contains(sha256);
+ return ipDigests.sha256Digests.contains(sha256);
}
- /** Get CRC32 of a string */
+ /** Get CRC32 of a string
+ *
+ * TODO: Review if we should use CRC32 or other algorithms
+ */
private byte[] getCrc32(String str) {
final CRC32 crc = new CRC32();
crc.update(str.getBytes());
final long tmp = crc.getValue();
- return new byte[]{(byte)(tmp >> 24 & 255), (byte)(tmp >> 16 & 255),
- (byte)(tmp >> 8 & 255), (byte)(tmp & 255)};
+ return new byte[]{(byte) (tmp >> 24 & 255), (byte) (tmp >> 16 & 255),
+ (byte) (tmp >> 8 & 255), (byte) (tmp & 255)};
}
/** Get SHA256 of a string */
@@ -273,12 +222,12 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Domain CRC32 digest list:");
- mCrc32DomainDigests.dump(fd, pw, args);
+ mDomainDigests.crc32Digests.dump(fd, pw, args);
pw.println("Domain SHA256 digest list:");
- mSha256DomainDigests.dump(fd, pw, args);
+ mDomainDigests.sha256Digests.dump(fd, pw, args);
pw.println("Ip CRC32 digest list:");
- mCrc32IpDigests.dump(fd, pw, args);
+ mIpDigests.crc32Digests.dump(fd, pw, args);
pw.println("Ip SHA256 digest list:");
- mSha256IpDigests.dump(fd, pw, args);
+ mIpDigests.sha256Digests.dump(fd, pw, args);
}
}
diff --git a/services/core/java/com/android/server/updates/NetworkWatchlistInstallReceiver.java b/services/core/java/com/android/server/updates/NetworkWatchlistInstallReceiver.java
new file mode 100644
index 0000000..3b7ddc2
--- /dev/null
+++ b/services/core/java/com/android/server/updates/NetworkWatchlistInstallReceiver.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 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.updates;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkWatchlistManager;
+import android.os.RemoteException;
+import android.util.Slog;
+
+public class NetworkWatchlistInstallReceiver extends ConfigUpdateInstallReceiver {
+
+ public NetworkWatchlistInstallReceiver() {
+ super("/data/misc/network_watchlist/", "network_watchlist.xml", "metadata/", "version");
+ }
+
+ @Override
+ protected void postInstall(Context context, Intent intent) {
+ try {
+ context.getSystemService(NetworkWatchlistManager.class).reloadWatchlist();
+ } catch (Exception e) {
+ // Network Watchlist is not available
+ Slog.wtf("NetworkWatchlistInstallReceiver", "Unable to reload watchlist");
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
index f3cb980..212d25d 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
@@ -95,41 +95,6 @@
}
@Test
- public void testWatchlistSettings_writeSettingsToDisk() throws Exception {
- copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
- WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
- settings.writeSettingsToDisk(Arrays.asList(TEST_NEW_CC_DOMAIN_CRC32),
- Arrays.asList(TEST_NEW_CC_DOMAIN_SHA256), Arrays.asList(TEST_NEW_CC_IP_CRC32),
- Arrays.asList(TEST_NEW_CC_IP_SHA256));
- // Ensure old watchlist is not in memory
- assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
- assertFalse(settings.containsIp(TEST_CC_IP));
- assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
- assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
- assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
- assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
- assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
- assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
- // Ensure new watchlist is in memory
- assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
- assertTrue(settings.containsIp(TEST_NEW_CC_IP));
- // Reload settings from disk and test again
- settings = new WatchlistSettings(mTestXmlFile);
- // Ensure old watchlist is not in memory
- assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
- assertFalse(settings.containsIp(TEST_CC_IP));
- assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
- assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
- assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
- assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
- assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
- assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
- // Ensure new watchlist is in memory
- assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
- assertTrue(settings.containsIp(TEST_NEW_CC_IP));
- }
-
- @Test
public void testWatchlistSettings_writeSettingsToMemory() throws Exception {
copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);