Merge "Add coverage for StopTetheringCallback methods" into main
diff --git a/OWNERS_core_networking b/OWNERS_core_networking
index 078ccde..4b73639 100644
--- a/OWNERS_core_networking
+++ b/OWNERS_core_networking
@@ -1,4 +1,5 @@
jchalard@google.com
+jimictw@google.com
junyulai@google.com
lorenzo@google.com
maze@google.com
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 11b14ed..3fcf356 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -241,7 +241,7 @@
// Currently active tethering requests per tethering type. Only one of each type can be
// requested at a time. After a tethering type is requested, the map keeps tethering parameters
// to be used after the interface comes up asynchronously.
- private final SparseArray<TetheringRequest> mActiveTetheringRequests =
+ private final SparseArray<TetheringRequest> mPendingTetheringRequests =
new SparseArray<>();
private final Context mContext;
@@ -640,7 +640,7 @@
}
if (enabled) {
- ensureIpServerStarted(iface);
+ ensureIpServerStartedForInterface(iface);
} else {
ensureIpServerStopped(iface);
}
@@ -701,7 +701,7 @@
final IIntResultListener listener) {
mHandler.post(() -> {
final int type = request.getTetheringType();
- final TetheringRequest unfinishedRequest = mActiveTetheringRequests.get(type);
+ final TetheringRequest unfinishedRequest = mPendingTetheringRequests.get(type);
// If tethering is already enabled with a different request,
// disable before re-enabling.
if (unfinishedRequest != null && !unfinishedRequest.equalsIgnoreUidPackage(request)) {
@@ -709,7 +709,7 @@
unfinishedRequest.getInterfaceName(), null);
mEntitlementMgr.stopProvisioningIfNeeded(type);
}
- mActiveTetheringRequests.put(type, request);
+ mPendingTetheringRequests.put(type, request);
if (request.isExemptFromEntitlementCheck()) {
mEntitlementMgr.setExemptedDownstreamType(type);
@@ -728,7 +728,7 @@
});
}
void stopTetheringInternal(int type) {
- mActiveTetheringRequests.remove(type);
+ mPendingTetheringRequests.remove(type);
enableTetheringInternal(type, false /* disabled */, null, null);
mEntitlementMgr.stopProvisioningIfNeeded(type);
@@ -782,7 +782,7 @@
// If changing tethering fail, remove corresponding request
// no matter who trigger the start/stop.
if (result != TETHER_ERROR_NO_ERROR) {
- mActiveTetheringRequests.remove(type);
+ mPendingTetheringRequests.remove(type);
mTetheringMetrics.updateErrorCode(type, result);
mTetheringMetrics.sendReport(type);
}
@@ -944,7 +944,9 @@
public void onAvailable(String iface) {
if (this != mBluetoothCallback) return;
- enableIpServing(TETHERING_BLUETOOTH, iface, getRequestedState(TETHERING_BLUETOOTH));
+ final TetheringRequest request = getPendingTetheringRequest(TETHERING_BLUETOOTH);
+ enableIpServing(request, TETHERING_BLUETOOTH, iface,
+ getRequestedState(TETHERING_BLUETOOTH));
mConfiguredBluetoothIface = iface;
}
@@ -999,7 +1001,10 @@
// Ethernet callback arrived after Ethernet tethering stopped. Ignore.
return;
}
- enableIpServing(TETHERING_ETHERNET, iface, getRequestedState(TETHERING_ETHERNET));
+
+ final TetheringRequest request = getPendingTetheringRequest(TETHERING_ETHERNET);
+ enableIpServing(request, TETHERING_ETHERNET, iface,
+ getRequestedState(TETHERING_ETHERNET));
mConfiguredEthernetIface = iface;
}
@@ -1020,7 +1025,9 @@
} else {
mConfiguredVirtualIface = iface;
}
+ final TetheringRequest request = getPendingTetheringRequest(TETHERING_VIRTUAL);
enableIpServing(
+ request,
TETHERING_VIRTUAL,
mConfiguredVirtualIface,
getRequestedState(TETHERING_VIRTUAL));
@@ -1064,7 +1071,7 @@
*/
@NonNull
private TetheringRequest getOrCreatePendingTetheringRequest(int type) {
- TetheringRequest pending = mActiveTetheringRequests.get(type);
+ TetheringRequest pending = mPendingTetheringRequests.get(type);
if (pending != null) return pending;
Log.w(TAG, "No pending TetheringRequest for type " + type + " found, creating a placeholder"
@@ -1074,6 +1081,59 @@
return placeholder;
}
+ private void handleLegacyTether(String iface, int requestedState,
+ final IIntResultListener listener) {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ // After V, the TetheringManager and ConnectivityManager tether and untether methods
+ // throw UnsupportedOperationException, so this cannot happen in normal use. Ensure
+ // that this code cannot run even if callers use raw binder calls or other
+ // unsupported methods.
+ return;
+ }
+
+ final int type = ifaceNameToType(iface);
+ if (type == TETHERING_INVALID) {
+ Log.e(TAG, "Ignoring call to legacy tether for unknown iface " + iface);
+ try {
+ listener.onResult(TETHER_ERROR_UNKNOWN_IFACE);
+ } catch (RemoteException e) { }
+ }
+
+ int result = tetherInternal(null, iface, requestedState);
+ switch (type) {
+ case TETHERING_WIFI:
+ TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
+ "Legacy tether API called on Wifi iface " + iface,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI);
+ if (result == TETHER_ERROR_NO_ERROR) {
+ TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
+ "Legacy tether API succeeded on Wifi iface " + iface,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_SUCCESS);
+ }
+ break;
+ case TETHERING_WIFI_P2P:
+ TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
+ "Legacy tether API called on Wifi P2P iface " + iface,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_P2P);
+ if (result == TETHER_ERROR_NO_ERROR) {
+ TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
+ "Legacy tether API succeeded on Wifi P2P iface " + iface,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_P2P_SUCCESS);
+ }
+ break;
+ default:
+ // Do nothing
+ break;
+ }
+ try {
+ listener.onResult(result);
+ } catch (RemoteException e) { }
+ }
+
/**
* Legacy tether API that starts tethering with CONNECTIVITY_SCOPE_GLOBAL on the given iface.
*
@@ -1088,51 +1148,14 @@
* those broadcasts are disabled by OEM.
*/
void legacyTether(String iface, int requestedState, final IIntResultListener listener) {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) {
- // After V, the TetheringManager and ConnectivityManager tether and untether methods
- // throw UnsupportedOperationException, so this cannot happen in normal use. Ensure
- // that this code cannot run even if callers use raw binder calls or other
- // unsupported methods.
- return;
- }
- mHandler.post(() -> {
- int result = tetherInternal(iface, requestedState);
- switch (ifaceNameToType(iface)) {
- case TETHERING_WIFI:
- TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
- "Legacy tether API called on Wifi iface " + iface,
- CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
- CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI);
- if (result == TETHER_ERROR_NO_ERROR) {
- TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
- "Legacy tether API succeeded on Wifi iface " + iface,
- CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
- CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_SUCCESS);
- }
- break;
- case TETHERING_WIFI_P2P:
- TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
- "Legacy tether API called on Wifi P2P iface " + iface,
- CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
- CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_P2P);
- if (result == TETHER_ERROR_NO_ERROR) {
- TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
- "Legacy tether API succeeded on Wifi P2P iface " + iface,
- CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
- CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_P2P_SUCCESS);
- }
- break;
- default:
- // Do nothing
- break;
- }
- try {
- listener.onResult(result);
- } catch (RemoteException e) { }
- });
+ mHandler.post(() -> handleLegacyTether(iface, requestedState, listener));
}
- private int tetherInternal(String iface, int requestedState) {
+ // TODO: is it possible to make the request @NonNull here?
+ // Because the IpServer doesn't actually look at (almost anything in) the request, we can just
+ // make callers synthesize a reasonable-looking request and make this @NonNull.
+ private int tetherInternal(@Nullable TetheringRequest request, String iface,
+ int requestedState) {
if (DBG) Log.d(TAG, "Tethering " + iface);
TetherState tetherState = mTetherStates.get(iface);
if (tetherState == null) {
@@ -1145,15 +1168,19 @@
Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
return TETHER_ERROR_UNAVAIL_IFACE;
}
- // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet
+ // NOTE: If a CMD_TETHER_REQUESTED message is already in the IpServer's queue but not yet
// processed, this will be a no-op and it will not return an error.
//
// This code cannot race with untether() because they both run on the handler thread.
+ //
+ // TODO: once the request is @NonNull, it will be correct to fetch the type from it, because
+ // the type used to fetch (or synthesize) the request is the same as the type passed in at
+ // IpServer creation time.
+ // BUG: the connectivity scope in the request is ignored. This matches current behaviour.
+ // TODO: see if the connectivity scope can be made (or is already) correct, and fetch the
+ // requested state from there.
final int type = tetherState.ipServer.interfaceType();
- final TetheringRequest request = mActiveTetheringRequests.get(type, null);
- if (request != null) {
- mActiveTetheringRequests.delete(type);
- }
+ mPendingTetheringRequests.remove(type);
tetherState.ipServer.enable(requestedState, request);
return TETHER_ERROR_NO_ERROR;
}
@@ -1213,8 +1240,9 @@
return mEntitlementMgr.isTetherProvisioningRequired(cfg);
}
+ // TODO: make this take a TetheringRequest instead.
private int getRequestedState(int type) {
- final TetheringRequest request = mActiveTetheringRequests.get(type);
+ final TetheringRequest request = mPendingTetheringRequests.get(type);
// The request could have been deleted before we had a chance to complete it.
// If so, assume that the scope is the default scope for this tethering type.
@@ -1529,8 +1557,8 @@
}
@VisibleForTesting
- SparseArray<TetheringRequest> getActiveTetheringRequests() {
- return mActiveTetheringRequests;
+ SparseArray<TetheringRequest> getPendingTetheringRequests() {
+ return mPendingTetheringRequests;
}
@VisibleForTesting
@@ -1590,14 +1618,21 @@
}
}
- private void enableIpServing(int tetheringType, String ifname, int ipServingMode) {
- enableIpServing(tetheringType, ifname, ipServingMode, false /* isNcm */);
+ final TetheringRequest getPendingTetheringRequest(int type) {
+ return mPendingTetheringRequests.get(type, null);
}
- private void enableIpServing(int tetheringType, String ifname, int ipServingMode,
- boolean isNcm) {
- ensureIpServerStarted(ifname, tetheringType, isNcm);
- if (tetherInternal(ifname, ipServingMode) != TETHER_ERROR_NO_ERROR) {
+ // TODO: make the request @NonNull and move the tetheringType and ipServingMode into it.
+ private void enableIpServing(@Nullable TetheringRequest request, int tetheringType,
+ String ifname, int ipServingMode) {
+ enableIpServing(request, tetheringType, ifname, ipServingMode, false /* isNcm */);
+ }
+
+ // TODO: make the request @NonNull and move the tetheringType and ipServingMode into it.
+ private void enableIpServing(@Nullable TetheringRequest request, int tetheringType,
+ String ifname, int ipServingMode, boolean isNcm) {
+ ensureIpServerStartedForType(ifname, tetheringType, isNcm);
+ if (tetherInternal(request, ifname, ipServingMode) != TETHER_ERROR_NO_ERROR) {
Log.e(TAG, "unable start tethering on iface " + ifname);
}
}
@@ -1649,7 +1684,9 @@
mLog.e(ifname + " is not a tetherable iface, ignoring");
return;
}
- enableIpServing(type, ifname, IpServer.STATE_LOCAL_ONLY);
+ // No need to look for active requests. There can never be explicit requests for
+ // TETHERING_WIFI_P2P because enableTetheringInternal ignores that type.
+ enableIpServing(null, type, ifname, IpServer.STATE_LOCAL_ONLY);
}
private void disableWifiP2pIpServingIfNeeded(String ifname) {
@@ -1659,17 +1696,35 @@
disableWifiIpServingCommon(TETHERING_WIFI_P2P, ifname);
}
+ // TODO: fold this in to enableWifiIpServing. We cannot do this at the moment because there
+ // are tests that send wifi AP broadcasts with a null interface. But if this can't happen on
+ // real devices, we should fix those tests to always pass in an interface.
+ private int maybeInferWifiTetheringType(String ifname) {
+ return SdkLevel.isAtLeastT() ? TETHERING_WIFI : ifaceNameToType(ifname);
+ }
+
private void enableWifiIpServing(String ifname, int wifiIpMode) {
mLog.log("request WiFi tethering - interface=" + ifname + " state=" + wifiIpMode);
// Map wifiIpMode values to IpServer.Callback serving states.
+ TetheringRequest request;
final int ipServingMode;
+ final int type;
switch (wifiIpMode) {
case IFACE_IP_MODE_TETHERED:
ipServingMode = IpServer.STATE_TETHERED;
+ type = maybeInferWifiTetheringType(ifname);
+ request = getPendingTetheringRequest(type);
break;
case IFACE_IP_MODE_LOCAL_ONLY:
ipServingMode = IpServer.STATE_LOCAL_ONLY;
+ type = maybeInferWifiTetheringType(ifname);
+ // BUG: this request is incorrect - instead of LOHS, it will reflect whatever
+ // request (if any) is being processed for TETHERING_WIFI. However, this is the
+ // historical behaviour. It mostly works because a) most of the time there is no
+ // such request b) tetherinternal doesn't look at the connectivity scope of the
+ // request, it takes the scope from requestedState.
+ request = getPendingTetheringRequest(type);
break;
default:
mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode);
@@ -1678,14 +1733,13 @@
// After T, tethering always trust the iface pass by state change intent. This allow
// tethering to deprecate tetherable wifi regexs after T.
- final int type = SdkLevel.isAtLeastT() ? TETHERING_WIFI : ifaceNameToType(ifname);
if (!checkTetherableType(type)) {
mLog.e(ifname + " is not a tetherable iface, ignoring");
return;
}
if (!TextUtils.isEmpty(ifname)) {
- enableIpServing(type, ifname, ipServingMode);
+ enableIpServing(request, type, ifname, ipServingMode);
} else {
mLog.e("Cannot enable IP serving on missing interface name");
}
@@ -1715,10 +1769,11 @@
return;
}
+ final TetheringRequest request = getPendingTetheringRequest(tetheringType);
if (ifaces != null) {
for (String iface : ifaces) {
if (ifaceNameToType(iface) == tetheringType) {
- enableIpServing(tetheringType, iface, requestedState, forNcmFunction);
+ enableIpServing(request, tetheringType, iface, requestedState, forNcmFunction);
return;
}
}
@@ -2950,7 +3005,7 @@
return type != TETHERING_INVALID;
}
- private void ensureIpServerStarted(final String iface) {
+ private void ensureIpServerStartedForInterface(final String iface) {
// If we don't care about this type of interface, ignore.
final int interfaceType = ifaceNameToType(iface);
if (!checkTetherableType(interfaceType)) {
@@ -2959,10 +3014,11 @@
return;
}
- ensureIpServerStarted(iface, interfaceType, false /* isNcm */);
+ ensureIpServerStartedForType(iface, interfaceType, false /* isNcm */);
}
- private void ensureIpServerStarted(final String iface, int interfaceType, boolean isNcm) {
+ private void ensureIpServerStartedForType(final String iface, int interfaceType,
+ boolean isNcm) {
// If we have already started a TISM for this interface, skip.
if (mTetherStates.containsKey(iface)) {
mLog.log("active iface (" + iface + ") reported as added, ignoring");
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index e50a7fd..866b219 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -957,8 +957,8 @@
mTethering.startTethering(request, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
- assertEquals(1, mTethering.getActiveTetheringRequests().size());
- assertEquals(request, mTethering.getActiveTetheringRequests().get(TETHERING_USB));
+ assertEquals(1, mTethering.getPendingTetheringRequests().size());
+ assertEquals(request, mTethering.getPendingTetheringRequests().get(TETHERING_USB));
if (mTethering.getTetheringConfiguration().isUsingNcm()) {
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NCM);
@@ -2170,7 +2170,7 @@
runUsbTethering(upstreamState);
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
assertTrue(mTethering.isTetheringActive());
- assertEquals(0, mTethering.getActiveTetheringRequests().size());
+ assertEquals(0, mTethering.getPendingTetheringRequests().size());
final Tethering.UserRestrictionActionListener ural = makeUserRestrictionActionListener(
mTethering, false /* currentDisallow */, true /* nextDisallow */);
diff --git a/bpf/headers/include/bpf/BpfMap.h b/bpf/headers/include/bpf/BpfMap.h
index 1037beb..576cca6 100644
--- a/bpf/headers/include/bpf/BpfMap.h
+++ b/bpf/headers/include/bpf/BpfMap.h
@@ -26,6 +26,7 @@
#include "BpfSyscallWrappers.h"
#include "bpf/BpfUtils.h"
+#include <cstdio>
#include <functional>
namespace android {
@@ -35,6 +36,30 @@
using base::unique_fd;
using std::function;
+#ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
+#undef BPFMAP_VERBOSE_ABORT
+#define BPFMAP_VERBOSE_ABORT
+#endif
+
+[[noreturn]] __attribute__((__format__(__printf__, 2, 3))) static inline
+void Abort(int __unused error, const char* __unused fmt, ...) {
+#ifdef BPFMAP_VERBOSE_ABORT
+ va_list va;
+ va_start(va, fmt);
+
+ fflush(stdout);
+ vfprintf(stderr, fmt, va);
+ if (error) fprintf(stderr, "; errno=%d [%s]", error, strerror(error));
+ putc('\n', stderr);
+ fflush(stderr);
+
+ va_end(va);
+#endif
+
+ abort();
+}
+
+
// This is a class wrapper for eBPF maps. The eBPF map is a special in-kernel
// data structure that stores data in <Key, Value> pairs. It can be read/write
// from userspace by passing syscalls with the map file descriptor. This class
@@ -60,14 +85,21 @@
protected:
void abortOnMismatch(bool writable) const {
- if (!mMapFd.ok()) abort();
+ if (!mMapFd.ok()) Abort(errno, "mMapFd %d is not valid", mMapFd.get());
if (isAtLeastKernelVersion(4, 14, 0)) {
int flags = bpfGetFdMapFlags(mMapFd);
- if (flags < 0) abort();
- if (flags & BPF_F_WRONLY) abort();
- if (writable && (flags & BPF_F_RDONLY)) abort();
- if (bpfGetFdKeySize(mMapFd) != sizeof(Key)) abort();
- if (bpfGetFdValueSize(mMapFd) != sizeof(Value)) abort();
+ if (flags < 0) Abort(errno, "bpfGetFdMapFlags fail: flags=%d", flags);
+ if (flags & BPF_F_WRONLY) Abort(0, "map is write-only (flags=0x%X)", flags);
+ if (writable && (flags & BPF_F_RDONLY))
+ Abort(0, "writable map is actually read-only (flags=0x%X)", flags);
+ int keySize = bpfGetFdKeySize(mMapFd);
+ if (keySize != sizeof(Key))
+ Abort(errno, "map key size mismatch (expected=%zu, actual=%d)",
+ sizeof(Key), keySize);
+ int valueSize = bpfGetFdValueSize(mMapFd);
+ if (valueSize != sizeof(Value))
+ Abort(errno, "map value size mismatch (expected=%zu, actual=%d)",
+ sizeof(Value), valueSize);
}
}
@@ -278,8 +310,8 @@
[[clang::reinitializes]] Result<void> resetMap(bpf_map_type map_type,
uint32_t max_entries,
uint32_t map_flags = 0) {
- if (map_flags & BPF_F_WRONLY) abort();
- if (map_flags & BPF_F_RDONLY) abort();
+ if (map_flags & BPF_F_WRONLY) Abort(0, "map_flags is write-only");
+ if (map_flags & BPF_F_RDONLY) Abort(0, "map_flags is read-only");
mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries,
map_flags));
if (!mMapFd.ok()) return ErrnoErrorf("BpfMap::resetMap() failed");
diff --git a/bpf/headers/include/bpf_helpers.h b/bpf/headers/include/bpf_helpers.h
index 425c429..67de633 100644
--- a/bpf/headers/include/bpf_helpers.h
+++ b/bpf/headers/include/bpf_helpers.h
@@ -128,14 +128,15 @@
#define KVER_(v) ((struct kver_uint){ .kver = (v) })
#define KVER(a, b, c) KVER_(((a) << 24) + ((b) << 16) + (c))
#define KVER_NONE KVER_(0)
+#define KVER_4_9 KVER(4, 9, 0)
#define KVER_4_14 KVER(4, 14, 0)
#define KVER_4_19 KVER(4, 19, 0)
#define KVER_5_4 KVER(5, 4, 0)
-#define KVER_5_8 KVER(5, 8, 0)
#define KVER_5_10 KVER(5, 10, 0)
#define KVER_5_15 KVER(5, 15, 0)
#define KVER_6_1 KVER(6, 1, 0)
#define KVER_6_6 KVER(6, 6, 0)
+#define KVER_6_12 KVER(6, 12, 0)
#define KVER_INF KVER_(0xFFFFFFFFu)
#define KVER_IS_AT_LEAST(kver, a, b, c) ((kver).kver >= KVER(a, b, c).kver)
diff --git a/bpf/progs/netd.c b/bpf/progs/netd.c
index 8897627..aab9c26 100644
--- a/bpf/progs/netd.c
+++ b/bpf/progs/netd.c
@@ -99,6 +99,17 @@
DEFINE_BPF_MAP_RO_NETD(data_saver_enabled_map, ARRAY, uint32_t, bool,
DATA_SAVER_ENABLED_MAP_SIZE)
+DEFINE_BPF_MAP_EXT(local_net_access_map, LPM_TRIE, LocalNetAccessKey, bool, 1000,
+ AID_ROOT, AID_NET_BW_ACCT, 0060, "fs_bpf_net_shared", "", PRIVATE,
+ BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER, LOAD_ON_ENG, LOAD_ON_USER,
+ LOAD_ON_USERDEBUG, 0)
+
+// not preallocated
+DEFINE_BPF_MAP_EXT(local_net_blocked_uid_map, HASH, uint32_t, bool, -1000,
+ AID_ROOT, AID_NET_BW_ACCT, 0060, "fs_bpf_net_shared", "", PRIVATE,
+ BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER, LOAD_ON_ENG, LOAD_ON_USER,
+ LOAD_ON_USERDEBUG, 0)
+
// iptables xt_bpf programs need to be usable by both netd and netutils_wrappers
// selinux contexts, because even non-xt_bpf iptables mutations are implemented as
// a full table dump, followed by an update in userspace, and then a reload into the kernel,
@@ -230,6 +241,66 @@
: bpf_skb_load_bytes(skb, L3_off, to, len);
}
+// False iff arguments are found with longest prefix match lookup and disallowed.
+static inline __always_inline bool is_local_net_access_allowed(const uint32_t if_index,
+ const struct in6_addr* remote_ip6, const uint16_t protocol, const __be16 remote_port) {
+ LocalNetAccessKey query_key = {
+ .lpm_bitlen = 8 * (sizeof(if_index) + sizeof(*remote_ip6) + sizeof(protocol)
+ + sizeof(remote_port)),
+ .if_index = if_index,
+ .remote_ip6 = *remote_ip6,
+ .protocol = protocol,
+ .remote_port = remote_port
+ };
+ bool* v = bpf_local_net_access_map_lookup_elem(&query_key);
+ return v ? *v : true;
+}
+
+static __always_inline inline bool should_block_local_network_packets(struct __sk_buff *skb,
+ const uint32_t uid, const struct egress_bool egress,
+ const struct kver_uint kver) {
+ if (is_system_uid(uid)) return false;
+
+ bool* block_local_net = bpf_local_net_blocked_uid_map_lookup_elem(&uid);
+ if (!block_local_net) return false; // uid not found in map
+ if (!*block_local_net) return false; // lookup returned 'bool false'
+
+ struct in6_addr remote_ip6;
+ uint8_t ip_proto;
+ uint8_t L4_off;
+ if (skb->protocol == htons(ETH_P_IP)) {
+ int remote_ip_ofs = egress.egress ? IP4_OFFSET(daddr) : IP4_OFFSET(saddr);
+ remote_ip6.s6_addr32[0] = 0;
+ remote_ip6.s6_addr32[1] = 0;
+ remote_ip6.s6_addr32[2] = htonl(0xFFFF);
+ (void)bpf_skb_load_bytes_net(skb, remote_ip_ofs, &remote_ip6.s6_addr32[3], 4, kver);
+ (void)bpf_skb_load_bytes_net(skb, IP4_OFFSET(protocol), &ip_proto, sizeof(ip_proto), kver);
+ uint8_t ihl;
+ (void)bpf_skb_load_bytes_net(skb, IPPROTO_IHL_OFF, &ihl, sizeof(ihl), kver);
+ L4_off = (ihl & 0x0F) * 4; // IHL calculation.
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ int remote_ip_ofs = egress.egress ? IP6_OFFSET(daddr) : IP6_OFFSET(saddr);
+ (void)bpf_skb_load_bytes_net(skb, remote_ip_ofs, &remote_ip6, sizeof(remote_ip6), kver);
+ (void)bpf_skb_load_bytes_net(skb, IP6_OFFSET(nexthdr), &ip_proto, sizeof(ip_proto), kver);
+ L4_off = sizeof(struct ipv6hdr);
+ } else {
+ return false;
+ }
+
+ __be16 remote_port = 0;
+ switch (ip_proto) {
+ case IPPROTO_TCP:
+ case IPPROTO_DCCP:
+ case IPPROTO_UDP:
+ case IPPROTO_UDPLITE:
+ case IPPROTO_SCTP:
+ (void)bpf_skb_load_bytes_net(skb, L4_off + (egress.egress ? 2 : 0), &remote_port, sizeof(remote_port), kver);
+ break;
+ }
+
+ return !is_local_net_access_allowed(skb->ifindex, &remote_ip6, ip_proto, remote_port);
+}
+
static __always_inline inline void do_packet_tracing(
const struct __sk_buff* const skb, const struct egress_bool egress, const uint32_t uid,
const uint32_t tag, const struct kver_uint kver) {
@@ -488,7 +559,7 @@
}
if (SDK_LEVEL_IS_AT_LEAST(lvl, 25Q2) && (match != DROP)) {
- // TODO: implement local network blocking
+ if (should_block_local_network_packets(skb, uid, egress, kver)) match = DROP;
}
// If an outbound packet is going to be dropped, we do not count that traffic.
diff --git a/bpf/progs/netd.h b/bpf/progs/netd.h
index be7c311..8400679 100644
--- a/bpf/progs/netd.h
+++ b/bpf/progs/netd.h
@@ -185,6 +185,8 @@
#define PACKET_TRACE_RINGBUF_PATH BPF_NETD_PATH "map_netd_packet_trace_ringbuf"
#define PACKET_TRACE_ENABLED_MAP_PATH BPF_NETD_PATH "map_netd_packet_trace_enabled_map"
#define DATA_SAVER_ENABLED_MAP_PATH BPF_NETD_PATH "map_netd_data_saver_enabled_map"
+#define LOCAL_NET_ACCESS_MAP_PATH BPF_NETD_PATH "map_netd_local_net_access_map"
+#define LOCAL_NET_BLOCKED_UID_MAP_PATH BPF_NETD_PATH "map_netd_local_net_blocked_uid_map"
#endif // __cplusplus
@@ -245,6 +247,18 @@
} IngressDiscardValue;
STRUCT_SIZE(IngressDiscardValue, 2 * 4); // 8
+typedef struct {
+ // Longest prefix match length in bits (value from 0 to 192).
+ uint32_t lpm_bitlen;
+ uint32_t if_index;
+ // IPv4 uses IPv4-mapped IPv6 address format.
+ struct in6_addr remote_ip6;
+ // u16 instead of u8 to avoid padding due to alignment requirement.
+ uint16_t protocol;
+ __be16 remote_port;
+} LocalNetAccessKey;
+STRUCT_SIZE(LocalNetAccessKey, 4 + 4 + 16 + 2 + 2); // 28
+
// Entry in the configuration map that stores which UID rules are enabled.
#define UID_RULES_CONFIGURATION_KEY 0
// Entry in the configuration map that stores which stats map is currently in use.
diff --git a/bpf/tests/mts/bpf_existence_test.cpp b/bpf/tests/mts/bpf_existence_test.cpp
index 0b5b7be..2cfa546 100644
--- a/bpf/tests/mts/bpf_existence_test.cpp
+++ b/bpf/tests/mts/bpf_existence_test.cpp
@@ -20,7 +20,9 @@
#include <set>
#include <string>
+#include <android-base/properties.h>
#include <android-modules-utils/sdk_level.h>
+#include <android/api-level.h>
#include <bpf/BpfUtils.h>
#include <gtest/gtest.h>
@@ -46,6 +48,11 @@
class BpfExistenceTest : public ::testing::Test {
};
+//ToDo: replace isAtLeast25Q2 with IsAtLeastB once sdk_level have been upgraded to 36 on aosp/main
+const bool unreleased = (android::base::GetProperty("ro.build.version.codename", "REL") != "REL");
+const int api_level = unreleased ? __ANDROID_API_FUTURE__ : android_get_device_api_level();
+const bool isAtLeast25Q2 = (api_level > __ANDROID_API_V__);
+
// Part of Android R platform (for 4.9+), but mainlined in S
static const set<string> PLATFORM_ONLY_IN_R = {
PLATFORM "map_offload_tether_ingress_map",
@@ -159,6 +166,12 @@
NETD "prog_netd_setsockopt_prog",
};
+// Provided by *current* mainline module for 25Q2+ devices
+static const set<string> MAINLINE_FOR_25Q2_PLUS = {
+ NETD "map_netd_local_net_access_map",
+ NETD "map_netd_local_net_blocked_uid_map",
+};
+
static void addAll(set<string>& a, const set<string>& b) {
a.insert(b.begin(), b.end());
}
@@ -209,6 +222,9 @@
DO_EXPECT(IsAtLeastV(), MAINLINE_FOR_V_PLUS);
DO_EXPECT(IsAtLeastV() && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_V_5_4_PLUS);
+ if (isAtLeast25Q2) ASSERT_TRUE(isAtLeastKernelVersion(5, 4, 0));
+ DO_EXPECT(isAtLeast25Q2, MAINLINE_FOR_25Q2_PLUS);
+
for (const auto& file : mustExist) {
EXPECT_EQ(0, access(file.c_str(), R_OK)) << file << " does not exist";
}
diff --git a/networksecurity/TEST_MAPPING b/networksecurity/TEST_MAPPING
index f9238c3..448ee84 100644
--- a/networksecurity/TEST_MAPPING
+++ b/networksecurity/TEST_MAPPING
@@ -10,6 +10,9 @@
"name": "NetSecConfigCertificateTransparencySctLogListTestCases"
},
{
+ "name": "NetSecConfigCertificateTransparencySctNoLogListTestCases"
+ },
+ {
"name": "NetworkSecurityUnitTests"
}
]
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
index ce14fc6..1478fd1 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
@@ -16,10 +16,10 @@
package com.android.server.net.ct;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_HTTP_ERROR;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_NOT_FOUND;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_VERSION_ALREADY_EXISTS;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_HTTP_ERROR;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_PUBLIC_KEY_NOT_FOUND;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_VERIFICATION;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_VERSION_ALREADY_EXISTS;
import android.annotation.RequiresApi;
import android.app.DownloadManager;
@@ -29,7 +29,6 @@
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
-import android.provider.DeviceConfig;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -237,13 +236,15 @@
try {
success = mSignatureVerifier.verify(contentUri, metadataUri);
} catch (MissingPublicKeyException e) {
- if (updateFailureCount()) {
- failureReason = CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_NOT_FOUND;
- }
+ updateFailureCount();
+ failureReason =
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_PUBLIC_KEY_NOT_FOUND;
+ Log.e(TAG, "No public key found for log list verification", e);
} catch (InvalidKeyException e) {
- if (updateFailureCount()) {
- failureReason = CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION;
- }
+ updateFailureCount();
+ failureReason =
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_VERIFICATION;
+ Log.e(TAG, "Signature invalid for log list verification", e);
} catch (IOException | GeneralSecurityException e) {
Log.e(TAG, "Could not verify new log list", e);
}
@@ -252,12 +253,14 @@
Log.w(TAG, "Log list did not pass verification");
// Avoid logging failure twice
- if (failureReason == -1 && updateFailureCount()) {
- failureReason = CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION;
+ if (failureReason == -1) {
+ updateFailureCount();
+ failureReason =
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_VERIFICATION;
}
if (failureReason != -1) {
- mLogger.logCTLogListUpdateFailedEvent(
+ mLogger.logCTLogListUpdateStateChangedEvent(
failureReason,
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0));
@@ -277,42 +280,38 @@
mDataStore.setPropertyInt(Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* value= */ 0);
mDataStore.store();
} else {
- if (updateFailureCount()) {
- mLogger.logCTLogListUpdateFailedEvent(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_VERSION_ALREADY_EXISTS,
- mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0));
+ updateFailureCount();
+ mLogger.logCTLogListUpdateStateChangedEvent(
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_VERSION_ALREADY_EXISTS,
+ mDataStore.getPropertyInt(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0));
}
}
- }
private void handleDownloadFailed(DownloadStatus status) {
Log.e(TAG, "Download failed with " + status);
- if (updateFailureCount()) {
- int failureCount =
- mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0);
+ updateFailureCount();
+ int failureCount =
+ mDataStore.getPropertyInt(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0);
- if (status.isHttpError()) {
- mLogger.logCTLogListUpdateFailedEvent(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_HTTP_ERROR,
- failureCount,
- status.reason());
- } else {
- // TODO(b/384935059): handle blocked domain logging
- mLogger.logCTLogListUpdateFailedEventWithDownloadStatus(
- status.reason(), failureCount);
- }
+ if (status.isHttpError()) {
+ mLogger.logCTLogListUpdateStateChangedEvent(
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_HTTP_ERROR,
+ failureCount,
+ status.reason());
+ } else {
+ // TODO(b/384935059): handle blocked domain logging
+ mLogger.logCTLogListUpdateStateChangedEventWithDownloadStatus(
+ status.reason(), failureCount);
}
}
/**
* Updates the data store with the current number of consecutive log list update failures.
- *
- * @return whether the failure count exceeds the threshold and should be logged.
*/
- private boolean updateFailureCount() {
+ private void updateFailureCount() {
int failure_count =
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0);
@@ -320,17 +319,6 @@
mDataStore.setPropertyInt(Config.LOG_LIST_UPDATE_FAILURE_COUNT, new_failure_count);
mDataStore.store();
-
- int threshold = DeviceConfig.getInt(
- Config.NAMESPACE_NETWORK_SECURITY,
- Config.FLAG_LOG_FAILURE_THRESHOLD,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
-
- boolean shouldReport = new_failure_count >= threshold;
- if (shouldReport) {
- Log.d(TAG, "Log list update failure count exceeds threshold: " + new_failure_count);
- }
- return shouldReport;
}
private long download(String url) {
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java
index 913c472..a6b15ab 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java
@@ -20,29 +20,30 @@
public interface CertificateTransparencyLogger {
/**
- * Logs a CTLogListUpdateFailed event to statsd, when failure is provided by DownloadManager.
+ * Logs a CTLogListUpdateStateChanged event to statsd, when failure is from DownloadManager.
*
* @param downloadStatus DownloadManager failure status why the log list wasn't updated
* @param failureCount number of consecutive log list update failures
*/
- void logCTLogListUpdateFailedEventWithDownloadStatus(int downloadStatus, int failureCount);
+ void logCTLogListUpdateStateChangedEventWithDownloadStatus(
+ int downloadStatus, int failureCount);
/**
- * Logs a CTLogListUpdateFailed event to statsd, when no HTTP error status code is present.
+ * Logs a CTLogListUpdateStateChanged event to statsd without a HTTP error status code.
*
* @param failureReason reason why the log list wasn't updated
* @param failureCount number of consecutive log list update failures
*/
- void logCTLogListUpdateFailedEvent(int failureReason, int failureCount);
+ void logCTLogListUpdateStateChangedEvent(int failureReason, int failureCount);
/**
- * Logs a CTLogListUpdateFailed event to statsd, when an HTTP error status code is provided.
+ * Logs a CTLogListUpdateStateChanged event to statsd with an HTTP error status code.
*
* @param failureReason reason why the log list wasn't updated (e.g. DownloadManager failures)
* @param failureCount number of consecutive log list update failures
* @param httpErrorStatusCode if relevant, the HTTP error status code from DownloadManager
*/
- void logCTLogListUpdateFailedEvent(
+ void logCTLogListUpdateStateChangedEvent(
int failureReason, int failureCount, int httpErrorStatusCode);
}
\ No newline at end of file
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java
index b97a885..3f5d1aa 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java
@@ -16,14 +16,14 @@
package com.android.server.net.ct;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_DEVICE_OFFLINE;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_DOWNLOAD_CANNOT_RESUME;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_HTTP_ERROR;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_NO_DISK_SPACE;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_TOO_MANY_REDIRECTS;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_UNKNOWN;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__PENDING_WAITING_FOR_WIFI;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_DEVICE_OFFLINE;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_DOWNLOAD_CANNOT_RESUME;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_HTTP_ERROR;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_NO_DISK_SPACE;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_TOO_MANY_REDIRECTS;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_UNKNOWN;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__PENDING_WAITING_FOR_WIFI;
import android.app.DownloadManager;
@@ -31,25 +31,28 @@
class CertificateTransparencyLoggerImpl implements CertificateTransparencyLogger {
@Override
- public void logCTLogListUpdateFailedEventWithDownloadStatus(
+ public void logCTLogListUpdateStateChangedEventWithDownloadStatus(
int downloadStatus, int failureCount) {
- logCTLogListUpdateFailedEvent(downloadStatusToFailureReason(downloadStatus), failureCount);
+ logCTLogListUpdateStateChangedEvent(
+ downloadStatusToFailureReason(downloadStatus), failureCount);
}
@Override
- public void logCTLogListUpdateFailedEvent(int failureReason, int failureCount) {
- logCTLogListUpdateFailedEvent(failureReason, failureCount, /* httpErrorStatusCode= */ 0);
+ public void logCTLogListUpdateStateChangedEvent(int failureReason, int failureCount) {
+ logCTLogListUpdateStateChangedEvent(
+ failureReason, failureCount, /* httpErrorStatusCode= */ 0);
}
@Override
- public void logCTLogListUpdateFailedEvent(
+ public void logCTLogListUpdateStateChangedEvent(
int failureReason, int failureCount, int httpErrorStatusCode) {
CertificateTransparencyStatsLog.write(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED,
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED,
failureReason,
failureCount,
- httpErrorStatusCode
- );
+ httpErrorStatusCode,
+ /* signature= */ "",
+ /* logListTimestampMs= */ 0);
}
/** Converts DownloadStatus reason into failure reason to log. */
@@ -57,21 +60,20 @@
switch (downloadStatusReason) {
case DownloadManager.PAUSED_WAITING_TO_RETRY:
case DownloadManager.PAUSED_WAITING_FOR_NETWORK:
- return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_DEVICE_OFFLINE;
+ return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_DEVICE_OFFLINE;
case DownloadManager.ERROR_UNHANDLED_HTTP_CODE:
case DownloadManager.ERROR_HTTP_DATA_ERROR:
- return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_HTTP_ERROR;
+ return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_HTTP_ERROR;
case DownloadManager.ERROR_TOO_MANY_REDIRECTS:
- return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_TOO_MANY_REDIRECTS;
+ return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_TOO_MANY_REDIRECTS;
case DownloadManager.ERROR_CANNOT_RESUME:
- return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_DOWNLOAD_CANNOT_RESUME;
+ return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_DOWNLOAD_CANNOT_RESUME;
case DownloadManager.ERROR_INSUFFICIENT_SPACE:
- return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_NO_DISK_SPACE;
+ return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_NO_DISK_SPACE;
case DownloadManager.PAUSED_QUEUED_FOR_WIFI:
- return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__PENDING_WAITING_FOR_WIFI;
+ return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__PENDING_WAITING_FOR_WIFI;
default:
- return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_UNKNOWN;
+ return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_UNKNOWN;
}
}
-
}
diff --git a/networksecurity/service/src/com/android/server/net/ct/Config.java b/networksecurity/service/src/com/android/server/net/ct/Config.java
index bc4efab..5fdba09 100644
--- a/networksecurity/service/src/com/android/server/net/ct/Config.java
+++ b/networksecurity/service/src/com/android/server/net/ct/Config.java
@@ -45,7 +45,6 @@
static final String FLAG_METADATA_URL = FLAGS_PREFIX + "metadata_url";
static final String FLAG_VERSION = FLAGS_PREFIX + "version";
static final String FLAG_PUBLIC_KEY = FLAGS_PREFIX + "public_key";
- static final String FLAG_LOG_FAILURE_THRESHOLD = FLAGS_PREFIX + "log_list_failure_threshold";
// properties
static final String VERSION = "version";
@@ -59,7 +58,4 @@
static final String URL_LOG_LIST = URL_PREFIX + "log_list.json";
static final String URL_SIGNATURE = URL_PREFIX + "log_list.sig";
static final String URL_PUBLIC_KEY = URL_PREFIX + "log_list.pub";
-
- // Threshold amounts
- static final int DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD = 10;
}
diff --git a/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java b/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java
index 96488fc..67ef63f 100644
--- a/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java
+++ b/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.net.Uri;
import android.os.Build;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -62,10 +63,15 @@
}
void setPublicKey(String publicKey) throws GeneralSecurityException {
+ byte[] decodedPublicKey = null;
+ try {
+ decodedPublicKey = Base64.getDecoder().decode(publicKey);
+ } catch (IllegalArgumentException e) {
+ throw new GeneralSecurityException("Invalid public key base64 encoding", e);
+ }
setPublicKey(
KeyFactory.getInstance("RSA")
- .generatePublic(
- new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey))));
+ .generatePublic(new X509EncodedKeySpec(decodedPublicKey)));
}
@VisibleForTesting
@@ -82,10 +88,21 @@
verifier.initVerify(mPublicKey.get());
ContentResolver contentResolver = mContext.getContentResolver();
+ boolean success = false;
try (InputStream fileStream = contentResolver.openInputStream(file);
InputStream signatureStream = contentResolver.openInputStream(signature)) {
verifier.update(fileStream.readAllBytes());
- return verifier.verify(signatureStream.readAllBytes());
+
+ byte[] signatureBytes = signatureStream.readAllBytes();
+ try {
+ success = verifier.verify(Base64.getDecoder().decode(signatureBytes));
+ } catch (IllegalArgumentException e) {
+ Log.w("CertificateTransparencyDownloader", "Invalid signature base64 encoding", e);
+ // TODO: remove the fallback once the signature base64 is published
+ Log.i("CertificateTransparencyDownloader", "Signature verification as raw bytes");
+ success = verifier.verify(signatureBytes);
+ }
}
+ return success;
}
}
diff --git a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
index 2f57fc9..dc8e54b 100644
--- a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
+++ b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
@@ -16,9 +16,10 @@
package com.android.server.net.ct;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_NOT_FOUND;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION;
-import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_VERSION_ALREADY_EXISTS;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_PUBLIC_KEY_NOT_FOUND;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_NOT_FOUND;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_VERIFICATION;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_VERSION_ALREADY_EXISTS;
import static com.google.common.truth.Truth.assertThat;
@@ -198,13 +199,9 @@
}
@Test
- public void testDownloader_publicKeyDownloadFail_failureThresholdExceeded_logsFailure()
+ public void testDownloader_publicKeyDownloadFail_logsFailure()
throws Exception {
mCertificateTransparencyDownloader.startPublicKeyDownload();
- // Set the failure count to just below the threshold
- mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
mCertificateTransparencyDownloader.onReceive(
mContext,
@@ -213,30 +210,11 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
- verify(mLogger, times(1))
- .logCTLogListUpdateFailedEventWithDownloadStatus(
- DownloadManager.ERROR_INSUFFICIENT_SPACE,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
- }
-
- @Test
- public void testDownloader_publicKeyDownloadFail_failureThresholdNotMet_doesNotLog()
- throws Exception {
- mCertificateTransparencyDownloader.startPublicKeyDownload();
- // Set the failure count to well below the threshold
- mDataStore.setPropertyInt(Config.LOG_LIST_UPDATE_FAILURE_COUNT, 0);
-
- mCertificateTransparencyDownloader.onReceive(
- mContext, makePublicKeyDownloadFailedIntent(DownloadManager.ERROR_HTTP_DATA_ERROR));
-
- assertThat(
- mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
.isEqualTo(1);
- verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt());
- verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus(
- anyInt(), anyInt());
+ verify(mLogger, times(1))
+ .logCTLogListUpdateStateChangedEventWithDownloadStatus(
+ DownloadManager.ERROR_INSUFFICIENT_SPACE,
+ /* failureCount= */ 1);
}
@Test
@@ -269,35 +247,9 @@
}
@Test
- public void testDownloader_metadataDownloadFail_failureThresholdExceeded_logsFailure()
+ public void testDownloader_metadataDownloadFail_logsFailure()
throws Exception {
mCertificateTransparencyDownloader.startMetadataDownload();
- // Set the failure count to just below the threshold
- mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
-
- mCertificateTransparencyDownloader.onReceive(
- mContext,
- makeMetadataDownloadFailedIntent(
- mCompatVersion, DownloadManager.ERROR_INSUFFICIENT_SPACE));
-
- assertThat(
- mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
- verify(mLogger, times(1))
- .logCTLogListUpdateFailedEventWithDownloadStatus(
- DownloadManager.ERROR_INSUFFICIENT_SPACE,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
- }
-
- @Test
- public void testDownloader_metadataDownloadFail_failureThresholdNotMet_doesNotLog()
- throws Exception {
- mCertificateTransparencyDownloader.startMetadataDownload();
- // Set the failure count to well below the threshold
- mDataStore.setPropertyInt(Config.LOG_LIST_UPDATE_FAILURE_COUNT, 0);
mCertificateTransparencyDownloader.onReceive(
mContext,
@@ -308,9 +260,10 @@
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
.isEqualTo(1);
- verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt());
- verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus(
- anyInt(), anyInt());
+ verify(mLogger, times(1))
+ .logCTLogListUpdateStateChangedEventWithDownloadStatus(
+ DownloadManager.ERROR_INSUFFICIENT_SPACE,
+ /* failureCount= */ 1);
}
@Test
@@ -347,13 +300,9 @@
}
@Test
- public void testDownloader_contentDownloadFail_failureThresholdExceeded_logsFailure()
+ public void testDownloader_contentDownloadFail_logsFailure()
throws Exception {
mCertificateTransparencyDownloader.startContentDownload(mCompatVersion);
- // Set the failure count to just below the threshold
- mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
mCertificateTransparencyDownloader.onReceive(
mContext,
@@ -363,32 +312,11 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
- verify(mLogger, times(1))
- .logCTLogListUpdateFailedEventWithDownloadStatus(
- DownloadManager.ERROR_INSUFFICIENT_SPACE,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
- }
-
- @Test
- public void testDownloader_contentDownloadFail_failureThresholdNotMet_doesNotLog()
- throws Exception {
- mCertificateTransparencyDownloader.startContentDownload(mCompatVersion);
- // Set the failure count to well below the threshold
- mDataStore.setPropertyInt(Config.LOG_LIST_UPDATE_FAILURE_COUNT, 0);
-
- mCertificateTransparencyDownloader.onReceive(
- mContext,
- makeContentDownloadFailedIntent(
- mCompatVersion, DownloadManager.ERROR_HTTP_DATA_ERROR));
-
- assertThat(
- mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
.isEqualTo(1);
- verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt());
- verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus(
- anyInt(), anyInt());
+ verify(mLogger, times(1))
+ .logCTLogListUpdateStateChangedEventWithDownloadStatus(
+ DownloadManager.ERROR_INSUFFICIENT_SPACE,
+ /* failureCount= */ 1);
}
@Test
@@ -410,16 +338,12 @@
@Test
public void
- testDownloader_contentDownloadSuccess_noSignatureFound_failureThresholdExceeded_logsSingleFailure()
+ testDownloader_contentDownloadSuccess_noSignatureFound_logsSingleFailure()
throws Exception {
File logListFile = makeLogListFile("456");
File metadataFile = sign(logListFile);
mSignatureVerifier.setPublicKey(mPublicKey);
mCertificateTransparencyDownloader.startMetadataDownload();
- // Set the failure count to just below the threshold
- mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
// Set the public key to be missing
mSignatureVerifier.resetPublicKey();
@@ -431,21 +355,26 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .isEqualTo(1);
verify(mLogger, times(1))
- .logCTLogListUpdateFailedEvent(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_NOT_FOUND,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .logCTLogListUpdateStateChangedEvent(
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_PUBLIC_KEY_NOT_FOUND,
+ /* failureCount= */ 1);
verify(mLogger, never())
- .logCTLogListUpdateFailedEvent(
+ .logCTLogListUpdateStateChangedEvent(
eq(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION),
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_NOT_FOUND),
+ anyInt());
+ verify(mLogger, never())
+ .logCTLogListUpdateStateChangedEvent(
+ eq(
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_VERIFICATION),
anyInt());
}
@Test
public void
- testDownloader_contentDownloadSuccess_wrongSignatureAlgo_failureThresholdExceeded_logsSingleFailure()
+ testDownloader_contentDownloadSuccess_wrongSignatureAlgo_logsSingleFailure()
throws Exception {
// Arrange
File logListFile = makeLogListFile("456");
@@ -455,11 +384,6 @@
KeyPairGenerator instance = KeyPairGenerator.getInstance("EC");
mSignatureVerifier.setPublicKey(instance.generateKeyPair().getPublic());
- // Set the failure count to just below the threshold
- mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
-
// Act
mCertificateTransparencyDownloader.startMetadataDownload();
mCertificateTransparencyDownloader.onReceive(
@@ -471,21 +395,21 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .isEqualTo(1);
verify(mLogger, never())
- .logCTLogListUpdateFailedEvent(
+ .logCTLogListUpdateStateChangedEvent(
eq(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_NOT_FOUND),
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_NOT_FOUND),
anyInt());
verify(mLogger, times(1))
- .logCTLogListUpdateFailedEvent(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .logCTLogListUpdateStateChangedEvent(
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_VERIFICATION,
+ /* failureCount= */ 1);
}
@Test
public void
- testDownloader_contentDownloadSuccess_signatureNotVerified_failureThresholdExceeded_logsSingleFailure()
+ testDownloader_contentDownloadSuccess_signatureNotVerified_logsSingleFailure()
throws Exception {
// Arrange
File logListFile = makeLogListFile("456");
@@ -495,11 +419,6 @@
KeyPairGenerator instance = KeyPairGenerator.getInstance("RSA");
mSignatureVerifier.setPublicKey(instance.generateKeyPair().getPublic());
- // Set the failure count to just below the threshold
- mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
-
// Act
mCertificateTransparencyDownloader.startMetadataDownload();
mCertificateTransparencyDownloader.onReceive(
@@ -511,89 +430,30 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
- verify(mLogger, never())
- .logCTLogListUpdateFailedEvent(
- eq(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_NOT_FOUND),
- anyInt());
- verify(mLogger, times(1))
- .logCTLogListUpdateFailedEvent(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
- }
-
- @Test
- public void
- testDownloader_contentDownloadSuccess_wrongSignature_failureThresholdNotMet_doesNotLog()
- throws Exception {
- File logListFile = makeLogListFile("456");
- File metadataFile = sign(logListFile);
- // Set the key to be deliberately wrong by using diff key pair
- KeyPairGenerator instance = KeyPairGenerator.getInstance("RSA");
- mSignatureVerifier.setPublicKey(instance.generateKeyPair().getPublic());
- // Set the failure count to well below the threshold
- mDataStore.setPropertyInt(Config.LOG_LIST_UPDATE_FAILURE_COUNT, 0);
-
- mCertificateTransparencyDownloader.startMetadataDownload();
- mCertificateTransparencyDownloader.onReceive(
- mContext, makeMetadataDownloadCompleteIntent(mCompatVersion, metadataFile));
- mCertificateTransparencyDownloader.onReceive(
- mContext, makeContentDownloadCompleteIntent(mCompatVersion, logListFile));
-
- assertThat(
- mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
.isEqualTo(1);
verify(mLogger, never())
- .logCTLogListUpdateFailedEvent(
+ .logCTLogListUpdateStateChangedEvent(
eq(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_NOT_FOUND),
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_NOT_FOUND),
anyInt());
verify(mLogger, never())
- .logCTLogListUpdateFailedEvent(
+ .logCTLogListUpdateStateChangedEvent(
eq(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION),
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_PUBLIC_KEY_NOT_FOUND),
anyInt());
- }
-
- @Test
- public void
- testDownloader_contentDownloadSuccess_installFail_failureThresholdExceeded_logsFailure()
- throws Exception {
- File invalidLogListFile = writeToFile("not_a_json_log_list".getBytes());
- File metadataFile = sign(invalidLogListFile);
- mSignatureVerifier.setPublicKey(mPublicKey);
- // Set the failure count to just below the threshold
- mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
-
- mCertificateTransparencyDownloader.startMetadataDownload();
- mCertificateTransparencyDownloader.onReceive(
- mContext, makeMetadataDownloadCompleteIntent(mCompatVersion, metadataFile));
- mCertificateTransparencyDownloader.onReceive(
- mContext, makeContentDownloadCompleteIntent(mCompatVersion, invalidLogListFile));
-
- assertThat(
- mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
verify(mLogger, times(1))
- .logCTLogListUpdateFailedEvent(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_VERSION_ALREADY_EXISTS,
- Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .logCTLogListUpdateStateChangedEvent(
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_VERIFICATION,
+ /* failureCount= */ 1);
}
@Test
public void
- testDownloader_contentDownloadSuccess_installFail_failureThresholdNotMet_doesNotLog()
+ testDownloader_contentDownloadSuccess_installFail_logsFailure()
throws Exception {
File invalidLogListFile = writeToFile("not_a_json_log_list".getBytes());
File metadataFile = sign(invalidLogListFile);
mSignatureVerifier.setPublicKey(mPublicKey);
- // Set the failure count to well below the threshold
- mDataStore.setPropertyInt(Config.LOG_LIST_UPDATE_FAILURE_COUNT, 0);
mCertificateTransparencyDownloader.startMetadataDownload();
mCertificateTransparencyDownloader.onReceive(
@@ -605,9 +465,10 @@
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
.isEqualTo(1);
- verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt());
- verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus(
- anyInt(), anyInt());
+ verify(mLogger, times(1))
+ .logCTLogListUpdateStateChangedEvent(
+ CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_VERSION_ALREADY_EXISTS,
+ /* failureCount= */ 1);
}
@Test
@@ -793,7 +654,7 @@
try (InputStream fileStream = new FileInputStream(file);
OutputStream outputStream = new FileOutputStream(signatureFile)) {
signer.update(fileStream.readAllBytes());
- outputStream.write(signer.sign());
+ outputStream.write(Base64.getEncoder().encode(signer.sign()));
}
return signatureFile;
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index eedf427..9b3c7ba 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -397,6 +397,16 @@
return mFactory.hasInterface(iface);
}
+ private List<String> getAllInterfaces() {
+ final ArrayList<String> interfaces = new ArrayList<>(
+ List.of(mFactory.getAvailableInterfaces(/* includeRestricted */ true)));
+
+ if (mTetheringInterfaceMode == INTERFACE_MODE_SERVER && mTetheringInterface != null) {
+ interfaces.add(mTetheringInterface);
+ }
+ return interfaces;
+ }
+
String[] getClientModeInterfaces(boolean includeRestricted) {
return mFactory.getAvailableInterfaces(includeRestricted);
}
@@ -459,10 +469,16 @@
public void setIncludeTestInterfaces(boolean include) {
mHandler.post(() -> {
mIncludeTestInterfaces = include;
- if (!include) {
+ if (include) {
+ trackAvailableInterfaces();
+ } else {
removeTestData();
+ // remove all test interfaces
+ for (String iface : getAllInterfaces()) {
+ if (isValidEthernetInterface(iface)) continue;
+ stopTrackingInterface(iface);
+ }
}
- trackAvailableInterfaces();
});
}
@@ -952,17 +968,7 @@
if (mIsEthernetEnabled == enabled) return;
mIsEthernetEnabled = enabled;
-
- // Interface in server mode should also be included.
- ArrayList<String> interfaces =
- new ArrayList<>(
- List.of(mFactory.getAvailableInterfaces(/* includeRestricted */ true)));
-
- if (mTetheringInterfaceMode == INTERFACE_MODE_SERVER) {
- interfaces.add(mTetheringInterface);
- }
-
- for (String iface : interfaces) {
+ for (String iface : getAllInterfaces()) {
setInterfaceUpState(iface, enabled);
}
broadcastEthernetStateChange(mIsEthernetEnabled);
diff --git a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java
index 1afe3b8..f17a7ec 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java
@@ -357,7 +357,8 @@
// interface, including change in administrative state. While RTM_SETLINK is used to
// modify an existing link rather than creating a new one.
return RtNetlinkLinkMessage.build(
- new StructNlMsgHdr(/*payloadLen*/ 0, RTM_NEWLINK, NLM_F_REQUEST, sequenceNumber),
+ new StructNlMsgHdr(
+ /*payloadLen*/ 0, RTM_NEWLINK, NLM_F_REQUEST_ACK, sequenceNumber),
new StructIfinfoMsg((short) AF_UNSPEC, /*type*/ 0, interfaceIndex,
flagsBits, changeBits),
DEFAULT_MTU, /*hardwareAddress*/ null, /*interfaceName*/ null);
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkLinkMessageTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkLinkMessageTest.java
index 8104e3a..b29fc73 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkLinkMessageTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkLinkMessageTest.java
@@ -308,7 +308,7 @@
@Test
public void testCreateSetInterfaceFlagsMessage() {
final String expectedHexBytes =
- "20000000100001006824000000000000" // struct nlmsghdr
+ "20000000100005006824000000000000" // struct nlmsghdr
+ "00000000080000000100000001000100"; // struct ifinfomsg
final String interfaceName = "wlan0";
final int interfaceIndex = 8;
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 5b2c9f7..57bc2be 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -1069,6 +1069,9 @@
@Test
fun testSetTetheringInterfaceMode_disableEnableEthernet() {
+ // do not run this test if an interface that can be used for tethering already exists.
+ assumeNoInterfaceForTetheringAvailable()
+
val listener = EthernetStateListener()
addInterfaceStateListener(listener)
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 286e08c..4ba41cd 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -722,9 +722,15 @@
val cv = ConditionVariable()
val cpb = PrivilegeWaiterCallback(cv)
- tryTest {
+ // The lambda below is capturing |cpb|, whose type inherits from a class that appeared in
+ // T. This means the lambda will compile as a private method of this class taking a
+ // PrivilegeWaiterCallback argument. As JUnit uses reflection to enumerate all methods
+ // including private methods, this would fail with a link error when running on S-.
+ // To solve this, make the lambda serializable, which causes the compiler to emit a
+ // synthetic class instead of a synthetic method.
+ tryTest @JvmSerializableLambda {
val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
- runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
}
// Wait for the callback to be registered
@@ -747,8 +753,8 @@
carrierConfigRule.cleanUpNow()
}
assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't change carrier privilege")
- } cleanup {
- runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ } cleanup @JvmSerializableLambda {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
tm.unregisterCarrierPrivilegesCallback(cpb)
}
}
@@ -762,9 +768,15 @@
val cv = ConditionVariable()
val cpb = CarrierServiceChangedWaiterCallback(cv)
- tryTest {
+ // The lambda below is capturing |cpb|, whose type inherits from a class that appeared in
+ // T. This means the lambda will compile as a private method of this class taking a
+ // PrivilegeWaiterCallback argument. As JUnit uses reflection to enumerate all methods
+ // including private methods, this would fail with a link error when running on S-.
+ // To solve this, make the lambda serializable, which causes the compiler to emit a
+ // synthetic class instead of a synthetic method.
+ tryTest @JvmSerializableLambda {
val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
- runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
}
// Wait for the callback to be registered
@@ -786,8 +798,8 @@
}
}
assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't modify carrier service package")
- } cleanup {
- runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ } cleanup @JvmSerializableLambda {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
tm.unregisterCarrierPrivilegesCallback(cpb)
}
}
diff --git a/tests/native/connectivity_native_test/Android.bp b/tests/native/connectivity_native_test/Android.bp
index 39a08fa..02ac3c5 100644
--- a/tests/native/connectivity_native_test/Android.bp
+++ b/tests/native/connectivity_native_test/Android.bp
@@ -24,7 +24,6 @@
"libcom.android.tethering.connectivity_native",
"liblog",
"libnetutils",
- "libprocessgroup",
],
static_libs: [
"connectivity_native_aidl_interface-lateststable-ndk",
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
index b7cfaf9..533bbf8 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
@@ -79,6 +79,7 @@
initMockResources();
doReturn(false).when(mFactory).updateInterfaceLinkState(anyString(), anyBoolean());
doReturn(new String[0]).when(mNetd).interfaceGetList();
+ doReturn(new String[0]).when(mFactory).getAvailableInterfaces(anyBoolean());
mHandlerThread = new HandlerThread(THREAD_NAME);
mHandlerThread.start();
tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd,