Merge "Un-drop support for pre-4.14 kernels."
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index ebc9d26..6a5089d 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -403,6 +403,18 @@
return null;
}
}
+
+ /** Get error BPF map. */
+ @Nullable public IBpfMap<S32, S32> getBpfErrorMap() {
+ if (!isAtLeastS()) return null;
+ try {
+ return new BpfMap<>(TETHER_ERROR_MAP_PATH,
+ BpfMap.BPF_F_RDONLY, S32.class, S32.class);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot create error map: " + e);
+ return null;
+ }
+ }
}
@VisibleForTesting
@@ -1287,13 +1299,15 @@
}
private void dumpCounters(@NonNull IndentingPrintWriter pw) {
- if (!mDeps.isAtLeastS()) {
- pw.println("No counter support");
- return;
- }
- try (IBpfMap<S32, S32> map = new BpfMap<>(TETHER_ERROR_MAP_PATH, BpfMap.BPF_F_RDONLY,
- S32.class, S32.class)) {
-
+ try (IBpfMap<S32, S32> map = mDeps.getBpfErrorMap()) {
+ if (map == null) {
+ pw.println("No error counter support");
+ return;
+ }
+ if (map.isEmpty()) {
+ pw.println("<empty>");
+ return;
+ }
map.forEach((k, v) -> {
String counterName;
try {
@@ -1307,7 +1321,7 @@
if (v.val > 0) pw.println(String.format("%s: %d", counterName, v.val));
});
} catch (ErrnoException | IOException e) {
- pw.println("Error dumping counter map: " + e);
+ pw.println("Error dumping error counter map: " + e);
}
}
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 5f4454b..f0d9057 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -53,6 +53,7 @@
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -99,6 +100,7 @@
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.Struct.S32;
import com.android.net.module.util.bpf.Tether4Key;
import com.android.net.module.util.bpf.Tether4Value;
import com.android.net.module.util.bpf.TetherStatsKey;
@@ -108,6 +110,7 @@
import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEvent;
import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEventConsumer;
import com.android.networkstack.tethering.BpfCoordinator;
+import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
import com.android.networkstack.tethering.Tether6Value;
@@ -197,6 +200,7 @@
@Mock private BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
@Mock private BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
@Mock private BpfMap<TetherDevKey, TetherDevValue> mBpfDevMap;
+ @Mock private BpfMap<S32, S32> mBpfErrorMap;
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
@@ -360,6 +364,11 @@
public BpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() {
return mBpfDevMap;
}
+
+ @Nullable
+ public BpfMap<S32, S32> getBpfErrorMap() {
+ return mBpfErrorMap;
+ }
};
mBpfCoordinator = spy(new BpfCoordinator(mBpfDeps));
@@ -1520,4 +1529,56 @@
verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(
mIpServer, makeForwardingRule(IPSEC_IFINDEX, neigh, mac));
}
+
+ // TODO: move to BpfCoordinatorTest once IpNeighborMonitor is migrated to BpfCoordinator.
+ @Test
+ public void addRemoveTetherClient() throws Exception {
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
+ DEFAULT_USING_BPF_OFFLOAD);
+
+ final int myIfindex = TEST_IFACE_PARAMS.index;
+ final int notMyIfindex = myIfindex - 1;
+
+ final InetAddress neighA = InetAddresses.parseNumericAddress("192.168.80.1");
+ final InetAddress neighB = InetAddresses.parseNumericAddress("192.168.80.2");
+ final InetAddress neighLL = InetAddresses.parseNumericAddress("169.254.0.1");
+ final InetAddress neighMC = InetAddresses.parseNumericAddress("224.0.0.1");
+ final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
+ final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
+ final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
+
+ // Events on other interfaces are ignored.
+ recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA);
+ verifyNoMoreInteractions(mBpfCoordinator);
+
+ // Events on this interface are received and sent to BpfCoordinator.
+ recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+ verify(mBpfCoordinator).tetherOffloadClientAdd(mIpServer, new ClientInfo(myIfindex,
+ TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighA, macA));
+ clearInvocations(mBpfCoordinator);
+
+ recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+ verify(mBpfCoordinator).tetherOffloadClientAdd(mIpServer, new ClientInfo(myIfindex,
+ TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighB, macB));
+ clearInvocations(mBpfCoordinator);
+
+ // Link-local and multicast neighbors are ignored.
+ recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA);
+ verifyNoMoreInteractions(mBpfCoordinator);
+ recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA);
+ verifyNoMoreInteractions(mBpfCoordinator);
+ clearInvocations(mBpfCoordinator);
+
+ // A neighbor that is no longer valid causes the client to be removed.
+ // NUD_FAILED events do not have a MAC address.
+ recvNewNeigh(myIfindex, neighA, NUD_FAILED, null);
+ verify(mBpfCoordinator).tetherOffloadClientRemove(mIpServer, new ClientInfo(myIfindex,
+ TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighA, macNull));
+ clearInvocations(mBpfCoordinator);
+
+ // A neighbor that is deleted causes the client to be removed.
+ recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
+ verify(mBpfCoordinator).tetherOffloadClientRemove(mIpServer, new ClientInfo(myIfindex,
+ TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighB, macNull));
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index bbca565..0bd6380 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -94,11 +94,13 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.Struct.S32;
import com.android.net.module.util.bpf.Tether4Key;
import com.android.net.module.util.bpf.Tether4Value;
import com.android.net.module.util.bpf.TetherStatsKey;
@@ -128,6 +130,7 @@
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
+import java.io.StringWriter;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -362,9 +365,6 @@
@Mock private IpServer mIpServer2;
@Mock private TetheringConfiguration mTetherConfig;
@Mock private ConntrackMonitor mConntrackMonitor;
- @Mock private IBpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map;
- @Mock private IBpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map;
- @Mock private IBpfMap<TetherDevKey, TetherDevValue> mBpfDevMap;
// Late init since methods must be called by the thread that created this object.
private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
@@ -383,10 +383,18 @@
spy(new TestBpfMap<>(Tether4Key.class, Tether4Value.class));
private final IBpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map =
spy(new TestBpfMap<>(Tether4Key.class, Tether4Value.class));
+ private final IBpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map =
+ spy(new TestBpfMap<>(TetherDownstream6Key.class, Tether6Value.class));
+ private final IBpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map =
+ spy(new TestBpfMap<>(TetherUpstream6Key.class, Tether6Value.class));
private final IBpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap =
spy(new TestBpfMap<>(TetherStatsKey.class, TetherStatsValue.class));
private final IBpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap =
spy(new TestBpfMap<>(TetherLimitKey.class, TetherLimitValue.class));
+ private final IBpfMap<TetherDevKey, TetherDevValue> mBpfDevMap =
+ spy(new TestBpfMap<>(TetherDevKey.class, TetherDevValue.class));
+ private final IBpfMap<S32, S32> mBpfErrorMap =
+ spy(new TestBpfMap<>(S32.class, S32.class));
private BpfCoordinator.Dependencies mDeps =
spy(new BpfCoordinator.Dependencies() {
@NonNull
@@ -457,6 +465,11 @@
public IBpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() {
return mBpfDevMap;
}
+
+ @Nullable
+ public IBpfMap<S32, S32> getBpfErrorMap() {
+ return mBpfErrorMap;
+ }
});
@Before public void setUp() {
@@ -2099,4 +2112,88 @@
assertEquals("upstreamIfindex: 1001, downstreamIfindex: 1003, address: 2001:db8::1, "
+ "srcMac: 12:34:56:78:90:ab, dstMac: 00:00:00:00:00:0a", rule.toString());
}
+
+ private void verifyDump(@NonNull final BpfCoordinator coordinator) {
+ final StringWriter stringWriter = new StringWriter();
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(stringWriter, " ");
+ coordinator.dump(ipw);
+ assertFalse(stringWriter.toString().isEmpty());
+ }
+
+ @Test
+ public void testDumpDoesNotCrash() throws Exception {
+ // This dump test only used to for improving mainline module test coverage and doesn't
+ // really do any meaningful tests.
+ // TODO: consider verifying the dump content and separate tests into testDumpXXX().
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+
+ // [1] Dump mostly empty content.
+ verifyDump(coordinator);
+
+ // [2] Dump mostly non-empty content.
+ // Test the following dump function and fill the corresponding content to execute
+ // code as more as possible for test coverage.
+ // - dumpBpfForwardingRulesIpv4
+ // * mBpfDownstream4Map
+ // * mBpfUpstream4Map
+ // - dumpBpfForwardingRulesIpv6
+ // * mBpfDownstream6Map
+ // * mBpfUpstream6Map
+ // - dumpStats
+ // * mBpfStatsMap
+ // - dumpDevmap
+ // * mBpfDevMap
+ // - dumpCounters
+ // * mBpfErrorMap
+ // - dumpIpv6ForwardingRulesByDownstream
+ // * mIpv6ForwardingRules
+
+ // dumpBpfForwardingRulesIpv4
+ mBpfDownstream4Map.insertEntry(
+ new TestDownstream4Key.Builder().build(),
+ new TestDownstream4Value.Builder().build());
+ mBpfUpstream4Map.insertEntry(
+ new TestUpstream4Key.Builder().build(),
+ new TestUpstream4Value.Builder().build());
+
+ // dumpBpfForwardingRulesIpv6
+ final Ipv6ForwardingRule rule = buildTestForwardingRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A);
+ mBpfDownstream6Map.insertEntry(rule.makeTetherDownstream6Key(), rule.makeTether6Value());
+
+ final TetherUpstream6Key upstream6Key = new TetherUpstream6Key(DOWNSTREAM_IFINDEX,
+ DOWNSTREAM_MAC);
+ final Tether6Value upstream6Value = new Tether6Value(UPSTREAM_IFINDEX,
+ MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
+ ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
+ mBpfUpstream6Map.insertEntry(upstream6Key, upstream6Value);
+
+ // dumpStats
+ mBpfStatsMap.insertEntry(
+ new TetherStatsKey(UPSTREAM_IFINDEX),
+ new TetherStatsValue(
+ 0L /* rxPackets */, 0L /* rxBytes */, 0L /* rxErrors */,
+ 0L /* txPackets */, 0L /* txBytes */, 0L /* txErrors */));
+
+ // dumpDevmap
+ coordinator.addUpstreamNameToLookupTable(UPSTREAM_IFINDEX, UPSTREAM_IFACE);
+ mBpfDevMap.insertEntry(
+ new TetherDevKey(UPSTREAM_IFINDEX),
+ new TetherDevValue(UPSTREAM_IFINDEX));
+
+ // dumpCounters
+ // The error code is defined in packages/modules/Connectivity/bpf_progs/bpf_tethering.h.
+ mBpfErrorMap.insertEntry(
+ new S32(0 /* INVALID_IPV4_VERSION */),
+ new S32(1000 /* count */));
+
+ // dumpIpv6ForwardingRulesByDownstream
+ final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
+ ipv6ForwardingRules = coordinator.getForwardingRulesForTesting();
+ final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> addressRuleMap =
+ new LinkedHashMap<>();
+ addressRuleMap.put(rule.address, rule);
+ ipv6ForwardingRules.put(mIpServer, addressRuleMap);
+
+ verifyDump(coordinator);
+ }
}
diff --git a/nearby/halfsheet/Android.bp b/nearby/halfsheet/Android.bp
index 486a3ff..c84caa6 100644
--- a/nearby/halfsheet/Android.bp
+++ b/nearby/halfsheet/Android.bp
@@ -23,7 +23,6 @@
sdk_version: "module_current",
// This is included in tethering apex, which uses min SDK 30
min_sdk_version: "30",
- target_sdk_version: "current",
updatable: true,
certificate: ":com.android.nearby.halfsheetcertificate",
libs: [
diff --git a/service/ServiceConnectivityResources/Android.bp b/service/ServiceConnectivityResources/Android.bp
index 02b2875..2260596 100644
--- a/service/ServiceConnectivityResources/Android.bp
+++ b/service/ServiceConnectivityResources/Android.bp
@@ -21,9 +21,8 @@
android_app {
name: "ServiceConnectivityResources",
- sdk_version: "module_30",
+ sdk_version: "module_current",
min_sdk_version: "30",
- target_sdk_version: "33",
resource_dirs: [
"res",
],
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 195f046..6ef239d 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -7500,7 +7500,7 @@
}
private void updateLinkProperties(NetworkAgentInfo networkAgent, @NonNull LinkProperties newLp,
- @NonNull LinkProperties oldLp) {
+ @Nullable LinkProperties oldLp) {
int netId = networkAgent.network.getNetId();
// The NetworkAgent does not know whether clatd is running on its network or not, or whether
@@ -7552,7 +7552,13 @@
}
// Start or stop DNS64 detection and 464xlat according to network state.
networkAgent.clatd.update();
- notifyIfacesChangedForNetworkStats();
+ // Notify NSS when relevant events happened. Currently, NSS only cares about
+ // interface changed to update clat interfaces accounting.
+ final boolean interfacesChanged = oldLp == null
+ || !Objects.equals(newLp.getAllInterfaceNames(), oldLp.getAllInterfaceNames());
+ if (interfacesChanged) {
+ notifyIfacesChangedForNetworkStats();
+ }
networkAgent.networkMonitor().notifyLinkPropertiesChanged(
new LinkProperties(newLp, true /* parcelSensitiveFields */));
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
@@ -8315,7 +8321,8 @@
}
}
- public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
+ public void handleUpdateLinkProperties(@NonNull NetworkAgentInfo nai,
+ @NonNull LinkProperties newLp) {
ensureRunningOnConnectivityServiceThread();
if (!mNetworkAgentInfos.contains(nai)) {
diff --git a/service/src/com/android/server/connectivity/Nat464Xlat.java b/service/src/com/android/server/connectivity/Nat464Xlat.java
index 4e19781..2ac2ad3 100644
--- a/service/src/com/android/server/connectivity/Nat464Xlat.java
+++ b/service/src/com/android/server/connectivity/Nat464Xlat.java
@@ -22,6 +22,7 @@
import static com.android.net.module.util.CollectionUtils.contains;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.ConnectivityManager;
import android.net.IDnsResolver;
import android.net.INetd;
@@ -420,7 +421,7 @@
* This is necessary because the LinkProperties in mNetwork come from the transport layer, which
* has no idea that 464xlat is running on top of it.
*/
- public void fixupLinkProperties(@NonNull LinkProperties oldLp, @NonNull LinkProperties lp) {
+ public void fixupLinkProperties(@Nullable LinkProperties oldLp, @NonNull LinkProperties lp) {
// This must be done even if clatd is not running, because otherwise shouldStartClat would
// never return true.
lp.setNat64Prefix(selectNat64Prefix());
@@ -433,6 +434,8 @@
}
Log.d(TAG, "clatd running, updating NAI for " + mIface);
+ // oldLp can't be null here since shouldStartClat checks null LinkProperties to start clat.
+ // Thus, the status won't pass isRunning check if the oldLp is null.
for (LinkProperties stacked: oldLp.getStackedLinks()) {
if (Objects.equals(mIface, stacked.getInterfaceName())) {
lp.addStackedLink(stacked);
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 2d8cf80..65e0b10 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -7236,6 +7236,15 @@
expectNotifyNetworkStatus(onlyCell(), onlyCell(), MOBILE_IFNAME);
reset(mStatsManager);
+ // Verify change fields other than interfaces does not trigger a notification to NSS.
+ cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24"));
+ cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"),
+ MOBILE_IFNAME));
+ cellLp.setDnsServers(List.of(InetAddress.getAllByName("8.8.8.8")));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ verifyNoMoreInteractions(mStatsManager);
+ reset(mStatsManager);
+
// Default network switch should update ifaces.
mWiFiNetworkAgent.connect(false);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 39fd780..ff771f6 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -93,6 +93,7 @@
import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.Network;
+import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState;
@@ -1744,7 +1745,7 @@
throws Exception {
doReturn(mMockNetworkAgent).when(mTestDeps)
.newNetworkAgent(
- any(), any(), anyString(), any(), any(), any(), any(), any());
+ any(), any(), anyString(), any(), any(), any(), any(), any(), any());
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
@@ -1774,7 +1775,7 @@
ArgumentCaptor.forClass(NetworkAgentConfig.class);
verify(mTestDeps).newNetworkAgent(
any(), any(), anyString(), ncCaptor.capture(), lpCaptor.capture(),
- any(), nacCaptor.capture(), any());
+ any(), nacCaptor.capture(), any(), any());
// Check LinkProperties
final LinkProperties lp = lpCaptor.getValue();
@@ -1968,7 +1969,7 @@
}
@Test
- public void testDataStallInIkev2VpnMobikeEnabled() throws Exception {
+ public void testDataStallInIkev2VpnRecoveredByMobike() throws Exception {
final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
@@ -1980,6 +1981,64 @@
// Verify MOBIKE is triggered
verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
+
+ // Expect to skip other data stall event if MOBIKE was started.
+ reset(mIkeSessionWrapper);
+ connectivityDiagCallback.onDataStallSuspected(report);
+ verify(mIkeSessionWrapper, never()).setNetwork(any());
+
+ reset(mIkev2SessionCreator);
+
+ // Send validation status update.
+ // Recovered and get network validated. It should not trigger the ike session reset.
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_VALID);
+ verify(mIkev2SessionCreator, never()).createIkeSession(
+ any(), any(), any(), any(), any(), any());
+
+ // Send invalid result to verify no ike session reset since the data stall suspected
+ // variables(timer counter and boolean) was reset.
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mExecutor).schedule(runnableCaptor.capture(), anyLong(), any());
+ runnableCaptor.getValue().run();
+ verify(mIkev2SessionCreator, never()).createIkeSession(
+ any(), any(), any(), any(), any(), any());
+ }
+
+ @Test
+ public void testDataStallInIkev2VpnNotRecoveredByMobike() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+
+ final ConnectivityDiagnosticsCallback connectivityDiagCallback =
+ getConnectivityDiagCallback();
+
+ doReturn(TEST_NETWORK).when(mMockNetworkAgent).getNetwork();
+ final DataStallReport report = createDataStallReport();
+ connectivityDiagCallback.onDataStallSuspected(report);
+
+ verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
+
+ reset(mIkev2SessionCreator);
+
+ // Send validation status update should result in ike session reset.
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+
+ // Verify reset is scheduled and run.
+ final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mExecutor).schedule(runnableCaptor.capture(), anyLong(), any());
+
+ // Another invalid status reported should not trigger other scheduled recovery.
+ reset(mExecutor);
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ verify(mExecutor, never()).schedule(runnableCaptor.capture(), anyLong(), any());
+
+ runnableCaptor.getValue().run();
+ verify(mIkev2SessionCreator).createIkeSession(any(), any(), any(), any(), any(), any());
}
@Test
diff --git a/tools/gn2bp/Android.bp.swp b/tools/gn2bp/Android.bp.swp
new file mode 100644
index 0000000..9a394e7
--- /dev/null
+++ b/tools/gn2bp/Android.bp.swp
@@ -0,0 +1,131 @@
+// Copyright (C) 2022 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.
+//
+// This file is automatically generated by gen_android_bp. Do not edit.
+
+// GN: //gn:default_deps
+cc_defaults {
+ name: "cronet_aml_defaults",
+ cflags: [
+ "-O2",
+ "-Wno-error=return-type",
+ "-Wno-sign-compare",
+ "-Wno-sign-promo",
+ "-Wno-unused-parameter",
+ "-fvisibility=hidden",
+ ],
+}
+
+// GN: //third_party/android_ndk:cpu_features
+filegroup {
+ name: "cronet_aml_third_party_android_ndk_cpu_features",
+ srcs: [
+ "third_party/android_ndk/sources/android/cpufeatures/cpu-features.c",
+ ],
+}
+
+// GN: //third_party/libevent:libevent
+cc_library_static {
+ name: "cronet_aml_third_party_libevent_libevent",
+ srcs: [
+ "third_party/libevent/buffer.c",
+ "third_party/libevent/epoll.c",
+ "third_party/libevent/evbuffer.c",
+ "third_party/libevent/evdns.c",
+ "third_party/libevent/event.c",
+ "third_party/libevent/event_tagging.c",
+ "third_party/libevent/evrpc.c",
+ "third_party/libevent/evutil.c",
+ "third_party/libevent/http.c",
+ "third_party/libevent/log.c",
+ "third_party/libevent/poll.c",
+ "third_party/libevent/select.c",
+ "third_party/libevent/signal.c",
+ "third_party/libevent/strlcpy.c",
+ ],
+ defaults: [
+ "cronet_aml_defaults",
+ ],
+ cflags: [
+ "-DANDROID",
+ "-DANDROID_NDK_VERSION_ROLL=r23_1",
+ "-DCR_CLANG_REVISION=\"llvmorg-16-init-8189-g97196a2d-1\"",
+ "-DCR_LIBCXX_REVISION=166132d607a5b33487d523bd9cc2ba9176ddffef",
+ "-DDCHECK_ALWAYS_ON=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DHAVE_CONFIG_H",
+ "-DHAVE_SYS_UIO_H",
+ "-D_DEBUG",
+ "-D_GNU_SOURCE",
+ "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+ "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+ ],
+ local_include_dirs: [
+ "./",
+ "buildtools/third_party/libc++/",
+ "out/test/gen/",
+ "third_party/libevent/android/",
+ ],
+}
+
+// GN: //third_party/zlib:zlib
+cc_library_static {
+ name: "cronet_aml_third_party_zlib_zlib",
+ srcs: [
+ ":cronet_aml_third_party_android_ndk_cpu_features",
+ "third_party/zlib/adler32.c",
+ "third_party/zlib/compress.c",
+ "third_party/zlib/cpu_features.c",
+ "third_party/zlib/crc32.c",
+ "third_party/zlib/deflate.c",
+ "third_party/zlib/gzclose.c",
+ "third_party/zlib/gzlib.c",
+ "third_party/zlib/gzread.c",
+ "third_party/zlib/gzwrite.c",
+ "third_party/zlib/infback.c",
+ "third_party/zlib/inffast.c",
+ "third_party/zlib/inflate.c",
+ "third_party/zlib/inftrees.c",
+ "third_party/zlib/trees.c",
+ "third_party/zlib/uncompr.c",
+ "third_party/zlib/zutil.c",
+ ],
+ defaults: [
+ "cronet_aml_defaults",
+ ],
+ cflags: [
+ "-DANDROID",
+ "-DANDROID_NDK_VERSION_ROLL=r23_1",
+ "-DCPU_NO_SIMD",
+ "-DCR_CLANG_REVISION=\"llvmorg-16-init-8189-g97196a2d-1\"",
+ "-DCR_LIBCXX_REVISION=166132d607a5b33487d523bd9cc2ba9176ddffef",
+ "-DDCHECK_ALWAYS_ON=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DHAVE_SYS_UIO_H",
+ "-DZLIB_DEBUG",
+ "-DZLIB_IMPLEMENTATION",
+ "-D_DEBUG",
+ "-D_GNU_SOURCE",
+ "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+ "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+ ],
+ local_include_dirs: [
+ "./",
+ "buildtools/third_party/libc++/",
+ "out/test/gen/",
+ "third_party/android_ndk/sources/android/cpufeatures/",
+ "third_party/zlib/",
+ ],
+}
+
diff --git a/tools/gn2bp/gen_android_bp b/tools/gn2bp/gen_android_bp
index cacb051..1422b69 100755
--- a/tools/gn2bp/gen_android_bp
+++ b/tools/gn2bp/gen_android_bp
@@ -28,6 +28,7 @@
import argparse
import collections
import json
+import logging as log
import os
import re
import sys
@@ -98,9 +99,6 @@
# Compiler flags which are passed through to the blueprint.
cflag_allowlist = r'^-DPERFETTO.*$'
-# Compiler defines which are passed through to the blueprint.
-define_allowlist = r'^(GOOGLE_PROTO.*)|(ZLIB_.*)|(USE_MMAP)|(HAVE_HIDDEN)$'
-
# Additional arguments to apply to Android.bp rules.
additional_args = {
# TODO: remove if this is not useful for the cronet build.
@@ -341,7 +339,7 @@
self.local_include_dirs = set()
self.header_libs = set()
self.required = set()
- self.tool_files = None
+ self.tool_files = []
self.android = Target('android')
self.host = Target('host')
self.stl = None
@@ -670,12 +668,38 @@
blueprint.add_module(module)
+def create_action_module(blueprint, target):
+ bp_module_name = label_to_module_name(target.name)
+ module = Module('genrule', bp_module_name, target.name)
+ script = gn_utils.label_to_path(target.script)
+ module.tool_files = [script]
+
+ arg_string = ' '.join(target.args)
+ module.cmd = '$(location %s) %s' % (script, arg_string)
+
+ if all(os.path.splitext(it)[1] == '.h' for it in target.outputs):
+ module.genrule_headers.add(bp_module_name)
+
+ # For gn actions, sources and inputs are treated equally.
+ # TODO: there should be a label_to_path function that takes a set / list.
+ module.srcs.update(gn_utils.label_to_path(it) for it in target.inputs)
+ module.srcs.update(gn_utils.label_to_path(it) for it in target.sources)
+
+ # Actions using template "action_with_pydeps" also put script inside inputs.
+ # TODO: it might make sense to filter inputs inside GnParser.
+ if script in module.srcs:
+ module.srcs.remove(script)
+
+ module.out.update(target.outputs)
+ blueprint.add_module(module)
+ return module
+
+
def _get_cflags(target):
cflags = {flag for flag in target.cflags if re.match(cflag_allowlist, flag)}
- cflags |= set("-D%s" % define
- for define in target.defines
- if re.match(define_allowlist, define))
+ # Consider proper allowlist or denylist if needed
+ cflags |= set("-D%s" % define.replace("\"", "\\\"") for define in target.defines)
return cflags
@@ -694,6 +718,7 @@
if bp_module_name in blueprint.modules:
return blueprint.modules[bp_module_name]
target = gn.get_target(gn_target_name)
+ log.info('create modules for %s (%s)', target.name, target.type)
name_without_toolchain = gn_utils.label_without_toolchain(target.name)
if target.type == 'executable':
@@ -727,7 +752,13 @@
name_without_toolchain == gn_utils.GEN_VERSION_TARGET:
module = create_gen_version_module(blueprint, target, bp_module_name)
else:
- raise Error('Unhandled action: {}'.format(target.name))
+ module = create_action_module(blueprint, target)
+ elif target.type == 'copy':
+ # TODO: careful now! copy targets are not supported yet, but this will stop
+ # traversing the dependency tree. For //base:base, this is not a big
+ # problem as libicu contains the only copy target which happens to be a
+ # leaf node.
+ return None
else:
raise Error('Unknown target %s (%s)' % (target.name, target.type))
@@ -783,17 +814,10 @@
# dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
all_deps = target.deps | target.source_set_deps | target.transitive_proto_deps
for dep_name in all_deps:
- # If the dependency refers to a library which we can replace with an
- # Android equivalent, stop recursing and patch the dependency in.
- # Don't recurse into //buildtools, builtin_deps are intercepted at
- # the //gn:xxx level.
- if dep_name.startswith('//buildtools'):
- continue
-
- # Ignore the dependency on the gen_buildflags genrule. That is run
- # separately in this generator and the generated file is copied over
- # into the repo (see usage of |buildflags_dir| in this script).
- if dep_name.startswith(gn_utils.BUILDFLAGS_TARGET):
+ # |builtin_deps| override GN deps with Android-specific ones. See the
+ # config in the top of this file.
+ if gn_utils.label_without_toolchain(dep_name) in builtin_deps:
+ builtin_deps[gn_utils.label_without_toolchain(dep_name)](module)
continue
dep_module = create_modules_from_target(blueprint, gn, dep_name)
@@ -802,11 +826,6 @@
if not module_is_compiled:
continue
- # |builtin_deps| override GN deps with Android-specific ones. See the
- # config in the top of this file.
- if gn_utils.label_without_toolchain(dep_name) in builtin_deps:
- builtin_deps[gn_utils.label_without_toolchain(dep_name)](module)
- continue
# Don't recurse in any other //gn dep if not handled by builtin_deps.
if dep_name.startswith('//gn:'):
@@ -873,12 +892,21 @@
default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
)
parser.add_argument(
+ '-v',
+ '--verbose',
+ help='Print debug logs.',
+ action='store_true',
+ )
+ parser.add_argument(
'targets',
nargs=argparse.REMAINDER,
help='Targets to include in the blueprint (e.g., "//:perfetto_tests")'
)
args = parser.parse_args()
+ if args.verbose:
+ log.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', level=log.DEBUG)
+
with open(args.desc) as f:
desc = json.load(f)
diff --git a/tools/gn2bp/gn_utils.py b/tools/gn2bp/gn_utils.py
index 95f677a..6cf5b7d 100644
--- a/tools/gn2bp/gn_utils.py
+++ b/tools/gn2bp/gn_utils.py
@@ -39,114 +39,12 @@
}
-def _check_command_output(cmd, cwd):
- try:
- output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, cwd=cwd)
- except subprocess.CalledProcessError as e:
- print(
- 'Command "{}" failed in {}:'.format(' '.join(cmd), cwd),
- file=sys.stderr)
- print(e.output.decode(), file=sys.stderr)
- sys.exit(1)
- else:
- return output.decode()
-
-
def repo_root():
"""Returns an absolute path to the repository root."""
return os.path.join(
os.path.realpath(os.path.dirname(__file__)), os.path.pardir)
-def _tool_path(name, system_buildtools=False):
- # Pass-through to use name if the caller requests to use the system
- # toolchain.
- if system_buildtools:
- return [name]
- wrapper = os.path.abspath(
- os.path.join(repo_root(), 'tools', 'run_buildtools_binary.py'))
- return ['python3', wrapper, name]
-
-
-def prepare_out_directory(gn_args,
- name,
- root=repo_root(),
- system_buildtools=False):
- """Creates the JSON build description by running GN.
-
- Returns (path, desc) where |path| is the location of the output directory
- and |desc| is the JSON build description.
- """
- out = os.path.join(root, 'out', name)
- try:
- os.makedirs(out)
- except OSError as e:
- if e.errno != errno.EEXIST:
- raise
- _check_command_output(
- _tool_path('gn', system_buildtools) +
- ['gen', out, '--args=%s' % gn_args],
- cwd=repo_root())
- return out
-
-
-def load_build_description(out, system_buildtools=False):
- """Creates the JSON build description by running GN."""
- desc = _check_command_output(
- _tool_path('gn', system_buildtools) +
- ['desc', out, '--format=json', '--all-toolchains', '//*'],
- cwd=repo_root())
- return json.loads(desc)
-
-
-def create_build_description(gn_args, root=repo_root()):
- """Prepares a GN out directory and loads the build description from it.
-
- The temporary out directory is automatically deleted.
- """
- out = prepare_out_directory(gn_args, 'tmp.gn_utils', root=root)
- try:
- return load_build_description(out)
- finally:
- shutil.rmtree(out)
-
-
-def build_targets(out, targets, quiet=False, system_buildtools=False):
- """Runs ninja to build a list of GN targets in the given out directory.
-
- Compiling these targets is required so that we can include any generated
- source files in the amalgamated result.
- """
- targets = [t.replace('//', '') for t in targets]
- with open(os.devnull, 'w') as devnull:
- stdout = devnull if quiet else None
- cmd = _tool_path('ninja', system_buildtools) + targets
- subprocess.check_call(cmd, cwd=os.path.abspath(out), stdout=stdout)
-
-
-def compute_source_dependencies(out, system_buildtools=False):
- """For each source file, computes a set of headers it depends on."""
- ninja_deps = _check_command_output(
- _tool_path('ninja', system_buildtools) + ['-t', 'deps'], cwd=out)
- deps = {}
- current_source = None
- for line in ninja_deps.split('\n'):
- filename = os.path.relpath(os.path.join(out, line.strip()), repo_root())
- if not line or line[0] != ' ':
- current_source = None
- continue
- elif not current_source:
- # We're assuming the source file is always listed before the
- # headers.
- assert os.path.splitext(line)[1] in ['.c', '.cc', '.cpp', '.S']
- current_source = filename
- deps[current_source] = []
- else:
- assert current_source
- deps[current_source].append(filename)
- return deps
-
-
def label_to_path(label):
"""Turn a GN output label (e.g., //some_dir/file.cc) into a path."""
assert label.startswith('//')
@@ -172,115 +70,6 @@
return name
-def gen_buildflags(gn_args, target_file):
- """Generates the perfetto_build_flags.h for the given config.
-
- target_file: the path, relative to the repo root, where the generated
- buildflag header will be copied into.
- """
- tmp_out = prepare_out_directory(gn_args, 'tmp.gen_buildflags')
- build_targets(tmp_out, [BUILDFLAGS_TARGET], quiet=True)
- src = os.path.join(tmp_out, 'gen', 'build_config', 'perfetto_build_flags.h')
- shutil.copy(src, os.path.join(repo_root(), target_file))
- shutil.rmtree(tmp_out)
-
-
-def check_or_commit_generated_files(tmp_files, check):
- """Checks that gen files are unchanged or renames them to the final location
-
- Takes in input a list of 'xxx.swp' files that have been written.
- If check == False, it renames xxx.swp -> xxx.
- If check == True, it just checks that the contents of 'xxx.swp' == 'xxx'.
- Returns 0 if no diff was detected, 1 otherwise (to be used as exit code).
- """
- res = 0
- for tmp_file in tmp_files:
- assert (tmp_file.endswith('.swp'))
- target_file = os.path.relpath(tmp_file[:-4])
- if check:
- if not filecmp.cmp(tmp_file, target_file):
- sys.stderr.write('%s needs to be regenerated\n' % target_file)
- res = 1
- os.unlink(tmp_file)
- else:
- os.rename(tmp_file, target_file)
- return res
-
-
-class ODRChecker(object):
- """Detects ODR violations in linker units
-
- When we turn GN source sets into Soong & Bazel file groups, there is the risk
- to create ODR violations by including the same file group into different
- linker unit (this is because other build systems don't have a concept
- equivalent to GN's source_set). This class navigates the transitive
- dependencies (mostly static libraries) of a target and detects if multiple
- paths end up including the same file group. This is to avoid situations like:
-
- traced.exe -> base(file group)
- traced.exe -> libperfetto(static lib) -> base(file group)
- """
-
- def __init__(self, gn, target_name):
- self.gn = gn
- self.root = gn.get_target(target_name)
- self.source_sets = collections.defaultdict(set)
- self.deps_visited = set()
- self.source_set_hdr_only = {}
-
- self._visit(target_name)
- num_violations = 0
- if target_name in ODR_VIOLATION_IGNORE_TARGETS:
- return
- for sset, paths in self.source_sets.items():
- if self.is_header_only(sset):
- continue
- if len(paths) != 1:
- num_violations += 1
- print(
- 'ODR violation in target %s, multiple paths include %s:\n %s' %
- (target_name, sset, '\n '.join(paths)),
- file=sys.stderr)
- if num_violations > 0:
- raise Exception('%d ODR violations detected. Build generation aborted' %
- num_violations)
-
- def _visit(self, target_name, parent_path=''):
- target = self.gn.get_target(target_name)
- path = ((parent_path + ' > ') if parent_path else '') + target_name
- if not target:
- raise Exception('Cannot find target %s' % target_name)
- for ssdep in target.source_set_deps:
- name_and_path = '%s (via %s)' % (target_name, path)
- self.source_sets[ssdep].add(name_and_path)
- deps = set(target.deps).union(
- target.transitive_proto_deps) - self.deps_visited
- for dep_name in deps:
- dep = self.gn.get_target(dep_name)
- if dep.type == 'executable':
- continue # Execs are strong boundaries and don't cause ODR violations.
- # static_library dependencies should reset the path. It doesn't matter if
- # we get to a source file via:
- # source_set1 > static_lib > source.cc OR
- # source_set1 > source_set2 > static_lib > source.cc
- # This is NOT an ODR violation because source.cc is linked from the same
- # static library
- next_parent_path = path if dep.type != 'static_library' else ''
- self.deps_visited.add(dep_name)
- self._visit(dep_name, next_parent_path)
-
- def is_header_only(self, source_set_name):
- cached = self.source_set_hdr_only.get(source_set_name)
- if cached is not None:
- return cached
- target = self.gn.get_target(source_set_name)
- if target.type != 'source_set':
- raise TypeError('%s is not a source_set' % source_set_name)
- res = all(src.endswith('.h') for src in target.sources)
- self.source_set_hdr_only[source_set_name] = res
- return res
-
-
class GnParser(object):
"""A parser with some cleverness for GN json desc files
@@ -309,7 +98,7 @@
self.name = name # e.g. //src/ipc:ipc
VALID_TYPES = ('static_library', 'shared_library', 'executable', 'group',
- 'action', 'source_set', 'proto_library')
+ 'action', 'source_set', 'proto_library', 'copy', 'action_foreach')
assert (type in VALID_TYPES)
self.type = type
self.testonly = False
@@ -396,10 +185,14 @@
target.toolchain = desc.get('toolchain', None)
self.all_targets[gn_target_name] = target
+ # TODO: determine if below comment should apply for cronet builds in Android.
# We should never have GN targets directly depend on buidtools. They
# should hop via //gn:xxx, so we can give generators an opportunity to
# override them.
- assert (not gn_target_name.startswith('//buildtools'))
+ # Specifically allow targets to depend on libc++ and libunwind.
+ if not any(match in gn_target_name for match in ['libc++', 'libunwind']):
+ assert (not gn_target_name.startswith('//buildtools'))
+
# Don't descend further into third_party targets. Genrators are supposed
# to either ignore them or route to other externally-provided targets.
@@ -422,7 +215,7 @@
elif target.type in LINKER_UNIT_TYPES:
self.linker_units[gn_target_name] = target
target.sources.update(desc.get('sources', []))
- elif target.type == 'action':
+ elif target.type in ['action', 'action_foreach']:
self.actions[gn_target_name] = target
target.inputs.update(desc.get('inputs', []))
target.sources.update(desc.get('sources', []))
@@ -432,6 +225,9 @@
# Args are typically relative to the root build dir (../../xxx)
# because root build dir is typically out/xxx/).
target.args = [re.sub('^../../', '//', x) for x in desc['args']]
+ elif target.type == 'copy':
+ # TODO: copy rules are not currently implemented.
+ self.actions[gn_target_name] = target
# Default for 'public' is //* - all headers in 'sources' are public.
# TODO(primiano): if a 'public' section is specified (even if empty), then
@@ -462,7 +258,7 @@
target.update(dep) # Bubble up source set's cflags/ldflags etc.
elif dep.type == 'group':
target.update(dep) # Bubble up groups's cflags/ldflags etc.
- elif dep.type == 'action':
+ elif dep.type in ['action', 'action_foreach', 'copy']:
if proto_target_type is None:
target.deps.add(dep_name)
elif dep.type in LINKER_UNIT_TYPES:
diff --git a/tools/gn2bp/run_buildtools_binary.py b/tools/gn2bp/run_buildtools_binary.py
deleted file mode 100755
index 7a6fe81..0000000
--- a/tools/gn2bp/run_buildtools_binary.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2022 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.
-""" A wrapper to run gn, ninja and other buildtools/ for all platforms. """
-
-from __future__ import print_function
-
-import os
-import subprocess
-import sys
-
-from platform import system, machine
-
-ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-
-
-def run_buildtools_binary(args):
- if len(args) < 1:
- print('Usage %s command [args]\n' % sys.argv[0])
- return 1
-
- sys_name = system().lower()
- os_dir = None
- ext = ''
- if sys_name == 'windows':
- os_dir = 'win'
- ext = '.exe'
- elif sys_name == 'darwin':
- os_dir = 'mac'
- elif sys_name == 'linux':
- os_dir = 'linux64'
- else:
- print('OS not supported: %s\n' % sys_name)
- return 1
-
- cmd = args[0]
- args = args[1:]
- exe_path = os.path.join(ROOT_DIR, 'buildtools', os_dir, cmd) + ext
- if sys_name == 'windows':
- # execl() behaves oddly on Windows: the spawned process doesn't seem to
- # receive CTRL+C. Use subprocess instead.
- return subprocess.call([exe_path] + args)
- else:
- os.execl(exe_path, os.path.basename(exe_path), *args)
-
-
-if __name__ == '__main__':
- sys.exit(run_buildtools_binary(sys.argv[1:]))
diff --git a/tools/gn2bp/update_results.sh b/tools/gn2bp/update_results.sh
new file mode 100755
index 0000000..31f6605
--- /dev/null
+++ b/tools/gn2bp/update_results.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# This script is expected to run after gen_android_bp is modified.
+#
+# ./update_result.sh desc.json
+#
+# TARGETS contains targets which are supported by gen_android_bp and
+# this script generates Android.bp.swp from TARGETS.
+# This makes it easy to realize unintended impact/degression on
+# previously supported targets.
+
+set -eux
+
+TARGETS=(
+ "//third_party/zlib:zlib"
+ "//third_party/libevent:libevent"
+)
+
+BASEDIR=$(dirname "$0")
+$BASEDIR/gen_android_bp --desc $1 --out $BASEDIR/Android.bp ${TARGETS[@]}