[automerger skipped] Support kernels that make SOCK_DESTROY work on FIN_WAIT sockets. am: 66ab04aa4c -s ours am skip reason: Merged-In If40089365027b4ef98fdcf695aec9404a4c32fd9 with SHA-1 56f489f85b is already in history Original change: https://android-review.googlesource.com/c/kernel/tests/+/3561369 Change-Id: I51c33395c59714d36cd1c549689ef3c0c7e6b56b Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/net/test/Android.bp b/net/test/Android.bp index fe063ee..35e8a26 100644 --- a/net/test/Android.bp +++ b/net/test/Android.bp
@@ -15,11 +15,6 @@ "scapy", ], main: "all_tests.py", - version: { - py3: { - embedded_launcher: true, - }, - }, test_config: "vts_kernel_net_tests.xml", test_suites: [ "vts", @@ -39,10 +34,5 @@ "scapy", ], main: "all_tests_gki.py", - version: { - py3: { - embedded_launcher: true, - }, - }, test_config: "vts_kernel_net_tests.xml", }
diff --git a/net/test/OWNERS b/net/test/OWNERS index 76fba4e..e2aecdb 100644 --- a/net/test/OWNERS +++ b/net/test/OWNERS
@@ -1,6 +1,5 @@ # Bug component: 31808 set noparent -maze@google.com file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking_xts -per-file build_rootfs.sh = rammuthiah@google.com, adelva@google.com, muntsinger@google.com +per-file build_rootfs.sh = rammuthiah@google.com, adelva@google.com
diff --git a/net/test/all_tests.py b/net/test/all_tests.py index 422005f..84a23c6 100755 --- a/net/test/all_tests.py +++ b/net/test/all_tests.py
@@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import ctypes import importlib import os import sys @@ -23,6 +24,16 @@ import namespace import net_test +# man 2 personality +personality = ctypes.CDLL(None).personality +personality.restype = ctypes.c_int +personality.argtypes = [ctypes.c_ulong] + +# From Linux kernel's include/uapi/linux/personality.h +PER_QUERY = 0xFFFFFFFF +PER_LINUX = 0 +PER_LINUX32 = 8 + all_test_modules = [ 'anycast_test', 'bpf_test', @@ -51,8 +62,15 @@ def RunTests(modules_to_test): - print('Running on %s %s %s %s-%sbit%s%s' - % (os.uname()[0], os.uname()[2], net_test.LINUX_VERSION, os.uname()[4], + uname = os.uname() + linux = uname.sysname + kver = uname.release + arch = uname.machine + p = personality(PER_LINUX) + true_arch = os.uname().machine + personality(p) + print('Running on %s %s %s %s/%s-%sbit%s%s' + % (linux, kver, net_test.LINUX_VERSION, true_arch, arch, '64' if sys.maxsize > 0x7FFFFFFF else '32', ' GKI' if gki.IS_GKI else '', ' GSI' if net_test.IS_GSI else ''), file=sys.stderr)
diff --git a/net/test/bpf.py b/net/test/bpf.py index bc46e95..65b0749 100755 --- a/net/test/bpf.py +++ b/net/test/bpf.py
@@ -48,11 +48,8 @@ # After ACK merge of 5.10.168 is when support for this was backported from # upstream Linux 5.14 and was merged into ACK android{12,13}-5.10 branches. -# ACK android12-5.10 was >= 5.10.168 without this support only for ~4.5 hours -# ACK android13-4.10 was >= 5.10.168 without this support only for ~25 hours -# as such we can >= 5.10.168 instead of > 5.10.168 -# Additionally require support to be backported to any 5.10+ non-GKI/GSI kernel. -HAVE_SO_NETNS_COOKIE = net_test.LINUX_VERSION >= (5, 10, 168) or net_test.NonGXI(5, 10) +# Require support to be backported to any 5.10+ kernel. +HAVE_SO_NETNS_COOKIE = net_test.LINUX_VERSION >= (5, 10, 0) # Note: This is *not* correct for parisc & sparc architectures SO_NETNS_COOKIE = 71 @@ -213,7 +210,6 @@ # pylint: enable=invalid-name libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) -HAVE_EBPF_5_4 = net_test.LINUX_VERSION >= (5, 4, 0) # set memlock resource 1 GiB resource.setrlimit(resource.RLIMIT_MEMLOCK, (1073741824, 1073741824))
diff --git a/net/test/kernel_feature_test.py b/net/test/kernel_feature_test.py index 2594a82..1f76dd5 100755 --- a/net/test/kernel_feature_test.py +++ b/net/test/kernel_feature_test.py
@@ -96,15 +96,6 @@ def testIsGKI(self): pass - @unittest.skipUnless(not net_test.IS_GSI and not gki.IS_GKI, "GSI or GKI") - def testMinRequiredKernelVersion(self): - self.assertTrue(net_test.KernelAtLeast([(4, 19, 236), - (5, 4, 186), - (5, 10, 199), - (5, 15, 136), - (6, 1, 57)]), - "%s [%s] is too old." % (os.uname()[2], os.uname()[4])) - if __name__ == "__main__": unittest.main()
diff --git a/net/test/multinetwork_base.py b/net/test/multinetwork_base.py index 8c5fc26..bc5300f 100644 --- a/net/test/multinetwork_base.py +++ b/net/test/multinetwork_base.py
@@ -57,6 +57,7 @@ IPV4_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv4/fwmark_reflect" IPV6_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv6/fwmark_reflect" RA_HONOR_PIO_LIFE_SYSCTL = "/proc/sys/net/ipv6/conf/default/ra_honor_pio_life" +RA_HONOR_PIO_PFLAG = "/proc/sys/net/ipv6/conf/default/ra_honor_pio_pflag" HAVE_ACCEPT_RA_MIN_LFT = (os.path.isfile(ACCEPT_RA_MIN_LFT_SYSCTL) or net_test.NonGXI(5, 10) or @@ -65,6 +66,14 @@ HAVE_AUTOCONF_TABLE = os.path.isfile(AUTOCONF_TABLE_SYSCTL) HAVE_RA_HONOR_PIO_LIFE = (os.path.isfile(RA_HONOR_PIO_LIFE_SYSCTL) or net_test.KernelAtLeast([(6, 7, 0)])) +HAVE_RA_HONOR_PIO_PFLAG = (os.path.isfile(RA_HONOR_PIO_PFLAG) or + net_test.KernelAtLeast([(6, 12, 0)])) + +HAVE_USEROPT_PIO_FIX = net_test.KernelAtLeast([(4, 19, 320), (5, 4, 282), + (5, 10, 224), (5, 15, 165), + (6, 1, 104), (6, 6, 45), + (6, 9, 13), (6, 10, 4), + (6, 11, 0)]) class ConfigurationError(AssertionError): @@ -244,7 +253,7 @@ @classmethod def SendRA(cls, netid, retranstimer=None, reachabletime=0, routerlft=RA_VALIDITY, - piolft=RA_VALIDITY, m=0, o=0, options=()): + piolft=RA_VALIDITY, m=0, o=0, piopflag=0, options=()): macaddr = cls.RouterMacAddress(netid) lladdr = cls._RouterAddress(netid, 6) @@ -259,6 +268,8 @@ if not HAVE_AUTOCONF_TABLE: routerlft = 0 + res1 = 0x10 if piopflag else 0 + ra = (scapy.Ether(src=macaddr, dst="33:33:00:00:00:01") / scapy.IPv6(src=lladdr, hlim=255) / scapy.ICMPv6ND_RA(reachabletime=reachabletime, @@ -268,7 +279,7 @@ scapy.ICMPv6NDOptSrcLLAddr(lladdr=macaddr) / scapy.ICMPv6NDOptPrefixInfo(prefix=cls.OnlinkPrefix(6, netid), prefixlen=cls.OnlinkPrefixLen(6), - L=1, A=1, + L=1, A=1, res1=res1, validlifetime=piolft, preferredlifetime=piolft)) for option in options:
diff --git a/net/test/multinetwork_test.py b/net/test/multinetwork_test.py index 2bae074..ab9012c 100755 --- a/net/test/multinetwork_test.py +++ b/net/test/multinetwork_test.py
@@ -599,6 +599,8 @@ self.SetAcceptRaMinLft(0) if multinetwork_base.HAVE_RA_HONOR_PIO_LIFE: self.SetRaHonorPioLife(0) + if multinetwork_base.HAVE_RA_HONOR_PIO_PFLAG: + self.SetRaHonorPioPflag(0) def GetRoutingTable(self): if multinetwork_base.HAVE_AUTOCONF_TABLE: @@ -629,6 +631,10 @@ self.SetSysctl( "/proc/sys/net/ipv6/conf/%s/accept_ra_min_lft" % self.IFACE, min_lft) + def SetRaHonorPioPflag(self, val): + self.SetSysctl( + "/proc/sys/net/ipv6/conf/%s/ra_honor_pio_pflag" % self.IFACE, val) + def GetAcceptRaMinLft(self): return int(self.GetSysctl( "/proc/sys/net/ipv6/conf/%s/accept_ra_min_lft" % self.IFACE)) @@ -921,10 +927,39 @@ time.sleep(0.1) # Give the kernel time to notice our RA self.assertFalse(self.FindRoutesWithDestination(PREFIX)) + @unittest.skipUnless(multinetwork_base.HAVE_RA_HONOR_PIO_PFLAG, + "needs support for ra_honor_pio_pflag") + def testPioPflag(self): + self.SetRaHonorPioPflag(1); + + # Test setup has sent an initial RA -- expire it. + self.SendRA(self.NETID, routerlft=0, piolft=0) + time.sleep(0.1) # Give the kernel time to notice our RA + # Check that the prefix route was deleted. + prefixroutes = self.FindRoutesWithDestination(self.OnlinkPrefix(6, self.NETID)) + self.assertEqual([], prefixroutes) + + # Sending a 0-lifetime PIO does not cause the address to be deleted, see + # rfc2462#section-5.5.3. + address = self.MyAddress(6, self.NETID) + self.iproute.DelAddress(address, 64, self.ifindices[self.NETID]) + + # PIO with p-flag is ignored + self.SendRA(self.NETID, piopflag=1) + time.sleep(0.1) # Give the kernel time to notice our RA + self.assertIsNone(self.MyAddress(6, self.NETID)) + + self.SetRaHonorPioPflag(0); + # PIO with p-flag is processed + self.SendRA(self.NETID, piopflag=1) + time.sleep(0.1) # Give the kernel time to notice our RA + self.assertIsNotNone(self.MyAddress(6, self.NETID)) + class RATest(multinetwork_base.MultiNetworkBaseTest): ND_ROUTER_ADVERT = 134 + ND_OPT_PIO = 3 ND_OPT_PREF64 = 38 NDOptHeader = cstruct.Struct("ndopt_header", "!BB", "type length") Pref64Option = cstruct.Struct("pref64_option", "!BBH12s", @@ -1047,7 +1082,11 @@ # Check that we get an an RTM_NEWNDUSEROPT message on the socket with the # expected option. csocket.SetSocketTimeout(s.sock, 100) - while True: + + needPIO = multinetwork_base.HAVE_USEROPT_PIO_FIX + needPref64 = True + + while needPIO or needPref64: try: data = s._Recv() except IOError as e: @@ -1071,8 +1110,6 @@ # print("ndopthdr=[%s] data=[%s] leftover=[%s]" % (ndopthdr, data, leftover)) - foundPref64 = False - while data: # print("data2=[%s]\n" % data) @@ -1083,22 +1120,19 @@ # print("type=%d len=%d payload[%s]\n" % (header_opt.type, header_opt.length * 8, payload)) - if header_opt.type != self.ND_OPT_PREF64: - continue + if header_opt.type == self.ND_OPT_PIO: + needPIO = False + elif header_opt.type == self.ND_OPT_PREF64: + needPref64 = False + self.assertEqual(len(opt), len(payload)) + self.assertEqual(opt, self.Pref64Option(payload)) + else: + # cannot happen: no other options we generate are currently considered user options + assert False - self.assertEqual(len(opt), len(payload)) - - actual_opt = self.Pref64Option(payload) - self.assertEqual(opt, actual_opt) - foundPref64 = True - - if foundPref64: - break - + # we only ever reach here if we find all options we need s.close() - self.assertEqual(foundPref64, True) - def testRaFlags(self): def GetInterfaceIpv6Flags(iface): attrs = self.iproute.GetIflaAfSpecificData(iface, AF_INET6)
diff --git a/net/test/net_test.py b/net/test/net_test.py index cdfdc0f..e29884e 100644 --- a/net/test/net_test.py +++ b/net/test/net_test.py
@@ -24,6 +24,7 @@ import sys import unittest +from scapy.arch import linux from scapy import all as scapy import binascii @@ -270,7 +271,7 @@ def GetInterfaceIndex(ifname): with UDPSocket(AF_INET) as s: ifr = struct.pack("%dsi" % IFNAMSIZ, ifname.encode(), 0) - ifr = fcntl.ioctl(s, scapy.SIOCGIFINDEX, ifr) + ifr = fcntl.ioctl(s, linux.SIOCGIFINDEX, ifr) return struct.unpack("%dsi" % IFNAMSIZ, ifr)[1] @@ -289,14 +290,14 @@ ifname_bytes = ifname.encode() with UDPSocket(AF_INET) as s: ifr = struct.pack("%dsH" % IFNAMSIZ, ifname_bytes, 0) - ifr = fcntl.ioctl(s, scapy.SIOCGIFFLAGS, ifr) + ifr = fcntl.ioctl(s, linux.SIOCGIFFLAGS, ifr) _, flags = struct.unpack("%dsH" % IFNAMSIZ, ifr) if up: - flags |= scapy.IFF_UP + flags |= linux.IFF_UP else: - flags &= ~scapy.IFF_UP + flags &= ~linux.IFF_UP ifr = struct.pack("%dsH" % IFNAMSIZ, ifname_bytes, flags) - ifr = fcntl.ioctl(s, scapy.SIOCSIFFLAGS, ifr) + ifr = fcntl.ioctl(s, linux.SIOCSIFFLAGS, ifr) def SetInterfaceUp(ifname):
diff --git a/net/test/packets.py b/net/test/packets.py index 2a2ca1e..e9c4777 100644 --- a/net/test/packets.py +++ b/net/test/packets.py
@@ -87,7 +87,7 @@ seq=seq, ack=0, flags=TCP_SYN, window=TCP_WINDOW)) -def RST(version, srcaddr, dstaddr, packet): +def RST(version, srcaddr, dstaddr, packet, sent_fin=False): ip = _GetIpLayer(version) original = packet.getlayer("TCP") was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0 @@ -95,7 +95,7 @@ ip(src=srcaddr, dst=dstaddr) / scapy.TCP(sport=original.dport, dport=original.sport, ack=original.seq + was_syn_or_fin, - seq=original.ack, + seq=original.ack + sent_fin, flags=TCP_RST | TCP_ACK, window=TCP_WINDOW)) def SYNACK(version, srcaddr, dstaddr, packet):
diff --git a/net/test/rootfs/OWNERS b/net/test/rootfs/OWNERS index 9ee1527..728156f 100644 --- a/net/test/rootfs/OWNERS +++ b/net/test/rootfs/OWNERS
@@ -1,3 +1,2 @@ adelva@google.com -muntsinger@google.com rammuthiah@google.com
diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh index 8635eab..3a37255 100755 --- a/net/test/run_net_test.sh +++ b/net/test/run_net_test.sh
@@ -178,9 +178,15 @@ elif [[ "$1" == "--nobuild" ]]; then nobuild=1 shift + elif [[ "$1" == "--build" ]]; then + nobuild=0 + shift elif [[ "$1" == "--norun" ]]; then norun=1 shift + elif [[ "$1" == "--run" ]]; then + norun=0 + shift elif [[ "$1" == "--verbose" ]]; then verbose=1 shift
diff --git a/net/test/sock_diag_test.py b/net/test/sock_diag_test.py index fee9d0b..a668ce0 100755 --- a/net/test/sock_diag_test.py +++ b/net/test/sock_diag_test.py
@@ -639,7 +639,7 @@ # Close the socket and check that it goes into FIN_WAIT1 and sends a FIN. net_test.EnableFinWait(self.accepted) self.accepted.close() - del self.accepted + self.accepted = None diag_req.states = 1 << tcp_test.TCP_FIN_WAIT1 diag_msg, attrs = self.sock_diag.GetSockInfo(diag_req) self.assertEqual(tcp_test.TCP_FIN_WAIT1, diag_msg.state)
diff --git a/net/test/tcp_test.py b/net/test/tcp_test.py index f3ee291..b869aa4 100644 --- a/net/test/tcp_test.py +++ b/net/test/tcp_test.py
@@ -40,13 +40,20 @@ class TcpBaseTest(multinetwork_base.MultiNetworkBaseTest): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.accepted = None + self.s = None + self.last_packet = None + self.sent_fin = False + def CloseSockets(self): - if hasattr(self, "accepted"): + if self.accepted: self.accepted.close() - del self.accepted - if hasattr(self, "s"): + self.accepted = None + if self.s: self.s.close() - del self.s + self.s = None def tearDown(self): self.CloseSockets() @@ -81,12 +88,15 @@ def RstPacket(self): return packets.RST(self.version, self.myaddr, self.remoteaddr, - self.last_packet) + self.last_packet, self.sent_fin) def FinPacket(self): return packets.FIN(self.version, self.myaddr, self.remoteaddr, self.last_packet) + def ExpectPacketOn(self, netid, msg, pkt): + self.sent_fin |= (pkt.getlayer("TCP").flags & packets.TCP_FIN) != 0 + return super(TcpBaseTest, self).ExpectPacketOn(netid, msg, pkt) def IncomingConnection(self, version, end_state, netid): self.s = self.OpenListenSocket(version, netid)
diff --git a/net/test/util.py b/net/test/util.py index cbcd2d0..ced986e 100644 --- a/net/test/util.py +++ b/net/test/util.py
@@ -12,6 +12,28 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Utilities for kernel net tests.""" + +import ctypes + + +def GetSysprop(name): + PROP_VALUE_MAX = 92 + libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) + name = ctypes.create_string_buffer(name) + value = ctypes.create_string_buffer(PROP_VALUE_MAX) + libc.__system_property_get(name, value) + return value.value + + +def VendorApiLevelIsAtLeast(min_level): + try: + level = int(GetSysprop(b"ro.vendor.api_level")) + except AttributeError: + return True + return level >= min_level + + def GetPadLength(block_size, length): return (block_size - (length % block_size)) % block_size
diff --git a/tools/OWNERS b/tools/OWNERS new file mode 100644 index 0000000..b3332dc --- /dev/null +++ b/tools/OWNERS
@@ -0,0 +1,11 @@ +bettyzhou@google.com +edliaw@google.com +elsk@google.com +hsinyichen@google.com +joneslee@google.com +jstultz@google.com +locc@google.com +maennich@google.com +vmartensson@google.com +willmcvicker@google.com +
diff --git a/tools/coverage_howto.md b/tools/coverage_howto.md new file mode 100644 index 0000000..c9df271 --- /dev/null +++ b/tools/coverage_howto.md
@@ -0,0 +1,126 @@ +HOW TO COLLECT KERNEL CODE COVERAGE FROM A TEST RUN +=================================================== + + +## 1. Build and use a kernel with GCOV profile enabled +### Build and install with scripts +Build and install a GCOV kernel on a Cuttlefish or physical device with one of +the following commands: +``` +$ kernel/tests/tools/launch_cvd.sh --gcov +``` +``` +$ kernel/tests/tools/flash_device.sh --gcov +``` +To view available options, run the scripts with `--help`. + +### Build on your own +Build a kernel with +[`--gcov`](https://android.googlesource.com/kernel/build/+/refs/heads/main/kleaf/docs/gcov.md) +option. This will also trigger the build to save the required *.gcno files +needed to viewing the collected count data. + +For example, build a Cuttlefish kernel with GCOV: +``` +$ tools/bazel run --gcov //common-modules/virtual-device:virtual_device_x86_64_dist +``` + +## 2. Run tests with kernel coverage collection enabled +### `run_test_only.sh` +Collect test coverage data with `run_test_only.sh --gcov` and the required +options. For example, + +``` +$ kernel/tests/tools/run_test_only.sh --gcov \ + --serial 0.0.0.0:6520 --test='selftests kselftest_net_socket' +``` + +To view available options, run the script with `--help`. + +### `tradefed.sh` +Adding the appropriate coverage flags to the tradefed call will trigger it to +take care of mounting debugfs, reseting the gcov counts prior to test run, and +collecting gcov data files from debugfs after test completion. These coverage +arguments are: +``` +--coverage --coverage-toolchain GCOV_KERNEL --auto-collect GCOV_KERNEL_COVERAGE +``` + +The following is a full example call running just the `kselftest_net_socket` +test in the selftests test suite that exists under the `out/tests/testcases` +directory. The artifact output has been redirected to `tf-logs` for easier +reference needed in the next step. +``` +$ prebuilts/tradefed/filegroups/tradefed/tradefed.sh run commandAndExit \ + template/local_min --template:map test=suite/test_mapping_suite \ + --include-filter 'selftests kselftest_net_socket' \ + --tests-dir=out/tests/testcases \ + --primary-abi-only --log-file-path tf-logs \ + --coverage --coverage-toolchain GCOV_KERNEL \ + --auto-collect GCOV_KERNEL_COVERAGE +``` + +## 3. Create an lcov tracefile out of the gcov tar artifact from test run +The previously mentioned `run_test_only.sh` or `tradefed.sh` run will produce +a tar file artifact in the log folder with a name like +`<test>_kernel_coverage_*.tar.gz`. This tar file is an archive of all the gcov +data files collected into debugfs from the profiled device. In order to make +it easier to work with this data, it needs to be converted to a single lcov +tracefile. + +The script `create-tracefile.py` facilitates this generation by handling the +required unpacking, file path corrections and ultimate `lcov` call. +`run_test_only.sh` calls `create-tracefile.py` automatically if it can locate +the kernel source. Otherwise, it shows the arguments for you to run +`create-tracefile.py` in the kernel source tree. + +If you use `tradefed.sh`, you need to issue the `create-tracefile.py` command. +The following is an example where we generate a tracefile named `cov.info` +only including results from `net/socket.c`. (If no source files are specified +as included, then all source file data is used.) +``` +$ kernel/tests/tools/create-tracefile.py -t tf-logs --include net/socket.c +``` + +## 4. Visualizing results +With the created tracefile, there are a number of different ways to view +coverage data from it. Check out `man lcov` for more options. +### Summary +``` +$ lcov --summary --rc lcov_branch_coverage=1 cov.info +Reading tracefile cov.info_fix +Summary coverage rate: + lines......: 6.0% (81646 of 1370811 lines) + functions..: 9.6% (10285 of 107304 functions) + branches...: 3.7% (28639 of 765538 branches) +``` +### List +``` +$ lcov --list --rc lcov_branch_coverage=1 cov.info +Reading tracefile cov.info_fix + |Lines |Functions|Branches +Filename |Rate Num|Rate Num|Rate Num +================================================================================ +[/usr/local/google/home/joefradley/dev/common-android-mainline-2/common/] +arch/x86/crypto/aesni-intel_glue.c |23.9% 623|22.2% 36|15.0% 240 +arch/x86/crypto/blake2s-glue.c |50.0% 28|50.0% 2|16.7% 30 +arch/x86/crypto/chacha_glue.c | 0.0% 157| 0.0% 10| 0.0% 80 +<truncated> +virt/lib/irqbypass.c | 0.0% 137| 0.0% 6| 0.0% 88 +================================================================================ + Total:| 6.0% 1369k| 9.6% 0M| 3.7% 764k +``` +### HTML +The `lcov` tool `genhtml` is used to generate html. To create html with the +default settings: + +``` +$ genhtml --branch-coverage -o html cov.info +``` + +The page can be viewed at `html/index.html`. + +Options of interest: + * `--frame`: Creates a left hand macro view in a source file view. + * `--missed`: Helpful if you want to sort by what source is missing the most + as opposed to the default coverage percentages.
diff --git a/tools/create-tracefile.py b/tools/create-tracefile.py new file mode 100755 index 0000000..c91d510 --- /dev/null +++ b/tools/create-tracefile.py
@@ -0,0 +1,580 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2024 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 utility generates a single lcov tracefile from a gcov tar file.""" + +import argparse +import collections +import fnmatch +import glob +import json +import logging +import os +import pathlib +import re +import shutil +import sys +import tarfile + + +LCOV = "lcov" + +# Relative to the root of the source tree. +OUTPUT_COV_DIR = os.path.join("out", "coverage") + +BUILD_CONFIG_CONSTANTS_PATH = os.path.join("common", "build.config.constants") + +PREBUILT_CLANG_DIR = os.path.join("prebuilts", "clang", "host", "linux-x86") + +PREBUILT_LLVM_COV_PATH_FORMAT = os.path.join( + PREBUILT_CLANG_DIR, "clang-%s", "bin", "llvm-cov" +) + +PREBUILT_STABLE_LLVM_COV_PATH = os.path.join( + PREBUILT_CLANG_DIR, "llvm-binutils-stable", "llvm-cov" +) + +EXCLUDED_FILES = [ + "*/security/selinux/av_permissions.h", + "*/security/selinux/flask.h", +] + + +def create_llvm_gcov_sh( + llvm_cov_filename: str, + llvm_gcov_sh_filename: str, +) -> None: + """Create a shell script that is compatible with gcov. + + Args: + llvm_cov_filename: The absolute path to llvm-cov. + llvm_gcov_sh_filename: The path to the script to be created. + """ + file_path = pathlib.Path(llvm_gcov_sh_filename) + file_path.parent.mkdir(parents=True, exist_ok=True) + file_path.write_text(f'#!/bin/bash\nexec {llvm_cov_filename} gcov "$@"') + os.chmod(llvm_gcov_sh_filename, 0o755) + + +def generate_lcov_tracefile( + gcov_dir: str, + kernel_source: str, + gcov_filename: str, + tracefile_filename: str, + included_files: [], +) -> None: + """Call lcov to create tracefile based on gcov data files. + + Args: + gcov_dir: Directory that contains the extracted gcov data files as retrieved + from debugfs. + kernel_source: Directory containing the kernel source same as what was used + to build system under test. + gcov_filename: The absolute path to gcov or a compatible script. + tracefile_filename: The name of tracefile to create. + included_files: List of source file pattern to include in tracefile. Can be + empty in which case include allo source. + """ + exclude_args = " ".join([f'--exclude "{f}"' for f in EXCLUDED_FILES]) + include_args = ( + " ".join([f'--include "{f[0]}"' for f in included_files]) + if included_files is not None + else "" + ) + + logging.info("Running lcov on %s", gcov_dir) + lcov_cmd = ( + f"{LCOV} -q " + "--ignore-errors=source " + "--rc branch_coverage=1 " + f"-b {kernel_source} " + f"-d {gcov_dir} " + f"--gcov-tool {gcov_filename} " + f"{exclude_args} " + f"{include_args} " + "--ignore-errors gcov,gcov,unused,unused " + "--capture " + f"-o {tracefile_filename} " + ) + os.system(lcov_cmd) + + +def update_symlink_from_mapping(filepath: str, prefix_mappings: {}) -> bool: + """Update symbolic link based on prefix mappings. + + It will attempt to update the given symbolic link based on the prefix + mappings. For every "from" prefix that matches replace with the new "to" + value. If the resulting path doesn't exist, try the next. + + Args: + filepath: Path of symbolic link to update. + prefix_mappings: A multimap where the key is the "from" prefix to match, and + the value is an array of "to" values to attempt to replace with. + + Returns: + True or false depending on the whether symbolic link was successfully + updated to a new path that exists. + """ + + link_target = os.readlink(filepath) + for old_prefix, new_prefix_list in prefix_mappings.items(): + for new_prefix in new_prefix_list: + if link_target.startswith(old_prefix): + new_target = os.path.abspath( + link_target.replace(old_prefix, new_prefix) + ) + if not os.path.exists(new_target): + continue + os.unlink(filepath) # Remove the old symbolic link + os.symlink(new_target, filepath) # Create the updated link + return True + + return False + + +def correct_symlinks_in_directory(directory: str, prefix_mappings: {}) -> None: + """Recursively traverses a directory, updating symbolic links. + + Replaces 'old_prefix' in the link destination with 'new_prefix'. + + Args: + directory: The root directory to traverse. + prefix_mappings: Dictionary where the keys are the old prefixes and the + values are the new prefixes + """ + + logging.info("Fixing up symbolic links in %s", directory) + + for root, _, files in os.walk(directory): + for filename in files: + filepath = os.path.join(root, filename) + if os.path.islink(filepath): + if not update_symlink_from_mapping(filepath, prefix_mappings): + logging.error( + "Unable to update link at %s with any prefix mappings: %s", + filepath, + prefix_mappings, + ) + sys.exit(-1) + + +def find_most_recent_tarfile(path: str, pattern: str = "*.tar.gz") -> str: + """Attempts to find a valid tar file given the location. + + If location is a directory finds the most recent tarfile or if location is a + a valid tar file returns, if neither of these return None. + + Args: + path (str): The path to either a tarfile or a directory. + pattern (str, optional): Glob pattern for matching tarfiles. Defaults to + "*.tar.gz". + + Returns: + str: The path to the most recent tarfile found, or the original path + if it was a valid tarfile. None if no matching tarfiles are found. + """ + + if os.path.isfile(path): + if tarfile.is_tarfile(path): + return path # Path is a valid tarfile + return None # Path is a file but not a tar file + + if os.path.isdir(path): + results = [] + for root, _, files in os.walk(path): + for file in files: + if fnmatch.fnmatch(file, pattern): + full_path = os.path.join(root, file) + results.append((full_path, os.path.getmtime(full_path))) + + if results: + return max(results, key=lambda item: item[1])[ + 0 + ] # Return path of the most recent one + else: + return None # No tarfiles found in the directory + + return None # Path is neither a tarfile nor a directory + + +def make_absolute(path: str, base_dir: str) -> str: + if os.path.isabs(path): + return path + + return os.path.join(base_dir, path) + + +def append_slash(path: str) -> str: + if path is not None and path[-1] != "/": + path += "/" + return path + + +def update_multimap_from_json( + json_file: str, base_dir: str, result_multimap: collections.defaultdict +) -> None: + """Reads 'to' and 'from' fields from a JSON file and updates a multimap. + + 'from' refers to a bazel sandbox directory. + 'to' refers to the output directory of gcno files. + The multimap is implemented as a dictionary of lists allowing multiple 'to' + values for each 'from' key. + + Sample input: + [ + { + "from": "/sandbox/1/execroot/_main/out/android-mainline/common", + "to": "bazel-out/k8-fastbuild/bin/common/kernel_x86_64/kernel_x86_64_gcno" + }, + { + "from": "/sandbox/2/execroot/_main/out/android-mainline/common", + "to": "bazel-out/k8-fastbuild/bin/common-modules/virtual-device/virtual_device_x86_64/virtual_device_x86_64_gcno" + } + ] + + Args: + json_file: The path to the JSON file. + base_dir: Used if either of the 'to' or 'from' paths are relative to make + them absolute by prepending this base_dir value. + result_multimap: A multimap that is updated with every 'to' and 'from' + found. + + Returns: + The updated dictionary. + """ + with open(json_file, "r") as file: + data = json.load(file) + + for item in data: + to_value = append_slash(item.get("to")) + from_value = append_slash(item.get("from")) + if to_value and from_value: + to_value = make_absolute(to_value, base_dir) + from_value = make_absolute(from_value, base_dir) + result_multimap[from_value].append(to_value) + + +def read_gcno_mapping_files( + search_dir_pattern: str, + base_dir: str, + result_multimap: collections.defaultdict +) -> None: + """Search a directory for gcno_mapping.""" + found = False + pattern = os.path.join(search_dir_pattern, "gcno_mapping.*.json") + for filepath in glob.iglob(pattern, recursive=False): + found = True + logging.info("Reading %s", filepath) + update_multimap_from_json(filepath, base_dir, result_multimap) + + if not found: + logging.error("No gcno_mapping in %s", search_dir_pattern) + + +def read_gcno_dir( + gcno_dir: str, result_multimap: collections.defaultdict +) -> None: + """Read a directory containing gcno_mapping and gcno files.""" + multimap = collections.defaultdict(list) + read_gcno_mapping_files(gcno_dir, gcno_dir, multimap) + + to_value = append_slash(os.path.abspath(gcno_dir)) + for from_value in multimap: + result_multimap[from_value].append(to_value) + + +def get_testname_from_filename(file_path: str) -> str: + filename = os.path.basename(file_path) + if "_kernel_coverage" in filename: + tmp = filename[: filename.find("_kernel_coverage")] + testname = tmp[: tmp.rfind("_")] + else: + testname = filename[: filename.rfind("_")] + return testname + + +def unpack_gcov_tar(file_path: str, output_dir: str) -> str: + """Unpack the tar file into the specified directory. + + Args: + file_path: The path of the tar file to be unpacked. + output_dir: The root directory where the unpacked folder will reside. + + Returns: + The path of extracted data. + """ + + testname = get_testname_from_filename(file_path) + logging.info( + "Unpacking %s for test %s...", os.path.basename(file_path), testname + ) + + test_dest_dir = os.path.join(output_dir, testname) + if os.path.exists(test_dest_dir): + shutil.rmtree(test_dest_dir) + os.makedirs(test_dest_dir) + shutil.unpack_archive(file_path, test_dest_dir, "tar") + return test_dest_dir + + +def get_parent_path(path: str, levels_up: int) -> str: + """Goes up a specified number of levels from a given path. + + Args: + path: The path to find desired ancestor. + levels_up: The number of levels up to go. + + Returns: + The desired ancestor of the given path. + """ + p = pathlib.Path(path) + for _ in range(levels_up): + p = p.parent + return str(p) + + +def get_kernel_repo_dir() -> str: + # Assume this script is in a kernel source tree: + # kernel_repo/kernel/tests/tools/<this_script> + return get_parent_path(os.path.abspath(__file__), 4) + + +def load_kernel_clang_version(repo_dir: str) -> str: + """Load CLANG_VERSION from build.config.constants.""" + config_path = os.path.join(repo_dir, BUILD_CONFIG_CONSTANTS_PATH) + if not os.path.isfile(config_path): + return "" + clang_version = "" + with open(config_path, "r") as config_file: + for line in config_file: + match = re.fullmatch(r"\s*CLANG_VERSION=(\S*)\s*", line) + if match: + clang_version = match.group(1) + return clang_version + + +class Config: + """The input and output paths of this script.""" + + def __init__(self, repo_dir: str, llvm_cov_path: str, tmp_dir: str): + """Each argument can be empty.""" + self._repo_dir = os.path.abspath(repo_dir) if repo_dir else None + self._llvm_cov_path = ( + os.path.abspath(llvm_cov_path) if llvm_cov_path else None + ) + self._tmp_dir = os.path.abspath(tmp_dir) if tmp_dir else None + self._repo_out_dir = None + + @property + def repo_dir(self) -> str: + if not self._repo_dir: + self._repo_dir = get_kernel_repo_dir() + return self._repo_dir + + def _get_repo_path(self, rel_path: str) -> str: + repo_path = os.path.join(self.repo_dir, rel_path) + if not os.path.exists(repo_path): + logging.error( + "%s does not exist. If this script is not in the source directory," + " specify --repo-dir. If you do not have full kernel source," + " specify --llvm-cov, --gcno-dir, and --tmp-dir.", + repo_path, + ) + sys.exit(-1) + return repo_path + + @property + def llvm_cov_path(self) -> str: + if not self._llvm_cov_path: + # Load the clang version in kernel repo, + # or use the stable version in platform repo. + clang_version = load_kernel_clang_version(self.repo_dir) + self._llvm_cov_path = self._get_repo_path( + PREBUILT_LLVM_COV_PATH_FORMAT % clang_version if clang_version else + PREBUILT_STABLE_LLVM_COV_PATH + ) + return self._llvm_cov_path + + @property + def repo_out_dir(self) -> str: + if not self._repo_out_dir: + self._repo_out_dir = self._get_repo_path("out") + return self._repo_out_dir + + @property + def tmp_dir(self) -> str: + if not self._tmp_dir: + # Temporary directory does not have to exist. + self._tmp_dir = os.path.join(self.repo_dir, OUTPUT_COV_DIR) + return self._tmp_dir + + @property + def llvm_gcov_sh_path(self) -> str: + return os.path.join(self.tmp_dir, "tmp", "llvm-gcov.sh") + + +def main() -> None: + arg_parser = argparse.ArgumentParser( + description="Generate lcov tracefiles from gcov file dumps" + ) + + arg_parser.add_argument( + "-t", + dest="tar_location", + required=True, + help=( + "Either a path to a gcov tar file or a directory that contains gcov" + " tar file(s). The gcov tar file is expected to be created from" + " Tradefed. If a directory is used, will search the entire directory" + " for files matching *_kernel_coverage*.tar.gz and select the most" + " recent one." + ), + ) + arg_parser.add_argument( + "-o", + dest="out_file", + required=False, + help="Name of output tracefile generated. Default: cov.info", + default="cov.info", + ) + arg_parser.add_argument( + "--include", + action="append", + nargs=1, + required=False, + help=( + "File pattern of source file(s) to include in generated tracefile." + " Multiple patterns can be specified by using multiple --include" + " command line switches. If no includes are specified all source is" + " included." + ), + ) + arg_parser.add_argument( + "--repo-dir", + required=False, + help="Root directory of kernel source" + ) + arg_parser.add_argument( + "--dist-dir", + dest="dist_dirs", + action="append", + default=[], + required=False, + help="Dist directory containing gcno mapping files" + ) + arg_parser.add_argument( + "--gcno-dir", + dest="gcno_dirs", + action="append", + default=[], + required=False, + help="Path to an extracted .gcno.tar.gz" + ) + arg_parser.add_argument( + "--llvm-cov", + required=False, + help=( + "Path to llvm-cov. Default: " + + os.path.join("<repo_dir>", PREBUILT_LLVM_COV_PATH_FORMAT % "*") + + " or " + os.path.join("<repo_dir>", PREBUILT_STABLE_LLVM_COV_PATH) + ) + ) + arg_parser.add_argument( + "--tmp-dir", + required=False, + help=( + "Path to the directory where the temporary files are created." + " Default: " + os.path.join("<repo_dir>", OUTPUT_COV_DIR) + ) + ) + arg_parser.add_argument( + "--verbose", + action="store_true", + default=False, + help="Enable verbose logging", + ) + + args = arg_parser.parse_args() + + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(level=logging.WARNING) + + if shutil.which(LCOV) is None: + logging.error( + "%s is not found and is required for this script. Please install from:", + LCOV, + ) + logging.critical(" https://github.com/linux-test-project/lcov") + sys.exit(-1) + + if args.repo_dir and not os.path.isdir(args.repo_dir): + logging.error("%s is not a directory.", args.repo_dir) + sys.exit(-1) + + if args.llvm_cov and not os.path.isfile(args.llvm_cov): + logging.error("%s is not a file.", args.llvm_cov) + sys.exit(-1) + + for gcno_dir in args.gcno_dirs + args.dist_dirs: + if not os.path.isdir(gcno_dir): + logging.error("%s is not a directory.", gcno_dir) + sys.exit(-1) + + config = Config(args.repo_dir, args.llvm_cov, args.tmp_dir) + + gcno_mappings = collections.defaultdict(list) + if not args.gcno_dirs and not args.dist_dirs: + dist_dir_pattern = os.path.join(config.repo_out_dir, "**", "dist") + read_gcno_mapping_files(dist_dir_pattern, config.repo_dir, gcno_mappings) + + for dist_dir in args.dist_dirs: + read_gcno_mapping_files(dist_dir, config.repo_dir, gcno_mappings) + + for gcno_dir in args.gcno_dirs: + read_gcno_dir(gcno_dir, gcno_mappings) + + if not gcno_mappings: + # read_gcno_mapping_files prints the error messages + sys.exit(-1) + + tar_file = find_most_recent_tarfile( + args.tar_location, pattern="*kernel_coverage_*.tar.gz" + ) + if tar_file is None: + logging.error("Unable to find a gcov tar under %s", args.tar_location) + sys.exit(-1) + + gcov_dir = unpack_gcov_tar(tar_file, config.tmp_dir) + correct_symlinks_in_directory(gcov_dir, gcno_mappings) + + create_llvm_gcov_sh( + config.llvm_cov_path, + config.llvm_gcov_sh_path, + ) + + generate_lcov_tracefile( + gcov_dir, + config.repo_dir, + config.llvm_gcov_sh_path, + args.out_file, + args.include, + ) + + +if __name__ == "__main__": + main()
diff --git a/tools/fetch_artifact.sh b/tools/fetch_artifact.sh new file mode 100755 index 0000000..a29e1af --- /dev/null +++ b/tools/fetch_artifact.sh
@@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-2.0 + +# fetch_artifact .sh is a handy tool dedicated to download artifacts from ci. +# The fetch_artifact binary is needed for this script. Please see more info at: +# go/fetch_artifact, +# or +# https://android.googlesource.com/tools/fetch_artifact/ +# Will use x20 binary: /google/data/ro/projects/android/fetch_artifact by default. +# Can install fetch_artifact locally with: +# sudo glinux-add-repo android stable && \ +# sudo apt update && \ +# sudo apt install android-fetch-artifact# +# +DEFAULT_FETCH_ARTIFACT=/google/data/ro/projects/android/fetch_artifact +BOLD="$(tput bold)" +END="$(tput sgr0)" +GREEN="$(tput setaf 2)" +RED="$(tput setaf 198)" +YELLOW="$(tput setaf 3)" +BLUE="$(tput setaf 34)" + +function print_info() { + echo "[$MY_NAME]: ${GREEN}$1${END}" +} + +function print_warn() { + echo "[$MY_NAME]: ${YELLOW}$1${END}" +} + +function print_error() { + echo -e "[$MY_NAME]: ${RED}$1${END}" + exit 1 +} + +function binary_checker() { + if which fetch_artifact &> /dev/null; then + FETCH_CMD="fetch_artifact" + elif [ ! -z "${FETCH_ARTIFACT}" ] && [ -f "${FETCH_ARTIFACT}" ]; then + FETCH_CMD="${FETCH_ARTIFACT}" + elif [ -f "$DEFAULT_FETCH_ARTIFACT" ]; then + FETCH_CMD="$DEFAULT_FETCH_ARTIFACT" + else + print_error "\n${RED} fetch_artifact is not found${END}" + echo -e "\n${RED} Please see go/fetch_artifact${END} or + https://android.googlesource.com/tools/fetch_artifact/+/refs/heads/main" + exit 1 + fi +} + + +binary_checker + +fetch_cli="$FETCH_CMD" + +BUILD_INFO= +BUILD_FORMAT="ab://<branch>/<build_target>/<build_id>/<file_name>" +EXTRA_OPTIONS= + +MY_NAME="${0##*/}" + +for i in "$@"; do + case $i in + "ab://"*) + BUILD_INFO=$i + ;; + *) + EXTRA_OPTIONS+=" $i" + ;; + esac +done +if [ -z "$BUILD_INFO" ]; then + print_error "$0 didn't come with the expected $BUILD_FORMAT" +fi + +IFS='/' read -ra array <<< "$BUILD_INFO" +if [ ${#array[@]} -lt 6 ]; then + print_error "Invalid build format: $BUILD_INFO. Needs to be: $BUILD_FORMAT" +elif [ ${#array[@]} -gt 7 ]; then + print_error "Invalid TEST_DIR format: $BUILD_INFO. Needs to be: $BUILD_FORMAT" +else + fetch_cli+=" --branch ${array[2]}" + fetch_cli+=" --target ${array[3]}" + if [[ "${array[4]}" != latest* ]]; then + fetch_cli+=" --bid ${array[4]}" + else + fetch_cli+=" --latest" + fi + fetch_cli+="$EXTRA_OPTIONS" + fetch_cli+=" '${array[5]}'" +fi + +print_info "Run: $fetch_cli" +eval "$fetch_cli"
diff --git a/tools/flash_device.sh b/tools/flash_device.sh new file mode 100755 index 0000000..f7f75e0 --- /dev/null +++ b/tools/flash_device.sh
@@ -0,0 +1,1448 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-2.0 + +# A handy tool to flash device with local build or remote build. + +# Constants +FETCH_SCRIPT="fetch_artifact.sh" +# Please see go/cl_flashstation +CL_FLASH_CLI=/google/bin/releases/android/flashstation/cl_flashstation +LOCAL_FLASH_CLI=/google/bin/releases/android/flashstation/local_flashstation +MIX_SCRIPT_NAME="build_mixed_kernels_ramdisk" +FETCH_SCRIPT="kernel/tests/tools/fetch_artifact.sh" +DOWNLOAD_PATH="/tmp/downloaded_images" +KERNEL_TF_PREBUILT=prebuilts/tradefed/filegroups/tradefed/tradefed.sh +PLATFORM_TF_PREBUILT=tools/tradefederation/prebuilts/filegroups/tradefed/tradefed.sh +KERNEL_JDK_PATH=prebuilts/jdk/jdk11/linux-x86 +PLATFORM_JDK_PATH=prebuilts/jdk/jdk21/linux-x86 +LOCAL_JDK_PATH=/usr/local/buildtools/java/jdk11 +LOG_DIR=$PWD/out/test_logs/$(date +%Y%m%d_%H%M%S) +MIN_FASTBOOT_VERSION="35.0.2-12583183" +VENDOR_KERNEL_IMGS=("boot.img" "initramfs.img" "dtb.img" "dtbo.img" "vendor_dlkm.img") +# Color constants +BOLD="$(tput bold)" +END="$(tput sgr0)" +GREEN="$(tput setaf 2)" +RED="$(tput setaf 198)" +YELLOW="$(tput setaf 3)" +ORANGE="$(tput setaf 208)" +BLUE=$(tput setaf 4) + +SKIP_BUILD=false +GCOV=false +DEBUG=false +KASAN=false +EXTRA_OPTIONS=() +LOCAL_REPO= +DEVICE_VARIANT="userdebug" + +BOARD= +ABI= +PRODUCT= +BUILD_TYPE= +DEVICE_KERNEL_STRING= +DEVICE_KERNEL_VERSION= +SYSTEM_DLKM_INFO= + +function print_help() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "This script will build images and flash a physical device." + echo "" + echo "Available options:" + echo " -s <serial_number>, --serial=<serial_number>" + echo " [Mandatory] The serial number for device to be flashed with." + echo " --skip-build [Optional] Skip the image build step. Will build by default if in repo." + echo " --gcov [Optional] Build gcov enabled kernel" + echo " --debug [Optional] Build debug enabled kernel" + echo " --kasan [Optional] Build kasan enabled kernel" + echo " -pb <platform_build>, --platform-build=<platform_build>" + echo " [Optional] The platform build path. Can be a local path or a remote build" + echo " as ab://<branch>/<build_target>/<build_id>." + echo " If not specified and the script is running from a platform repo," + echo " it will use the platform build in the local repo." + echo " If string 'None' is set, no platform build will be flashed," + echo " -sb <system_build>, --system-build=<system_build>" + echo " [Optional] The system build path for GSI testing. Can be a local path or" + echo " remote build as ab://<branch>/<build_target>/<build_id>." + echo " If not specified, no system build will be used." + echo " -kb <kernel_build>, --kernel-build=<kernel_build>" + echo " [Optional] The kernel build path. Can be a local path or a remote build" + echo " as ab://<branch>/<build_target>/<build_id>." + echo " If not specified and the script is running from an Android common kernel repo," + echo " it will use the kernel in the local repo." + echo " If string 'None' is set, no kernel build will be flashed," + echo " -vkb <vendor_kernel_build>, --vendor-kernel-build=<vendor_kernel_build>" + echo " [Optional] The vendor kernel build path. Can be a local path or a remote build" + echo " as ab://<branch>/<build_target>/<build_id>." + echo " If not specified, and the script is running from a vendor kernel repo, " + echo " it will use the kernel in the local repo." + echo " If string 'None' is set, no vendor kernel build will be flashed," + echo " -vkbt <vendor_kernel_build_target>, --vendor-kernel-build-target=<vendor_kernel_build_target>" + echo " [Optional] The vendor kernel build target to be used to build vendor kernel." + echo " If not specified, and the script is running from a vendor kernel repo, " + echo " it will try to find a local build target in the local repo." + echo " --device-variant=<device_variant>" + echo " [Optional] Device variant such as userdebug, user, or eng." + echo " If not specified, will be userdebug by default." + echo " -h, --help Display this help message and exit" + echo "" + echo "Examples:" + echo "$0" + echo "$0 -s 1C141FDEE003FH" + echo "$0 -s 1C141FDEE003FH -pb ab://git_main/raven-userdebug/latest" + echo "$0 -s 1C141FDEE003FH -pb ~/aosp-main" + echo "$0 -s 1C141FDEE003FH -vkb ~/pixel-mainline -pb ab://git_main/raven-trunk_staging-userdebug/latest" + echo "$0 -s 1C141FDEE003FH -vkb ab://kernel-android-gs-pixel-mainline/kernel_raviole_kleaf/latest \ +-pb ab://git_trunk_pixel_kernel_61-release/raven-userdebug/latest \ +-kb ab://aosp_kernel-common-android-mainline/kernel_aarch64/latest" + echo "" + exit 0 +} + +function parse_arg() { + while test $# -gt 0; do + case "$1" in + -h|--help) + print_help + ;; + -s) + shift + if test $# -gt 0; then + SERIAL_NUMBER=$1 + else + print_error "device serial is not specified" + fi + shift + ;; + --serial*) + SERIAL_NUMBER=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + --skip-build) + SKIP_BUILD=true + shift + ;; + -pb) + shift + if test $# -gt 0; then + PLATFORM_BUILD=$1 + else + print_error "platform build is not specified" + fi + shift + ;; + --platform-build=*) + PLATFORM_BUILD=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + -sb) + shift + if test $# -gt 0; then + SYSTEM_BUILD=$1 + else + print_error "system build is not specified" + fi + shift + ;; + --system-build=*) + SYSTEM_BUILD=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + -kb) + shift + if test $# -gt 0; then + KERNEL_BUILD=$1 + else + print_error "kernel build path is not specified" + fi + shift + ;; + --kernel-build=*) + KERNEL_BUILD=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + -vkb) + shift + if test $# -gt 0; then + VENDOR_KERNEL_BUILD=$1 + else + print_error "vendor kernel build path is not specified" + fi + shift + ;; + --vendor-kernel-build=*) + VENDOR_KERNEL_BUILD=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + -vkbt) + shift + if test $# -gt 0; then + VENDOR_KERNEL_BUILD_TARGET=$1 + else + print_error "vendor kernel build target is not specified" + fi + shift + ;; + --vendor-kernel-build-target=*) + VENDOR_KERNEL_BUILD_TARGET=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + --device-variant=*) + DEVICE_VARIANT=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + --gcov) + GCOV=true + shift + ;; + --debug) + DEBUG=true + shift + ;; + --kasan) + KASAN=true + shift + ;; + *) + print_error "Unsupported flag: $1" >&2 + shift + ;; + esac + done +} + +function adb_checker() { + if ! which adb &> /dev/null; then + print_error "adb not found!" + fi +} + +function go_to_repo_root() { + current_dir="$1" + while [ ! -d ".repo" ] && [ "$current_dir" != "/" ]; do + current_dir=$(dirname "$current_dir") # Go up one directory + cd "$current_dir" + done +} + +function print_info() { + local log_prompt=$MY_NAME + if [ ! -z "$2" ]; then + log_prompt+=" line $2" + fi + echo "[$log_prompt]: ${GREEN}$1${END}" +} + +function print_warn() { + local log_prompt=$MY_NAME + if [ ! -z "$2" ]; then + log_prompt+=" line $2" + fi + echo "[$log_prompt]: ${ORANGE}$1${END}" +} + +function print_error() { + local log_prompt=$MY_NAME + if [ ! -z "$2" ]; then + log_prompt+=" line $2" + fi + echo -e "[$log_prompt]: ${RED}$1${END}" + cd $OLD_PWD + exit 1 +} + +function set_platform_repo () { + print_warn "Build environment target product '${TARGET_PRODUCT}' does not match expected $1. \ + Reset build environment" "$LINENO" + local lunch_cli="source build/envsetup.sh && lunch $1" + if [ -f "build/release/release_configs/trunk_staging.textproto" ]; then + lunch_cli+="-trunk_staging-$DEVICE_VARIANT" + else + lunch_cli+="-$DEVICE_VARIANT" + fi + print_info "Setup build environment with: $lunch_cli" "$LINENO" + eval "$lunch_cli" + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "$lunch_cli succeeded" "$LINENO" + else + print_error "$lunch_cli failed" "$LINENO" + fi +} + +function find_repo () { + manifest_output=$(grep -e "superproject" -e "gs-pixel" -e "kernel/private/devices/google/common" \ + -e "private/google-modules/soc/gs" -e "kernel/common" -e "common-modules/virtual-device" \ + .repo/manifests/default.xml) + case "$manifest_output" in + *platform/superproject*) + PLATFORM_REPO_ROOT="$PWD" + if [ -z "$PLATFORM_BUILD" ]; then + PLATFORM_VERSION=$(grep -e "platform/superproject" .repo/manifests/default.xml | \ + grep -oP 'revision="\K[^"]*') + print_info "PLATFORM_REPO_ROOT=$PLATFORM_REPO_ROOT, PLATFORM_VERSION=$PLATFORM_VERSION" "$LINENO" + PLATFORM_BUILD="$PLATFORM_REPO_ROOT" + fi + ;; + *kernel/private/devices/google/common*|*private/google-modules/soc/gs*) + VENDOR_KERNEL_REPO_ROOT="$PWD" + if [ -z "$VENDOR_KERNEL_BUILD" ]; then + VENDOR_KERNEL_VERSION=$(grep -e "default revision" .repo/manifests/default.xml | \ + grep -oP 'revision="\K[^"]*') + print_info "VENDOR_KERNEL_REPO_ROOT=$VENDOR_KERNEL_REPO_ROOT" "$LINENO" + print_info "VENDOR_KERNEL_VERSION=$VENDOR_KERNEL_VERSION" "$LINENO" + VENDOR_KERNEL_BUILD="$VENDOR_KERNEL_REPO_ROOT" + fi + ;; + *common-modules/virtual-device*) + KERNEL_REPO_ROOT="$PWD" + if [ -z "$KERNEL_BUILD" ]; then + KERNEL_VERSION=$(grep -e "kernel/superproject" \ + .repo/manifests/default.xml | grep -oP 'revision="common-\K[^"]*') + print_info "KERNEL_REPO_ROOT=$KERNEL_REPO_ROOT, KERNEL_VERSION=$KERNEL_VERSION" "$LINENO" + KERNEL_BUILD="$KERNEL_REPO_ROOT" + fi + ;; + *) + print_warn "Unknown manifest output. Could not determine repository type." "$LINENO" + ;; + esac +} + +function build_platform () { + if [[ "$SKIP_BUILD" = true ]]; then + print_warn "--skip-build is set. Do not rebuild platform build" "$LINENO" + return + fi + build_cmd="m -j12 ; make otatools -j12 ; make dist -j12" + print_warn "Flag --skip-build is not set. Rebuilt images at $PWD with: $build_cmd" "$LINENO" + eval $build_cmd + exit_code=$? + if [ $exit_code -eq 1 ]; then + print_warn "$build_cmd returned exit_code $exit_code" "$LINENO" + print_error "$build_cmd failed" "$LINENO" + else + if [ -f "${ANDROID_PRODUCT_OUT}/system.img" ]; then + print_info "${ANDROID_PRODUCT_OUT}/system.img exist" "$LINENO" + else + print_error "${ANDROID_PRODUCT_OUT}/system.img doesn't exist" "$LINENO" + fi + fi +} + +function build_ack () { + if [[ "$SKIP_BUILD" = true ]]; then + print_warn "--skip-build is set. Do not rebuild kernel" "$LINENO" + return + fi + build_cmd="tools/bazel run --config=fast" + if [ "$GCOV" = true ]; then + build_cmd+=" --gcov" + fi + if [ "$DEBUG" = true ]; then + build_cmd+=" --debug" + fi + if [ "$KASAN" = true ]; then + build_cmd+=" --kasan" + fi + build_cmd+=" //common:kernel_aarch64_dist" + print_warn "Flag --skip-build is not set. Rebuild the kernel with: $build_cmd." "$LINENO" + eval $build_cmd + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "$build_cmd succeeded" "$LINENO" + else + print_error "$build_cmd failed" "$LINENO" + fi +} + +function format_ab_platform_build_string() { + if [[ "$PLATFORM_BUILD" != ab://* ]]; then + print_error "Please provide the platform build in the form of ab:// with flag -pb" "$LINENO" + return 1 + fi + IFS='/' read -ra array <<< "$PLATFORM_BUILD" + local _branch="${array[2]}" + local _build_target="${array[3]}" + local _build_id="${array[4]}" + if [ -z "$_branch" ]; then + print_info "Branch is not specified in platform build as ab://<branch>. Using git_main branch" "$LINENO" + _branch="git_main" + fi + if [ -z "$_build_target" ]; then + if [ ! -z "$PRODUCT" ]; then + _build_target="$PRODUCT-userdebug" + else + print_error "Can not find platform build target through device info. Please \ + provide platform build in the form of ab://<branch>/<build_target> or \ + ab://<branch>/<build_target>/<build_id>" "$LINENO" + fi + fi + if [[ "$_branch" == aosp-main* ]] || [[ "$_branch" == git_main* ]]; then + if [[ "$_build_target" != *-trunk_staging-* ]] || [[ "$_build_target" != *-next-* ]] || [[ "$_build_target" != *-trunk_food-* ]]; then + _build_target="${_build_target/-user/-trunk_staging-user}" + fi + fi + if [ -z "$_build_id" ]; then + _build_id="latest" + fi + PLATFORM_BUILD="ab://$_branch/$_build_target/$_build_id" + print_info "Platform build to be used is $PLATFORM_BUILD" "$LINENO" +} + +function format_ab_kernel_build_string() { + if [[ "$KERNEL_BUILD" != ab://* ]]; then + print_error "Please provide the kernel build in the form of ab:// with flag -kb" "$LINENO" + return 1 + fi + IFS='/' read -ra array <<< "$KERNEL_BUILD" + local _branch="${array[2]}" + local _build_target="${array[3]}" + local _build_id="${array[4]}" + if [ -z "$_branch" ]; then + if [ -z "$DEVICE_KERNEL_VERSION" ]; then + print_error "Branch is not provided in kernel build $KERNEL_BUILD. \ + The kernel version can not be retrieved from device to decide GKI kernel build" "$LINENO" + fi + print_info "Branch is not specified in kernel build as ab://<branch>. Using $DEVICE_KERNEL_VERSION kernel branch." "$LINENO" + _branch="$DEVICE_KERNEL_VERSION" + fi + if [[ "$_branch" == "android"* ]]; then + _branch="aosp_kernel-common-$_branch" + fi + if [ -z "$_build_target" ]; then + _build_target="kernel_aarch64" + fi + if [ -z "$_build_id" ]; then + _build_id="latest" + fi + KERNEL_BUILD="ab://$_branch/$_build_target/$_build_id" + print_info "GKI kernel build to be used is $KERNEL_BUILD" "$LINENO" +} + +function format_ab_vendor_kernel_build_string() { + if [[ "$VENDOR_KERNEL_BUILD" != ab://* ]]; then + print_error "Please provide the vendor kernel build in the form of ab:// with flag -vkb" "$LINENO" + return 1 + fi + IFS='/' read -ra array <<< "$VENDOR_KERNEL_BUILD" + local _branch="${array[2]}" + local _build_target="${array[3]}" + local _build_id="${array[4]}" + if [ -z "$_branch" ]; then + if [ -z "$DEVICE_KERNEL_VERSION" ]; then + print_error "Branch is not provided in vendor kernel build $VENDOR_KERNEL_BUILD. \ + The kernel version can not be retrieved from device to decide vendor kernel build" "$LINENO" + fi + print_info "Branch is not specified in kernel build as ab://<branch>. Using $DEVICE_KERNEL_VERSION vendor kernel branch." "$LINENO" + _branch="$DEVICE_KERNEL_VERSION" + fi + case "$_branch" in + android-mainline ) + if [[ "$PRODUCT" == "raven" ]] || [[ "$PRODUCT" == "oriole" ]]; then + _branch="kernel-android-gs-pixel-mainline" + if [ -z "$_build_target" ]; then + _build_target="kernel_raviole_kleaf" + fi + else + print_error "There is no vendor kernel branch $_branch for $PRODUCT device" "$LINENO" + fi + ;; + android16-6.12 ) + if [[ "$PRODUCT" == "raven" ]] || [[ "$PRODUCT" == "oriole" ]]; then + _branch="kernel-android16-6.12-gs101" + if [ -z "$_build_target" ]; then + _build_target="kernel_raviole" + fi + else + print_error "There is no vendor kernel branch $_branch for $PRODUCT device" "$LINENO" + fi + ;; + android15-6.6 ) + if [[ "$PRODUCT" == "raven" ]] || [[ "$PRODUCT" == "oriole" ]]; then + _branch="kernel-android15-gs-pixel-6.6" + if [ -z "$_build_target" ]; then + _build_target="kernel_raviole" + fi + else + _branch="kernel-pixel-android15-gs-pixel-6.6" + fi + ;; + android14-6.1 ) + _branch="kernel-android14-gs-pixel-6.1" + ;; + android14-5.15 ) + if [[ "$PRODUCT" == "husky" ]] || [[ "$PRODUCT" == "shiba" ]]; then + _branch="kernel-android14-gs-pixel-5.15" + if [ -z "$_build_target" ]; then + _build_target="shusky" + fi + elif [[ "$PRODUCT" == "akita" ]]; then + _branch="kernel-android14-gs-pixel-5.15" + if [ -z "$_build_target" ]; then + _build_target="akita" + fi + else + print_error "There is no vendor kernel branch $_branch for $PRODUCT device" "$LINENO" + fi + ;; + android13-5.15 ) + if [[ "$PRODUCT" == "raven" ]] || [[ "$PRODUCT" == "oriole" ]]; then + _branch="kernel-android13-gs-pixel-5.15-gs101" + if [ -z "$_build_target" ]; then + _build_target="kernel_raviole_kleaf" + fi + else + print_error "There is no vendor kernel branch $_branch for $PRODUCT device" "$LINENO" + fi + ;; + android13-5.10 ) + if [[ "$PRODUCT" == "raven" ]] || [[ "$PRODUCT" == "oriole" ]]; then + _branch="kernel-android13-gs-pixel-5.10" + if [ -z "$_build_target" ]; then + _build_target="slider_gki" + fi + elif [[ "$PRODUCT" == "felix" ]] || [[ "$PRODUCT" == "lynx" ]] || [[ "$PRODUCT" == "tangorpro" ]]; then + _branch="kernel-android13-gs-pixel-5.10" + if [ -z "$_build_target" ]; then + _build_target="$PRODUCT" + fi + else + print_error "There is no vendor kernel branch $_branch for $PRODUCT device" "$LINENO" + fi + ;; + android12-5.10 ) + print_error "There is no vendor kernel branch $_branch for $PRODUCT device" "$LINENO" + ;; + esac + if [ -z "$_build_target" ]; then + case "$PRODUCT" in + caiman | komodo | tokay ) + _build_target="caimito" + ;; + husky | shiba ) + _build_target="shusky" + ;; + panther | cheetah ) + _build_target="pantah" + ;; + raven | oriole ) + _build_target="raviole" + ;; + * ) + _build_target="$PRODUCT" + ;; + esac + fi + if [ -z "$_build_id" ]; then + _build_id="latest" + fi + VENDOR_KERNEL_BUILD="ab://$_branch/$_build_target/$_build_id" + print_info "Vendor kernel build to be used is $VENDOR_KERNEL_BUILD" "$LINENO" +} + +function download_platform_build() { + print_info "Downloading $PLATFORM_BUILD to $PWD" "$LINENO" + local _build_info="$PLATFORM_BUILD" + local _file_patterns=("*$PRODUCT-img-*.zip" "bootloader.img" "radio.img" "misc_info.txt" "otatools.zip") + if [[ "$1" == *git_sc* ]]; then + _file_patterns+=("ramdisk.img") + elif [[ "$1" == *user/* ]]; then + _file_patterns+=("vendor_ramdisk-debug.img") + else + _file_patterns+=("vendor_ramdisk.img") + fi + + for _pattern in "${_file_patterns[@]}"; do + print_info "Downloading $_build_info/$_pattern" "$LINENO" + eval "$FETCH_SCRIPT $_build_info/$_pattern" + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "Downloading $_build_info/$_pattern succeeded" "$LINENO" + else + print_error "Downloading $_build_info/$_pattern failed" "$LINENO" + fi + if [[ "$_pattern" == "vendor_ramdisk-debug.img" ]]; then + cp vendor_ramdisk-debug.img vendor_ramdisk.img + fi + done + echo "" +} + +function download_gki_build() { + print_info "Downloading $1 to $PWD" "$LINENO" + local _build_info="$1" + local _file_patterns=( "boot-lz4.img" ) + + if [[ "$PRODUCT" == "oriole" ]] || [[ "$PRODUCT" == "raven" ]]; then + if [[ "$_build_info" != *android13* ]]; then + _file_patterns+=("system_dlkm_staging_archive.tar.gz" "kernel_aarch64_Module.symvers") + fi + else + _file_patterns+=("system_dlkm.img") + fi + for _pattern in "${_file_patterns[@]}"; do + print_info "Downloading $_build_info/$_pattern" "$LINENO" + eval "$FETCH_SCRIPT $_build_info/$_pattern" + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "Downloading $_build_info/$_pattern succeeded" "$LINENO" + else + print_error "Downloading $_build_info/$_pattern failed" "$LINENO" + fi + if [[ "$_pattern" == "boot-lz4.img" ]]; then + cp boot-lz4.img boot.img + fi + done + echo "" +} + +function download_vendor_kernel_build() { + print_info "Downloading $1 to $PWD" "$LINENO" + local _build_info="$1" + local _file_patterns=("Image.lz4" "dtbo.img" "initramfs.img") + + if [[ "$VENDOR_KERNEL_VERSION" == *6.6 ]]; then + _file_patterns+=("*vendor_dev_nodes_fragment.img") + fi + + case "$PRODUCT" in + oriole | raven | bluejay) + _file_patterns+=( "gs101-a0.dtb" "gs101-b0.dtb" ) + if [[ "$_build_info" == *android13* ]] || [ -z "$KERNEL_BUILD" ]; then + _file_patterns+=("vendor_dlkm.img") + else + _file_patterns+=("vendor_dlkm_staging_archive.tar.gz" "vendor_dlkm.props" "vendor_dlkm_file_contexts" \ + "kernel_aarch64_Module.symvers" "abi_gki_aarch64_pixel") + if [[ "$_build_info" == *android15* ]] && [[ "$_build_info" == *6.6* ]]; then + _file_patterns+=("vendor_dev_nodes_fragment.img" 'vendor-bootconfig.img') + elif [[ "$_build_info" == *pixel-mainline* ]]; then + _file_patterns+=("vendor-bootconfig.img") + fi + fi + ;; + felix | lynx | cheetah | tangorpro) + _file_patterns+=("vendor_dlkm.img" "system_dlkm.img" "gs201-a0.dtb" "gs201-a0.dtb" ) + ;; + shiba | husky | akita) + _file_patterns+=("vendor_dlkm.img" "system_dlkm.img" "zuma-a0-foplp.dtb" "zuma-a0-ipop.dtb" "zuma-b0-foplp.dtb" "zuma-b0-ipop.dtb" ) + ;; + caiman | komodo | tokay | comet) + _file_patterns+=("vendor_dlkm.img" "system_dlkm.img" "zuma-a0-foplp.dtb" "zuma-a0-ipop.dtb" "zuma-b0-foplp.dtb" "zuma-b0-ipop.dtb" \ + "zumapro-a0-foplp.dtb" "zumapro-a0-ipop.dtb" "zumapro-a1-foplp.dtb" "zumapro-a1-ipop.dtb" ) + ;; + *) + _file_pattern+=("vendor_dlkm.img" "system_dlkm.img" "*-a0-foplp.dtb" "*-a0-ipop.dtb" "*-a1-foplp.dtb" \ + "*-a1-ipop.dtb" "*-a0.dtb" "*-b0.dtb") + ;; + esac + + for _pattern in "${_file_patterns[@]}"; do + print_info "Downloading $_build_info/$_pattern" "$LINENO" + eval "$FETCH_SCRIPT $_build_info/$_pattern" + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "Downloading $_build_info/$_pattern succeeded" "$LINENO" + if [[ "$_pattern" == "vendor_dev_nodes_fragment.img" ]]; then + cp vendor_dev_nodes_fragment.img vendor_ramdisk_fragment_extra.img + fi + if [[ "$_pattern" == "abi_gki_aarch64_pixel" ]]; then + cp abi_gki_aarch64_pixel extracted_symbols + fi + else + print_warn "Downloading $_build_info/$_pattern failed" "$LINENO" + fi + done + echo "" +} + +function download_vendor_kernel_for_direct_flash() { + print_info "Downloading $1 to $PWD" "$LINENO" + local build_info="$1" + + for pattern in "${VENDOR_KERNEL_IMGS[@]}"; do + print_info "Downloading $_build_info/$_pattern" "$LINENO" + eval "$FETCH_SCRIPT $build_info/$pattern" + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "Downloading $build_info/$pattern succeeded" "$LINENO" + else + print_error "Downloading $build_info/$pattern failed" "$LINENO" + fi + done + echo "" + +} + +function flash_gki_build() { + local _flash_cmd + if [[ "$KERNEL_BUILD" == ab://* ]]; then + IFS='/' read -ra array <<< "$KERNEL_BUILD" + KERNEL_VERSION=$(echo "${array[2]}" | sed "s/aosp_kernel-common-//g") + _flash_cmd="$CL_FLASH_CLI --nointeractive -w -s $DEVICE_SERIAL_NUMBER " + _flash_cmd+=" -t ${array[3]}" + if [ ! -z "${array[4]}" ] && [[ "${array[4]}" != latest* ]]; then + _flash_cmd+=" --bid ${array[4]}" + else + _flash_cmd+=" -l ${array[2]}" + fi + elif [ -d "$KERNEL_BUILD" ]; then + _flash_cmd="$LOCAL_FLASH_CLI --nointeractive -w --kernel_dist_dir=$KERNEL_BUILD -s $DEVICE_SERIAL_NUMBER" + else + print_error "Can not flash GKI kernel from $KERNEL_BUILD" "$LINENO" + fi + + IFS='-' read -ra array <<< "$KERNEL_VERSION" + KERNEL_VERSION="${array[0]}-${array[1]}" + print_info "$KERNEL_BUILD is KERNEL_VERSION $KERNEL_VERSION" "$LINENO" + if [ ! -z "$DEVICE_KERNEL_VERSION" ] && [[ "$KERNEL_VERSION" != "$DEVICE_KERNEL_VERSION"* ]]; then + print_warn "Device $PRODUCT $SERIAL_NUMBER comes with $DEVICE_KERNEL_STRING $DEVICE_KERNEL_VERSION kernel. \ + Can't flash $KERNEL_VERSION GKI directly. Please use a platform build with the $KERNEL_VERSION kernel \ + or use a vendor kernel build by flag -vkb, for example -vkb -vkb ab://kernel-${array[0]}-gs-pixel-${array[1]}/<kernel_target>/latest" "$LINENO" + print_error "Cannot flash $KERNEL_VERSION GKI to device $SERIAL_NUMBER directly." "$LINENO" + fi + + print_info "Flashing GKI kernel with: $_flash_cmd" "$LINENO" + eval "$_flash_cmd" + exit_code=$? + if [ $exit_code -eq 0 ]; then + echo "Flash GKI kernel succeeded" + wait_for_device_in_adb + return + else + echo "Flash GKI kernel failed with exit code $exit_code" + exit 1 + fi +} + +function check_fastboot_version() { + local _fastboot_version=$(fastboot --version | awk 'NR==1 {print $3}') + + # Check if _fastboot_version is less than MIN_FASTBOOT_VERSION + if [[ "$_fastboot_version" < "$MIN_FASTBOOT_VERSION" ]]; then + print_info "The existing fastboot version $_fastboot_version doesn't meet minimum requirement $MIN_FASTBOOT_VERSION. Download the latest fastboot" "$LINENO" + + local _download_file_name="ab://aosp-sdk-release/sdk/latest/fastboot" + mkdir -p "/tmp/fastboot" || $(print_error "Fail to mkdir /tmp/fastboot" "$LINENO") + cd /tmp/fastboot || $(print_error "Fail to go to /tmp/fastboot" "$LINENO") + + # Use $FETCH_SCRIPT and $_download_file_name correctly + eval "$FETCH_SCRIPT $_download_file_name" + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "Download $_download_file_name succeeded" "$LINENO" + else + print_error "Download $_download_file_name failed" "$LINENO" + fi + + chmod +x /tmp/fastboot/fastboot + export PATH="/tmp/fastboot:$PATH" + + _fastboot_version=$(fastboot --version | awk 'NR==1 {print $3}') + print_info "The fastboot is updated to version $_fastboot_version" "$LINENO" + fi +} + +function flash_vendor_kernel_build() { + check_fastboot_version + + for pattern in "${VENDOR_KERNEL_IMGS[@]}"; do + if [ ! -f "$VENDOR_KERNEL_BUILD/$pattern" ]; then + print_error "$VENDOR_KERNEL_BUILD/$pattern doesn't exist" "$LINENO" + fi + done + + cd $VENDOR_KERNEL_BUILD + + # Switch to flashstatoin after b/390489174 + print_info "Flash vendor kernel from $VENDOR_KERNEL_BUILD" "$LINENO" + if [ ! -z "$ADB_SERIAL_NUMBER" ] && (( $(adb devices | grep "$ADB_SERIAL_NUMBER" | wc -l) > 0 )); then + print_info "Reboot $ADB_SERIAL_NUMBER into bootloader" "$LINENO" + adb -s "$ADB_SERIAL_NUMBER" reboot bootloader + sleep 10 + if [ -z "$FASTBOOT_SERIAL_NUMBER" ]; then + find_fastboot_serial_number + fi + elif [ ! -z "$FASTBOOT_SERIAL_NUMBER" ] && (( $(fastboot devices | grep "$ADB_SERIAL_NUMBER" | wc -l) > 0 )); then + print_info "Reboot $FASTBOOT_SERIAL_NUMBER into bootloader" "$LINENO" + fastboot -s "$FASTBOOT_SERIAL_NUMBER" reboot bootloader + sleep 2 + fi + print_info "Wiping the device" "$LINENO" + fastboot -s "$FASTBOOT_SERIAL_NUMBER" -w + print_info "Disabling oem verification" "$LINENO" + fastboot -s "$FASTBOOT_SERIAL_NUMBER" oem disable-verification + print_info "Flashing boot image" "$LINENO" + fastboot -s "$FASTBOOT_SERIAL_NUMBER" flash boot "$VENDOR_KERNEL_BUILD"/boot.img + print_info "Flashing dtb.img & initramfs.img" "$LINENO" + fastboot -s "$FASTBOOT_SERIAL_NUMBER" flash --dtb "$VENDOR_KERNEL_BUILD"/dtb.img vendor_boot:dlkm "$VENDOR_KERNEL_BUILD"/initramfs.img + print_info "Flashing dtbo.img" "$LINENO" + fastboot -s "$FASTBOOT_SERIAL_NUMBER" flash dtbo "$VENDOR_KERNEL_BUILD"/dtbo.img + print_info "Reboot into fastbootd" "$LINENO" + fastboot -s "$FASTBOOT_SERIAL_NUMBER" reboot fastboot + sleep 10 + print_info "Flashing vendor_dlkm.img" "$LINENO" + fastboot -s "$FASTBOOT_SERIAL_NUMBER" flash vendor_dlkm "$VENDOR_KERNEL_BUILD"/vendor_dlkm.img + print_info "Reboot the device" "$LINENO" + fastboot -s "$FASTBOOT_SERIAL_NUMBER" reboot + wait_for_device_in_adb +} + +# Function to check and wait for an ADB device +function wait_for_device_in_adb() { + local timeout_seconds="${2:-300}" # Timeout in seconds (default 5 minutes) + + local start_time=$(date +%s) + local end_time=$((start_time + timeout_seconds)) + + while (( $(date +%s) < end_time )); do + if [ -z "$ADB_SERIAL_NUMBER" ] && [ -x pontis ]; then + local _pontis_device=$(pontis devices | grep "$DEVICE_SERIAL_NUMBER") + if [[ "$_pontis_device" == *ADB* ]]; then + print_info "Device $DEVICE_SERIAL_NUMBER is connected through pontis in adb" "$LINENO" + find_adb_serial_number + get_device_info_from_adb + return 0 # Success + else + sleep 5 + fi + else + devices=$(adb devices | grep "$ADB_SERIAL_NUMBER" | wc -l) + + if (( devices > 0 )); then + print_info "Device $ADB_SERIAL_NUMBER is connected with adb" "$LINENO" + return 0 # Success + fi + print_info "Waiting for device $ADB_SERIAL_NUMBER in adb devices" "$LINENO" + sleep 5 + fi + done + + print_error "Timeout waiting for $ADB_SERIAL_NUMBER in adb devices" "$LINENO" +} + +function find_flashstation_binary() { + if [ -x "${ANDROID_HOST_OUT}/bin/local_flashstation" ]; then + $LOCAL_FLASH_CLI="${ANDROID_HOST_OUT}/bin/local_flashstation" + elif [ ! -x "$LOCAL_FLASH_CLI" ]; then + if ! which local_flashstation &> /dev/null; then + print_error "Can not find local_flashstation binary. \ + Please see go/web-flashstation-command-line to download it" "$LINENO" + else + LOCAL_FLASH_CLI="local_flashstation" + fi + fi + if [ -x "${ANDROID_HOST_OUT}/bin/cl_flashstation" ]; then + $CL_FLASH_CLI="${ANDROID_HOST_OUT}/bin/cl_flashstation" + elif [ ! -x "$CL_FLASH_CLI" ]; then + if ! which cl_flashstation &> /dev/null; then + print_error "Can not find cl_flashstation binary. \ + Please see go/web-flashstation-command-line to download it" "$LINENO" + else + CL_FLASH_CLI="cl_flashstation" + fi + fi +} + +function flash_platform_build() { + local _flash_cmd + if [[ "$PLATFORM_BUILD" == ab://* ]]; then + _flash_cmd="$CL_FLASH_CLI --nointeractive --force_flash_partitions --disable_verity -w -s $DEVICE_SERIAL_NUMBER " + IFS='/' read -ra array <<< "$PLATFORM_BUILD" + if [ ! -z "${array[3]}" ]; then + local _build_type="${array[3]#*-}" + if [[ "${array[2]}" == git_main* ]] && [[ "$_build_type" == user* ]]; then + print_info "Build variant is not provided, using trunk_staging build" "$LINENO" + _build_type="trunk_staging-$_build_type" + fi + _flash_cmd+=" -t $_build_type" + if [[ "$_build_type" == *user ]] && [ ! -z "$KERNEL_BUILD" ] && [ -z "$VENDOR_KERNEL_BUILD" ]; then + print_info "Need to flash GKI after flashing platform build, hence enabling --force_debuggable in user build flashing" "$LINENO" + _flash_cmd+=" --force_debuggable" + fi + fi + if [ ! -z "${array[4]}" ] && [[ "${array[4]}" != latest* ]]; then + echo "Flash $SERIAL_NUMBER with platform build from branch $PLATFORM_BUILD..." + _flash_cmd+=" --bid ${array[4]}" + else + echo "Flash $SERIAL_NUMBER with platform build $PLATFORM_BUILD..." + _flash_cmd+=" -l ${array[2]}" + fi + elif [ ! -z "$PLATFORM_REPO_ROOT" ] && [[ "$PLATFORM_BUILD" == "$PLATFORM_REPO_ROOT/out/target/product/$PRODUCT" ]] && \ + [ -x "$PLATFORM_REPO_ROOT/vendor/google/tools/flashall" ]; then + cd "$PLATFORM_REPO_ROOT" + print_info "Flashing device with vendor/google/tools/flashall" "$LINENO" + if [ -z "${TARGET_PRODUCT}" ] || [[ "${TARGET_PRODUCT}" != *"$PRODUCT" ]]; then + if [[ "$PLATFORM_VERSION" == aosp-* ]]; then + set_platform_repo "aosp_$PRODUCT" + else + set_platform_repo "$PRODUCT" + fi + fi + _flash_cmd="vendor/google/tools/flashall --nointeractive -w -s $DEVICE_SERIAL_NUMBER" + else + if [ -z "${TARGET_PRODUCT}" ]; then + export TARGET_PRODUCT="$PRODUCT" + fi + if [ -z "${TARGET_BUILD_VARIANT}" ]; then + export TARGET_BUILD_VARIANT="$DEVICE_VARIANT" + fi + if [ -z "${ANDROID_PRODUCT_OUT}" ] || [[ "${ANDROID_PRODUCT_OUT}" != "$PLATFORM_BUILD" ]] ; then + export ANDROID_PRODUCT_OUT="$PLATFORM_BUILD" + fi + if [ -z "${ANDROID_HOST_OUT}" ]; then + export ANDROID_HOST_OUT="$PLATFORM_BUILD" + fi + if [ ! -f "$PLATFORM_BUILD/system.img" ]; then + local device_image=$(find "$PLATFORM_BUILD" -maxdepth 1 -type f -name *-img*.zip) + unzip -j "$device_image" -d "$PLATFORM_BUILD" + fi + + awk '! /baseband/' "$PLATFORM_BUILD"/android-info.txt > temp && mv temp "$PLATFORM_BUILD"/android-info.txt + awk '! /bootloader/' "$PLATFORM_BUILD"/android-info.txt > temp && mv temp "$PLATFORM_BUILD"/android-info.txt + + _flash_cmd="$LOCAL_FLASH_CLI --nointeractive --force_flash_partitions --disable_verity --disable_verification -w -s $DEVICE_SERIAL_NUMBER" + fi + print_info "Flashing device with: $_flash_cmd" "$LINENO" + eval "$_flash_cmd" + exit_code=$? + if [ $exit_code -eq 0 ]; then + echo "Flash platform succeeded" + wait_for_device_in_adb + return + else + echo "Flash platform build failed with exit code $exit_code" + exit 1 + fi + +} + +function get_mix_ramdisk_script() { + download_file_name="ab://git_main/aosp_cf_x86_64_only_phone-trunk_staging-userdebug/latest/otatools.zip" + eval "$FETCH_SCRIPT $download_file_name" + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "Download $download_file_name succeeded" "$LINENO" + else + print_error "Download $download_file_name failed" "$LINENO" "$LINENO" + fi + eval "unzip -j otatools.zip bin/$MIX_SCRIPT_NAME" + echo "" +} + +function mixing_build() { + if [ ! -z ${PLATFORM_REPO_ROOT_PATH} ] && [ -f "$PLATFORM_REPO_ROOT_PATH/vendor/google/tools/$MIX_SCRIPT_NAME"]; then + mix_kernel_cmd="$PLATFORM_REPO_ROOT_PATH/vendor/google/tools/$MIX_SCRIPT_NAME" + elif [ -f "$DOWNLOAD_PATH/$MIX_SCRIPT_NAME" ]; then + mix_kernel_cmd="$DOWNLOAD_PATH/$MIX_SCRIPT_NAME" + else + cd "$DOWNLOAD_PATH" || $(print_error "Fail to go to $DOWNLOAD_PATH" "$LINENO") + get_mix_ramdisk_script + mix_kernel_cmd="$PWD/$MIX_SCRIPT_NAME" + fi + if [ ! -f "$mix_kernel_cmd" ]; then + print_error "$mix_kernel_cmd doesn't exist or is not executable" "$LINENO" + elif [ ! -x "$mix_kernel_cmd" ]; then + print_error "$mix_kernel_cmd is not executable" "$LINENO" + fi + if [[ "$PLATFORM_BUILD" == ab://* ]]; then + if [ -d "$DOWNLOAD_PATH/device_dir" ]; then + rm -rf "$DOWNLOAD_PATH/device_dir" + fi + PLATFORM_DIR="$DOWNLOAD_PATH/device_dir" + mkdir -p "$PLATFORM_DIR" + cd "$PLATFORM_DIR" || $(print_error "Fail to go to $PLATFORM_DIR" "$LINENO") + download_platform_build + PLATFORM_BUILD="$PLATFORM_DIR" + elif [ ! -z "$PLATFORM_REPO_ROOT" ] && [[ "$PLATFORM_BUILD" == "$PLATFORM_REPO_ROOT"* ]]; then + print_info "Copy platform build $PLATFORM_BUILD to $DOWNLOAD_PATH/device_dir" "$LINENO" + PLATFORM_DIR="$DOWNLOAD_PATH/device_dir" + mkdir -p "$PLATFORM_DIR" + cd "$PLATFORM_DIR" || $(print_error "Fail to go to $PLATFORM_DIR" "$LINENO") + local device_image=$(find "$PLATFORM_BUILD" -maxdepth 1 -type f -name *-img.zip) + if [ ! -z "device_image" ]; then + cp "$device_image $PLATFORM_DIR/$PRODUCT-img-0.zip" "$PLATFORM_DIR" + else + device_image=$(find "$PLATFORM_BUILD" -maxdepth 1 -type f -name *-img-*.zip) + if [ ! -z "device_image" ]; then + cp "$device_image $PLATFORM_DIR/$PRODUCT-img-0.zip" "$PLATFORM_DIR" + else + print_error "Can't find $RPODUCT-img-*.zip in $PLATFORM_BUILD" + fi + fi + local file_patterns=("bootloader.img" "radio.img" "vendor_ramdisk.img" "misc_info.txt" "otatools.zip") + for pattern in "${file_patterns[@]}"; do + cp "$PLATFORM_BUILD/$pattern" "$PLATFORM_DIR/$pattern" + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "Copied $PLATFORM_BUILD/$pattern to $PLATFORM_DIR" "$LINENO" + else + print_error "Failed to copy $PLATFORM_BUILD/$pattern to $PLATFORM_DIR" "$LINENO" + fi + done + PLATFORM_BUILD="$PLATFORM_DIR" + fi + + if [[ "$KERNEL_BUILD" == ab://* ]]; then + print_info "Download kernel build $KERNEL_BUILD" "$LINENO" + if [ -d "$DOWNLOAD_PATH/gki_dir" ]; then + rm -rf "$DOWNLOAD_PATH/gki_dir" + fi + GKI_DIR="$DOWNLOAD_PATH/gki_dir" + mkdir -p "$GKI_DIR" + cd "$GKI_DIR" || $(print_error "Fail to go to $GKI_DIR" "$LINENO") + download_gki_build $KERNEL_BUILD + KERNEL_BUILD="$GKI_DIR" + fi + + local new_device_dir="$DOWNLOAD_PATH/new_device_dir" + if [ -d "$new_device_dir" ]; then + rm -rf "$new_device_dir" + fi + mkdir -p "$new_device_dir" + local mixed_build_cmd="$mix_kernel_cmd" + if [ -d "${KERNEL_BUILD}" ]; then + mixed_build_cmd+=" --gki_dir $KERNEL_BUILD" + fi + mixed_build_cmd+=" $PLATFORM_BUILD $VENDOR_KERNEL_BUILD $new_device_dir" + print_info "Run: $mixed_build_cmd" "$LINENO" + eval $mixed_build_cmd + device_image=$(ls $new_device_dir/*$PRODUCT-img*.zip) + if [ ! -f "$device_image" ]; then + print_error "New device image is not created in $new_device_dir" "$LINENO" + fi + cp "$PLATFORM_BUILD"/bootloader.img $new_device_dir/. + cp "$PLATFORM_BUILD"/radio.img $new_device_dir/. + PLATFORM_BUILD="$new_device_dir" +} + +get_kernel_version_from_boot_image() { + local boot_image_path="$1" + local version_output + + # Check for mainline kernel + version_output=$(strings "$boot_image_path" | grep mainline) + if [ ! -z "$version_output" ]; then + KERNEL_VERSION="android-mainline" + return # Exit the function early if a match is found + fi + + # Check for Android 15 6.6 kernel + version_output=$(strings "$boot_image_path" | grep "android15" | grep "6.6") + if [ ! -z "$version_output" ]; then + KERNEL_VERSION="android15-6.6" + return + fi + + # Check for Android 14 6.1 kernel + version_output=$(strings "$boot_image_path" | grep "android14" | grep "6.1") + if [ ! -z "$version_output" ]; then + KERNEL_VERSION="android14-6.1" + return + fi + + # Check for Android 14 5.15 kernel + version_output=$(strings "$boot_image_path" | grep "android14" | grep "5.15") + if [ ! -z "$version_output" ]; then + KERNEL_VERSION="android14-5.15" + return + fi + + # Check for Android 13 5.15 kernel + version_output=$(strings "$boot_image_path" | grep "android13" | grep "5.15") + if [ ! -z "$version_output" ]; then + KERNEL_VERSION="android13-5.15" + return + fi + + # Check for Android 13 5.10 kernel + version_output=$(strings "$boot_image_path" | grep "android13" | grep "5.10") + if [ ! -z "$version_output" ]; then + KERNEL_VERSION="android13-5.10" + return + fi + + # Check for Android 12 5.10 kernel + version_output=$(strings "$boot_image_path" | grep "android12" | grep "5.10") + if [ ! -z "$version_output" ]; then + KERNEL_VERSION="android12-5.10" + return + fi +} + +function extract_device_kernel_version() { + local kernel_string="$1" + # Check if the string contains '-android' + if [[ "$kernel_string" == *"-mainline"* ]]; then + DEVICE_KERNEL_VERSION="android-mainline" + elif [[ "$kernel_string" == *"-android"* ]]; then + # Extract the substring between the first hyphen and the second hyphen + DEVICE_KERNEL_VERSION=$(echo "$kernel_string" | awk -F '-' '{print $2"-"$1}' | cut -d '.' -f -2) + else + print_warn "Can not parse $kernel_string into kernel version" "$LINENO" + fi + print_info "Device kernel version is $DEVICE_KERNEL_VERSION" "$LINENO" +} + +function find_adb_serial_number() { + print_info "Try to find device $DEVICE_SERIAL_NUMBER serial id in adb devices" "$LINENO" + local _device_ids=$(adb devices | awk '$2 == "device" {print $1}') + devices=() + while IFS= read -r device_id; do + devices+=("$device_id") + done <<< "$_device_ids" + + for device_id in "${devices[@]}"; do + local _device_serial_number=$(adb -s "$device_id" shell getprop ro.serialno) + #echo "DEVICE $device_id has serialno $_device_serial_number" + if [[ "$_device_serial_number" == "$DEVICE_SERIAL_NUMBER" ]]; then + ADB_SERIAL_NUMBER="$device_id" + print_info "Device $DEVICE_SERIAL_NUMBER shows up as $ADB_SERIAL_NUMBER in adb" "$LINENO" + return 0 + fi + done + print_error "Can not find device in adb has device serial number $DEVICE_SERIAL_NUMBER. \ + Check if the device is connected with adb authentication" "$LINENO" +} + +function find_fastboot_serial_number() { + print_info "Try to find device $DEVICE_SERIAL_NUMBER serial id in fastboot devices" "$LINENO" + local _output=$(fastboot devices | awk '{print $1}') + while IFS= read -r device_id; do + # Use fastboot getvar to retrieve serial number + local _output=$(fastboot -s "$device_id" getvar serialno 2>&1) + local _device_serial_number=$(echo "$_output" | grep -Po "serialno: [A-Z0-9]+" | cut -c 11-) + #echo "Device $device has serial number $_device_serial_number" + if [[ "$_device_serial_number" == "$DEVICE_SERIAL_NUMBER" ]]; then + FASTBOOT_SERIAL_NUMBER="$device_id" + print_info "Device $DEVICE_SERIAL_NUMBER shows up as $FASTBOOT_SERIAL_NUMBER in fastboot" "$LINENO" + return 0 + fi + done <<< "$_output" + print_error "Can not find device in fastboot has device serial number $DEVICE_SERIAL_NUMBER" "$LINENO" +} + +function get_device_info_from_adb { + if [ -z "$DEVICE_SERIAL_NUMBER" ]; then + DEVICE_SERIAL_NUMBER=$(adb -s "$ADB_SERIAL_NUMBER" shell getprop ro.serialno) + if [ -z "$DEVICE_SERIAL_NUMBER" ]; then + print_error "Can not get device serial adb -s $ADB_SERIAL_NUMBER" "$LINENO" + fi + fi + BOARD=$(adb -s "$ADB_SERIAL_NUMBER" shell getprop ro.product.board) + ABI=$(adb -s "$ADB_SERIAL_NUMBER" shell getprop ro.product.cpu.abi) + + # Only get PRODUCT if it's not already set + if [ -z "$PRODUCT" ]; then + PRODUCT=$(adb -s "$ADB_SERIAL_NUMBER" shell getprop ro.build.product) + # Check if PRODUCT is valid after attempting to retrieve it + if [ -z "$PRODUCT" ]; then + print_error "$ADB_SERIAL_NUMBER does not have a valid product value" "$LINENO" + fi + fi + + BUILD_TYPE=$(adb -s "$ADB_SERIAL_NUMBER" shell getprop ro.build.type) + DEVICE_KERNEL_STRING=$(adb -s "$ADB_SERIAL_NUMBER" shell uname -r) + extract_device_kernel_version "$DEVICE_KERNEL_STRING" + SYSTEM_DLKM_INFO=$(adb -s "$ADB_SERIAL_NUMBER" shell getprop dev.mnt.blk.system_dlkm) + if [[ "$SERIAL_NUMBER" != "$DEVICE_SERIAL_NUMBER" ]]; then + print_info "Device $SERIAL_NUMBER has DEVICE_SERIAL_NUMBER=$DEVICE_SERIAL_NUMBER, ADB_SERIAL_NUMBER=$ADB_SERIAL_NUMBER" "$LINENO" + fi + print_info "Device $SERIAL_NUMBER info: BOARD=$BOARD, ABI=$ABI, PRODUCT=$PRODUCT, BUILD_TYPE=$BUILD_TYPE \ + SYSTEM_DLKM_INFO=$SYSTEM_DLKM_INFO, DEVICE_KERNEL_STRING=$DEVICE_KERNEL_STRING" "$LINENO" +} + +function get_device_info_from_fastboot { + # try get product by fastboot command + if [ -z "$DEVICE_SERIAL_NUMBER" ]; then + local _output=$(fastboot -s "$FASTBOOT_SERIAL_NUMBER" getvar serialno 2>&1) + DEVICE_SERIAL_NUMBER=$(echo "$_output" | grep -Po "serialno: [A-Z0-9]+" | cut -c 11-) + if [ -z "$DEVICE_SERIAL_NUMBER" ]; then + print_error "Can not get device serial from $SERIAL_NUMBER" "$LINENO" + fi + fi + + # Only get PRODUCT if it's not already set + if [ -z "$PRODUCT" ]; then + _output=$(fastboot -s "$FASTBOOT_SERIAL_NUMBER" getvar product 2>&1) + PRODUCT=$(echo "$_output" | grep -oP '^product:\s*\K.*' | cut -d' ' -f1) + # Check if PRODUCT is valid after attempting to retrieve it + if [ -z "$PRODUCT" ]; then + print_error "$FASTBOOT_SERIAL_NUMBER does not have a valid product value" "$LINENO" + fi + fi + + if [[ "$SERIAL_NUMBER" != "$DEVICE_SERIAL_NUMBER" ]]; then + print_info "Device $SERIAL_NUMBER has DEVICE_SERIAL_NUMBER=$DEVICE_SERIAL_NUMBER, FASTBOOT_SERIAL_NUMBER=$FASTBOOT_SERIAL_NUMBER" "$LINENO" + fi + print_info "Device $SERIAL_NUMBER is in fastboot with device info: PRODUCT=$PRODUCT" "$LINENO" +} + +function get_device_info() { + local _adb_count=$(adb devices | grep "$SERIAL_NUMBER" | wc -l) + if (( _adb_count > 0 )); then + print_info "$SERIAL_NUMBER is connected through adb" "$LINENO" + ADB_SERIAL_NUMBER="$SERIAL_NUMBER" + get_device_info_from_adb + if [[ "$ADB_SERIAL_NUMBER" == "$DEVICE_SERIAL_NUMBER" ]]; then + FASTBOOT_SERIAL_NUMBER="$SERIAL_NUMBER" + fi + return 0 + fi + + local _fastboot_count=$(fastboot devices | grep "$SERIAL_NUMBER" | wc -l) + if (( _fastboot_count > 0 )); then + print_info "$SERIAL_NUMBER is connected through fastboot" "$LINENO" + FASTBOOT_SERIAL_NUMBER="$SERIAL_NUMBER" + get_device_info_from_fastboot + if [[ "$FASTBOOT_SERIAL_NUMBER" == "$DEVICE_SERIAL_NUMBER" ]]; then + ADB_SERIAL_NUMBER="$SERIAL_NUMBER" + fi + return 0 + fi + + if [ -x pontis ]; then + local _pontis_device=$(pontis devices | grep "$SERIAL_NUMBER") + if [[ "$_pontis_device" == *Fastboot* ]]; then + DEVICE_SERIAL_NUMBER="$SERIAL_NUMBER" + print_info "Device $SERIAL_NUMBER is connected through pontis in fastboot" "$LINENO" + find_fastboot_serial_number + get_device_info_from_fastboot + return 0 + elif [[ "$_pontis_device" == *ADB* ]]; then + DEVICE_SERIAL_NUMBER="$SERIAL_NUMBER" + print_info "Device $SERIAL_NUMBER is connected through pontis in adb" "$LINENO" + find_adb_serial_number + get_device_info_from_adb + return 0 + fi + fi + + print_error "$SERIAL_NUMBER is not connected with adb or fastboot" "$LINENO" +} + +adb_checker + +LOCAL_REPO= + +OLD_PWD=$PWD +MY_NAME=$0 + +parse_arg "$@" + +if [ -z "$SERIAL_NUMBER" ]; then + print_error "Device serial is not provided with flag -s <serial_number>." "$LINENO" + exit 1 +fi + +get_device_info + +FULL_COMMAND_PATH=$(dirname "$PWD/$0") +REPO_LIST_OUT=$(repo list 2>&1) +if [[ "$REPO_LIST_OUT" == "error"* ]]; then + print_error "Current path $PWD is not in an Android repo. Change path to repo root." "$LINENO" + go_to_repo_root "$FULL_COMMAND_PATH" + print_info "Changed path to $PWD" "$LINENO" +else + go_to_repo_root "$PWD" +fi + +REPO_ROOT_PATH="$PWD" +FETCH_SCRIPT="$REPO_ROOT_PATH/$FETCH_SCRIPT" + +find_repo + +if [[ "$PLATFORM_BUILD" == "None" ]]; then + PLATFORM_BUILD= +fi + +if [[ "$KERNEL_BUILD" == "None" ]]; then + KERNEL_BUILD= +fi + +if [[ "$VENDOR_KERNEL_BUILD" == "None" ]]; then + VENDOR_KERNEL_BUILD= +fi + +if [ ! -d "$DOWNLOAD_PATH" ]; then + mkdir -p "$DOWNLOAD_PATH" || $(print_error "Fail to create directory $DOWNLOAD_PATH" "$LINENO") +fi + +if [[ "$PLATFORM_BUILD" == ab://* ]]; then + format_ab_platform_build_string +elif [ ! -z "$PLATFORM_BUILD" ] && [ -d "$PLATFORM_BUILD" ]; then + # Check if PLATFORM_BUILD is an Android platform repo + cd "$PLATFORM_BUILD" || $(print_error "Fail to go to $PLATFORM_BUILD" "$LINENO") + PLATFORM_REPO_LIST_OUT=$(repo list 2>&1) + if [[ "$PLATFORM_REPO_LIST_OUT" != "error"* ]]; then + go_to_repo_root "$PWD" + if [[ "$PWD" != "$REPO_ROOT_PATH" ]]; then + find_repo + fi + if [ "$SKIP_BUILD" = false ] && [[ "$PLATFORM_BUILD" != "ab://"* ]] && [[ ! -z "$PLATFORM_BUILD" ]]; then + if [ -z "${TARGET_PRODUCT}" ] || [[ "${TARGET_PRODUCT}" != *"$PRODUCT" ]]; then + if [[ "$PLATFORM_VERSION" == aosp-* ]]; then + set_platform_repo "aosp_$PRODUCT" + else + set_platform_repo "$PRODUCT" + fi + elif [[ "${TARGET_PRODUCT}" == *"$PRODUCT" ]]; then + echo "TARGET_PRODUCT=${TARGET_PRODUCT}, ANDROID_PRODUCT_OUT=${ANDROID_PRODUCT_OUT}" + fi + if [[ "${TARGET_PRODUCT}" == *"$PRODUCT" ]]; then + build_platform + else + print_error "Can not build platform build due to lunch build target failure" "$LINENO" + fi + fi + if [ -d "${PLATFORM_REPO_ROOT}" ] && [ -f "$PLATFORM_REPO_ROOT/out/target/product/$PRODUCT/otatools.zip" ]; then + PLATFORM_BUILD=$PLATFORM_REPO_ROOT/out/target/product/$PRODUCT + elif [ -d "${ANDROID_PRODUCT_OUT}" ] && [ -f "${ANDROID_PRODUCT_OUT}/otatools.zip" ]; then + PLATFORM_BUILD="${ANDROID_PRODUCT_OUT}" + else + PLATFORM_BUILD= + fi + fi +fi + +if [[ "$SYSTEM_BUILD" == ab://* ]]; then + print_warn "System build is not supoort yet" "$LINENO" +elif [ ! -z "$SYSTEM_BUILD" ] && [ -d "$SYSTEM_BUILD" ]; then + print_warn "System build is not supoort yet" "$LINENO" + # Get GSI build + cd "$SYSTEM_BUILD" || $(print_error "Fail to go to $SYSTEM_BUILD" "$LINENO") + SYSTEM_REPO_LIST_OUT=$(repo list 2>&1) + if [[ "$SYSTEM_REPO_LIST_OUT" != "error"* ]]; then + go_to_repo_root "$PWD" + if [[ "$PWD" != "$REPO_ROOT_PATH" ]]; then + find_repo + fi + if [ -z "${TARGET_PRODUCT}" ] || [[ "${TARGET_PRODUCT}" != "_arm64" ]]; then + set_platform_repo "aosp_arm64" + if [ "$SKIP_BUILD" = false ] ; then + build_platform + fi + SYSTEM_BUILD="${ANDROID_PRODUCT_OUT}/system.img" + fi + fi +fi + +find_flashstation_binary + +if [[ "$KERNEL_BUILD" == ab://* ]]; then + format_ab_kernel_build_string +elif [ ! -z "$KERNEL_BUILD" ] && [ -d "$KERNEL_BUILD" ]; then + # Check if kernel repo is provided + cd "$KERNEL_BUILD" || $(print_error "Fail to go to $KERNEL_BUILD" "$LINENO") + KERNEL_REPO_LIST_OUT=$(repo list 2>&1) + if [[ "$KERNEL_REPO_LIST_OUT" != "error"* ]]; then + go_to_repo_root "$PWD" + if [[ "$PWD" != "$REPO_ROOT_PATH" ]]; then + find_repo + fi + if [ "$SKIP_BUILD" = false ] ; then + if [ ! -f "common/BUILD.bazel" ]; then + # TODO: Add build support to android12 and earlier kernels + print_error "bazel build is not supported in $PWD" "$LINENO" + else + build_ack + fi + fi + KERNEL_BUILD="$PWD/out/kernel_aarch64/dist" + elif [ -f "$KERNEL_BUILD/boot*.img" ]; then + get_kernel_version_from_boot_image "$KERNEL_BUILD/boot*.img" + fi +fi + +if [[ "$VENDOR_KERNEL_BUILD" == ab://* ]]; then + format_ab_vendor_kernel_build_string + print_info "Download vendor kernel build $VENDOR_KERNEL_BUILD" "$LINENO" + if [ -d "$DOWNLOAD_PATH/vendor_kernel_dir" ]; then + rm -rf "$DOWNLOAD_PATH/vendor_kernel_dir" + fi + VENDOR_KERNEL_DIR="$DOWNLOAD_PATH/vendor_kernel_dir" + mkdir -p "$VENDOR_KERNEL_DIR" + cd "$VENDOR_KERNEL_DIR" || $(print_error "Fail to go to $VENDOR_KERNEL_DIR" "$LINENO") + if [ -z "$PLATFORM_BUILD" ]; then + download_vendor_kernel_for_direct_flash $VENDOR_KERNEL_BUILD + else + download_vendor_kernel_build $VENDOR_KERNEL_BUILD + fi + VENDOR_KERNEL_BUILD="$VENDOR_KERNEL_DIR" +elif [ ! -z "$VENDOR_KERNEL_BUILD" ] && [ -d "$VENDOR_KERNEL_BUILD" ]; then + # Check if vendor kernel repo is provided + cd "$VENDOR_KERNEL_BUILD" || $(print_error "Fail to go to $VENDOR_KERNEL_BUILD" "$LINENO") + VENDOR_KERNEL_REPO_LIST_OUT=$(repo list 2>&1) + if [[ "$VENDOR_KERNEL_REPO_LIST_OUT" != "error"* ]]; then + go_to_repo_root "$PWD" + if [[ "$PWD" != "$REPO_ROOT_PATH" ]]; then + find_repo + fi + if [ -z "$VENDOR_KERNEL_BUILD_TARGET" ]; then + kernel_build_target_count=$(ls build_*.sh | wc -w) + if (( kernel_build_target_count == 1 )); then + VENDOR_KERNEL_BUILD_TARGET=$(echo $(ls build_*.sh) | sed 's/build_\(.*\)\.sh/\1/') + elif (( kernel_build_target_count > 1 )); then + print_warn "There are multiple build_*.sh scripts in $PWD, Can't decide vendor kernel build target" "$LINENO" + print_error "Please use -vkbt <value> or --vendor-kernel-build-target=<value> to specify a kernel build target" "$LINENO" + else + # TODO: Add build support to android12 and earlier kernels + print_error "There is no build_*.sh script in $PWD" "$LINENO" + fi + fi + if [ "$SKIP_BUILD" = false ] ; then + build_cmd="./build_$VENDOR_KERNEL_BUILD_TARGET.sh" + if [ "$GCOV" = true ]; then + build_cmd+=" --gcov" + fi + if [ "$DEBUG" = true ]; then + build_cmd+=" --debug" + fi + if [ "$KASAN" = true ]; then + build_cmd+=" --kasan" + fi + print_info "Build vendor kernel with $build_cmd" + eval "$build_cmd" + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "Build vendor kernel succeeded" + else + print_error "Build vendor kernel failed with exit code $exit_code" + exit 1 + fi + fi + VENDOR_KERNEL_BUILD="$PWD/out/$VENDOR_KERNEL_BUILD_TARGET/dist" + fi +fi + +if [ -z "$PLATFORM_BUILD" ]; then # No platform build provided + if [ -z "$KERNEL_BUILD" ] && [ -z "$VENDOR_KERNEL_BUILD" ]; then # No kernel or vendor kernel build + print_info "KERNEL_BUILD=$KERNEL_BUILD VENDOR_KERNEL_BUILD=$VENDOR_KERNEL_BUILD" "$LINENO" + print_error "Nothing to flash" "$LINENO" + fi + if [ ! -z "$VENDOR_KERNEL_BUILD" ]; then + print_info "Flash kernel from $VENDOR_KERNEL_BUILD" "$LINENO" + flash_vendor_kernel_build + fi + if [ ! -z "$KERNEL_BUILD" ]; then + flash_gki_build + fi +else # Platform build provided + if [ -z "$KERNEL_BUILD" ] && [ -z "$VENDOR_KERNEL_BUILD" ]; then # No kernel or vendor kernel build + print_info "Flash platform build only" + flash_platform_build + elif [ -z "$KERNEL_BUILD" ] && [ ! -z "$VENDOR_KERNEL_BUILD" ]; then # Vendor kernel build and platform build + print_info "Mix vendor kernel and platform build" + mixing_build + flash_platform_build + elif [ ! -z "$KERNEL_BUILD" ] && [ -z "$VENDOR_KERNEL_BUILD" ]; then # GKI build and platform build + flash_platform_build + get_device_info + flash_gki_build + elif [ ! -z "$KERNEL_BUILD" ] && [ ! -z "$VENDOR_KERNEL_BUILD" ]; then # All three builds provided + print_info "Mix GKI kernel, vendor kernel and platform build" "$LINENO" + mixing_build + flash_platform_build + fi +fi
diff --git a/tools/launch_cvd.sh b/tools/launch_cvd.sh new file mode 100755 index 0000000..fee7e36 --- /dev/null +++ b/tools/launch_cvd.sh
@@ -0,0 +1,510 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-2.0 + +# A handy tool to launch CVD with local build or remote build. + +# Constants +ACLOUD_PREBUILT="prebuilts/asuite/acloud/linux-x86/acloud" +OPT_SKIP_PRERUNCHECK='--skip-pre-run-check' +PRODUCT='aosp_cf_x86_64_phone' +# Color constants +#BOLD="$(tput bold)" # Unused +END="$(tput sgr0)" +GREEN="$(tput setaf 2)" +RED="$(tput setaf 198)" +YELLOW="$(tput setaf 3)" +# BLUE="$(tput setaf 34)" # Unused + +SKIP_BUILD=false +GCOV=false +DEBUG=false +KASAN=false +EXTRA_OPTIONS=() + +function print_help() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "This script will build images and launch a Cuttlefish device." + echo "" + echo "Available options:" + echo " --skip-build Skip the image build step. Will build by default if in repo." + echo " --gcov Launch CVD with gcov enabled kernel" + echo " --debug Launch CVD with debug enabled kernel" + echo " --kasan Launch CVD with kasan enabled kernel" + echo " -pb <platform_build>, --platform-build=<platform_build>" + echo " The platform build path. Can be a local path or a remote build" + echo " as ab://<branch>/<build_target>/<build_id>." + echo " If not specified, it will use the platform build in the local" + echo " repo, or the default compatible platform build for the kernel." + echo " -sb <system_build>, --system-build=<system_build>" + echo " The system build path for GSI testing. Can be a local path or" + echo " remote build as ab://<branch>/<build_target>/<build_id>." + echo " If not specified, no system build will be used." + echo " -kb <kernel_build>, --kernel-build=<kernel_build>" + echo " The kernel build path. Can be a local path or a remote build" + echo " as ab://<branch>/<build_target>/<build_id>." + echo " If not specified, it will use the kernel in the local repo." + echo " --acloud-bin=<acloud_bin>" + echo " The alternative alcoud binary path." + echo " --cf-product=<product_type>" + echo " The alternative cuttlefish product type for local build." + echo " Will use default aosp_cf_x86_64_phone if not specified." + echo " --acloud-arg=<acloud_arg>" + echo " Additional acloud command arg. Can be repeated." + echo " For example --acloud-arg=--local-instance to launch a local cvd." + echo " -h, --help Display this help message and exit" + echo "" + echo "Examples:" + echo "$0" + echo "$0 --acloud-arg=--local-instance" + echo "$0 -pb ab://git_main/aosp_cf_x86_64_phone-userdebug/latest" + echo "$0 -pb ~/aosp-main/out/target/product/vsoc_x86_64/" + echo "$0 -kb ~/android-mainline/out/virtual_device_x86_64/" + echo "" + exit 0 +} + +function parse_arg() { + while test $# -gt 0; do + case "$1" in + -h|--help) + print_help + ;; + --skip-build) + SKIP_BUILD=true + shift + ;; + -pb) + shift + if test $# -gt 0; then + PLATFORM_BUILD=$1 + else + print_error "platform build is not specified" + fi + shift + ;; + --platform-build=*) + PLATFORM_BUILD=$(echo "$1" | sed -e "s/^[^=]*=//g") + shift + ;; + -sb) + shift + if test $# -gt 0; then + SYSTEM_BUILD=$1 + else + print_error "system build is not specified" + fi + shift + ;; + --system-build=*) + SYSTEM_BUILD=$(echo "$1" | sed -e "s/^[^=]*=//g") + shift + ;; + -kb) + shift + if test $# -gt 0; then + KERNEL_BUILD=$1 + else + print_error "kernel build path is not specified" + fi + shift + ;; + --kernel-build=*) + KERNEL_BUILD=$(echo "$1" | sed -e "s/^[^=]*=//g") + shift + ;; + --acloud-arg=*) + EXTRA_OPTIONS+=("$(echo "$1" | sed -e "s/^[^=]*=//g")") # Use array append syntax + shift + ;; + --acloud-bin=*) + ACLOUD_BIN=$(echo "$1" | sed -e "s/^[^=]*=//g") + shift + ;; + --cf-product=*) + PRODUCT=$(echo "$1" | sed -e "s/^[^=]*=//g") + shift + ;; + --gcov) + GCOV=true + shift + ;; + --debug) + DEBUG=true + shift + ;; + --kasan) + KASAN=true + shift + ;; + *) + print_error "Unsupported flag: $1" >&2 + ;; + esac + done +} + +function adb_checker() { + if ! which adb &> /dev/null; then + print_error "adb not found!" + fi +} + +function create_kernel_build_cmd() { + local cf_kernel_repo_root=$1 + local cf_kernel_version=$2 + + local regex="((?<=android-)mainline|(\K\d+\.\d+(?=-stable)))|((?:android)\K\d+)" + local android_version + android_version=$(grep -oP "$regex" <(echo "$cf_kernel_version")) + local build_cmd="" + if [ -f "$cf_kernel_repo_root/common-modules/virtual-device/BUILD.bazel" ]; then + # support android-mainline, android16, android15, android14, android13 + build_cmd+="tools/bazel run --config=fast" + if [ "$GCOV" = true ]; then + build_cmd+=" --gcov" + fi + if [ "$DEBUG" = true ]; then + build_cmd+=" --debug" + fi + if [ "$KASAN" = true ]; then + build_cmd+=" --kasan" + fi + build_cmd+=" //common-modules/virtual-device:virtual_device_x86_64_dist" + elif [ -f "$cf_kernel_repo_root/build/build.sh" ]; then + if [[ "$android_version" == "12" ]]; then + build_cmd+="BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh" + build_cmd+=" && " + build_cmd+="BUILD_CONFIG=common-modules/virtual-device/build.config.virtual_device.x86_64 build/build.sh" + elif [[ "$android_version" == "11" ]] || [[ "$android_version" == "4.19" ]]; then + build_cmd+="BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh" + build_cmd+=" && " + build_cmd+="BUILD_CONFIG=common-modules/virtual-device/build.config.cuttlefish.x86_64 build/build.sh" + else + echo "The Kernel build $cf_kernel_version is not yet supported" >&2 + return 1 + fi + else + echo "The Kernel build $cf_kernel_version is not yet supported" >&2 + return 1 + fi + + echo "$build_cmd" +} + +function create_kernel_build_path() { + local cf_kernel_version=$1 + + local regex="((?<=android-)mainline|(\K\d+\.\d+(?=-stable)))|((?:android)\K\d+)" + local android_version + android_version=$(grep -oP "$regex" <(echo "$cf_kernel_version")) + if [ "$android_version" = "mainline" ] || greater_than_or_equal_to "$android_version" "14"; then + # support android-mainline, android16, android15, android14 + echo "out/virtual_device_x86_64/dist" + elif greater_than_or_equal_to "$android_version" "11" || [[ "$android_version" == "4.19" ]]; then + # support android13, android12, android11, android-4.19-stable + echo "out/$cf_kernel_version/dist" + else + echo "The version of this kernel build $cf_kernel_version is not supported yet" >&2 + return 1 + fi +} + +function go_to_repo_root() { + current_dir="$1" + while [ ! -d ".repo" ] && [ "$current_dir" != "/" ]; do + current_dir=$(dirname "$current_dir") # Go up one directory + cd "$current_dir" || print_error "Failed to cd to $current_dir" + done +} + +function greater_than_or_equal_to() { + local num1="$1" + local num2="$2" + + # This regex matches strings formatted as floating-point or integer numbers + local num_regex="^[0]([\.][0-9]+)?$|^[1-9][0-9]*([\.][0-9]+)?$" + if [[ ! "$num1" =~ $num_regex ]] || [[ ! "$num2" =~ $num_regex ]]; then + return 1 + fi + + if [[ $(echo "$num1 >= $num2" | bc -l) -eq 1 ]]; then + return 0 + else + return 1 + fi +} + +# Checks if target_path is within root_directory +function is_path_in_root() { + local root_directory="$1" + local target_path="$2" + + # expand the path variable, for example: + # "~/Documents" becomes "/home/user/Documents" + root_directory=$(eval echo "$root_directory") + target_path=$(eval echo "$target_path") + + # remove the trailing slashes + root_directory=$(realpath -m "$root_directory") + target_path=$(realpath -m "$target_path") + + # handles the corner case, for example: + # $root_directory="/home/user/Doc", $target_path="/home/user/Documents/" + root_directory="${root_directory}/" + + if [[ "$target_path" = "$root_directory"* ]]; then + return 0 + else + return 1 + fi +} + +function print_info() { + echo "[$MY_NAME]: ${GREEN}$1${END}" +} + +function print_warn() { + echo "[$MY_NAME]: ${YELLOW}$1${END}" +} + +function print_error() { + echo -e "[$MY_NAME]: ${RED}$1${END}" + cd "$OLD_PWD" || echo "Failed to cd to $OLD_PWD" + exit 1 +} + +function set_platform_repo() { + print_warn "Build target product '${TARGET_PRODUCT}' does not match expected '$1'" + local lunch_cli="source build/envsetup.sh && lunch $1" + if [ -f "build/release/release_configs/trunk_staging.textproto" ]; then + lunch_cli+="-trunk_staging-userdebug" + else + lunch_cli+="-userdebug" + fi + print_info "Setup build environment with: $lunch_cli" + eval "$lunch_cli" +} + +function find_repo() { + manifest_output=$(grep -e "superproject" -e "gs-pixel" -e "private/google-modules/soc/gs" \ + -e "kernel/common" -e "common-modules/virtual-device" .repo/manifests/default.xml) + case "$manifest_output" in + *platform/superproject*) + PLATFORM_REPO_ROOT="$PWD" + PLATFORM_VERSION=$(grep -e "platform/superproject" .repo/manifests/default.xml | \ + grep -oP 'revision="\K[^"]*') + print_info "PLATFORM_REPO_ROOT=$PLATFORM_REPO_ROOT, PLATFORM_VERSION=$PLATFORM_VERSION" + if [ -z "$PLATFORM_BUILD" ]; then + PLATFORM_BUILD="$PLATFORM_REPO_ROOT" + fi + ;; + *kernel/superproject*) + if [[ "$manifest_output" == *common-modules/virtual-device* ]]; then + CF_KERNEL_REPO_ROOT="$PWD" + CF_KERNEL_VERSION=$(grep -e "common-modules/virtual-device" \ + .repo/manifests/default.xml | grep -oP 'revision="\K[^"]*') + print_info "CF_KERNEL_REPO_ROOT=$CF_KERNEL_REPO_ROOT, CF_KERNEL_VERSION=$CF_KERNEL_VERSION" + if [ -z "$KERNEL_BUILD" ]; then + output=$(create_kernel_build_path "$CF_KERNEL_VERSION" 2>&1) + if [[ $? -ne 0 ]]; then + print_error "$output" + fi + KERNEL_BUILD="${CF_KERNEL_REPO_ROOT}/$output" + print_info "KERNEL_BUILD=$KERNEL_BUILD" + fi + fi + ;; + *) + print_warn "Unexpected manifest output. Could not determine repository type." + ;; + esac +} + +function rebuild_platform() { + build_cmd="m -j12" + print_warn "Flag --skip-build is not set. Rebuilt images at $PWD with: $build_cmd" + eval "$build_cmd" + exit_code=$? + if [ $exit_code -eq 0 ]; then + if [ -f "${ANDROID_PRODUCT_OUT}/system.img" ]; then + print_info "$build_cmd succeeded" + else + print_error "${ANDROID_PRODUCT_OUT}/system.img doesn't exist" + fi + else + print_warn "$build_cmd returned exit_code $exit_code or ${ANDROID_PRODUCT_OUT}/system.img is not found" + print_error "$build_cmd failed" + fi +} + +adb_checker + +OLD_PWD=$PWD +MY_NAME=$0 + +parse_arg "$@" + +FULL_COMMAND_PATH=$(dirname "$PWD/$0") +REPO_LIST_OUT=$(repo list 2>&1) +if [[ "$REPO_LIST_OUT" == "error"* ]]; then + echo -e "[$MY_NAME]: ${RED}Current path $PWD is not in an Android repo. Change path to repo root.${END}" + go_to_repo_root "$FULL_COMMAND_PATH" + print_info "Changed path to $PWD" +else + go_to_repo_root "$PWD" +fi + +find_repo + +if [ "$SKIP_BUILD" = false ] && [ -n "$PLATFORM_BUILD" ] && [[ "$PLATFORM_BUILD" != ab://* ]] \ +&& [ -d "$PLATFORM_BUILD" ]; then + # Check if PLATFORM_BUILD is an Android platform repo, if yes rebuild + cd "$PLATFORM_BUILD" || print_error "Failed to cd to $PLATFORM_BUILD" + PLATFORM_REPO_LIST_OUT=$(repo list 2>&1) + if [[ "$PLATFORM_REPO_LIST_OUT" != "error"* ]]; then + go_to_repo_root "$PWD" + if [ -z "${TARGET_PRODUCT}" ] || [[ "${TARGET_PRODUCT}" != "$PRODUCT" ]]; then + set_platform_repo "$PRODUCT" + rebuild_platform + PLATFORM_BUILD=${ANDROID_PRODUCT_OUT} + fi + fi +fi + +if [ "$SKIP_BUILD" = false ] && [ -n "$SYSTEM_BUILD" ] && [[ "$SYSTEM_BUILD" != ab://* ]] \ +&& [ -d "$SYSTEM_BUILD" ]; then + # Get GSI build + cd "$SYSTEM_BUILD" || print_error "Failed to cd to $SYSTEM_BUILD" + SYSTEM_REPO_LIST_OUT=$(repo list 2>&1) + if [[ "$SYSTEM_REPO_LIST_OUT" != "error"* ]]; then + go_to_repo_root "$PWD" + if [ -z "${TARGET_PRODUCT}" ] || [[ "${TARGET_PRODUCT}" != "aosp_x86_64" ]]; then + set_platform_repo "aosp_x86_64" + rebuild_platform + SYSTEM_BUILD="${ANDROID_PRODUCT_OUT}/system.img" + fi + fi +fi + +if [ "$SKIP_BUILD" = false ] && [ -n "$KERNEL_BUILD" ] && [[ "$KERNEL_BUILD" != ab://* ]]; then + if [ -d "$CF_KERNEL_REPO_ROOT" ] && [ -n "$CF_KERNEL_VERSION" ] && is_path_in_root "$CF_KERNEL_REPO_ROOT" "$KERNEL_BUILD"; then + # Support first-build in the local kernel repository + target_path="$CF_KERNEL_REPO_ROOT" + elif [ -d $KERNEL_BUILD ]; then + target_path="$KERNEL_BUILD" + else + print_error "Built kernel not found. Either build the kernel or use the default kernel from the local repository" + fi + + cd "$target_path" || print_error "Failed to cd to $target_path" + KERNEL_REPO_LIST_OUT=$(repo list 2>&1) + if [[ "$KERNEL_REPO_LIST_OUT" != "error"* ]]; then + go_to_repo_root "$target_path" + target_kernel_repo_root="$PWD" + target_cf_kernel_version=$(grep -e "common-modules/virtual-device" \ + .repo/manifests/default.xml | grep -oP 'revision="\K[^"]*') + + print_info "target_kernel_repo_root=$target_kernel_repo_root, target_cf_kernel_version=$target_cf_kernel_version" + + output=$(create_kernel_build_cmd $PWD $target_cf_kernel_version 2>&1) + if [[ $? -ne 0 ]]; then + print_error "$output" + fi + build_cmd="$output" + print_warn "Flag --skip-build is not set. Rebuild the kernel with: $build_cmd." + eval "$build_cmd" && print_info "$build_cmd succeeded" || print_error "$build_cmd failed" + else + print_warn "Current path $PWD is not a valid Android repo, please ensure it contains the kernel" + fi +fi + +if [ -z "$ACLOUD_BIN" ] || ! [ -x "$ACLOUD_BIN" ]; then + output=$(which acloud 2>&1) + if [ -z "$output" ]; then + print_info "Use acloud binary from $ACLOUD_PREBUILT" + if [ -n "${PLATFORM_REPO_ROOT}" ]; then + ACLOUD_PREBUILT="${PLATFORM_REPO_ROOT}/${ACLOUD_PREBUILT}" + elif [ -n "${CF_KERNEL_REPO_ROOT}" ]; then + ACLOUD_PREBUILT="${CF_KERNEL_REPO_ROOT}/${ACLOUD_PREBUILT}" + else + print_error "Unable to determine repository root path from repo manifest" + fi + ACLOUD_BIN="$ACLOUD_PREBUILT" + else + print_info "Use acloud binary from $output" + ACLOUD_BIN="$output" + fi + + # Check if the newly found or prebuilt ACLOUD_BIN is executable + if ! [ -x "$ACLOUD_BIN" ]; then + print_error "$ACLOUD_BIN is not executable" + fi +fi + +acloud_cli="$ACLOUD_BIN create" +EXTRA_OPTIONS+=("$OPT_SKIP_PRERUNCHECK") + +# Add in branch if not specified + +if [ -z "$PLATFORM_BUILD" ]; then + print_warn "Platform build is not specified, will use the latest aosp-main build." + acloud_cli+=' --branch aosp-main' +elif [[ "$PLATFORM_BUILD" == ab://* ]]; then + IFS='/' read -ra array <<< "$PLATFORM_BUILD" + acloud_cli+=" --branch ${array[2]}" + + # Check if array[3] exists before using it + if [ ${#array[@]} -ge 3 ] && [ -n "${array[3]}" ]; then + acloud_cli+=" --build-target ${array[3]}" + + # Check if array[4] exists and is not 'latest' before using it + if [ ${#array[@]} -ge 4 ] && [ -n "${array[4]}" ] && [ "${array[4]}" != 'latest' ]; then + acloud_cli+=" --build-id ${array[4]}" + fi + fi +else + acloud_cli+=" --local-image $PLATFORM_BUILD" +fi + +if [ -z "$KERNEL_BUILD" ]; then + print_warn "Flag --kernel-build is not set, will not launch Cuttlefish with different kernel." +elif [[ "$KERNEL_BUILD" == ab://* ]]; then + IFS='/' read -ra array <<< "$KERNEL_BUILD" + acloud_cli+=" --kernel-branch ${array[2]}" + + # Check if array[3] exists before using it + if [ ${#array[@]} -ge 3 ] && [ -n "${array[3]}" ]; then + acloud_cli+=" --kernel-build-target ${array[3]}" + + # Check if array[4] exists and is not 'latest' before using it + if [ ${#array[@]} -ge 4 ] && [ -n "${array[4]}" ] && [ "${array[4]}" != 'latest' ]; then + acloud_cli+=" --kernel-build-id ${array[4]}" + fi + fi +else + acloud_cli+=" --local-kernel-image $KERNEL_BUILD" +fi + +if [ -z "$SYSTEM_BUILD" ]; then + print_warn "System build is not specified, will not launch Cuttlefish with GSI mixed build." +elif [[ "$SYSTEM_BUILD" == ab://* ]]; then + IFS='/' read -ra array <<< "$SYSTEM_BUILD" + acloud_cli+=" --system-branch ${array[2]}" + + # Check if array[3] exists before using it + if [ ${#array[@]} -ge 3 ] && [ -n "${array[3]}" ]; then + acloud_cli+=" --system-build-target ${array[3]}" + + # Check if array[4] exists and is not 'latest' before using it + if [ ${#array[@]} -ge 4 ] && [ -n "${array[4]}" ] && [ "${array[4]}" != 'latest' ]; then + acloud_cli+=" --system-build-id ${array[4]}" + fi + fi +else + acloud_cli+=" --local-system-image $SYSTEM_BUILD" +fi + +acloud_cli+=" ${EXTRA_OPTIONS[*]}" +print_info "Launch CVD with command: $acloud_cli" +eval "$acloud_cli"
diff --git a/tools/run_test_only.sh b/tools/run_test_only.sh new file mode 100755 index 0000000..88843f5 --- /dev/null +++ b/tools/run_test_only.sh
@@ -0,0 +1,432 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-2.0 + +# +# A simple script to run test with Tradefed. +# + +KERNEL_TF_PREBUILT=prebuilts/tradefed/filegroups/tradefed/tradefed.sh +PLATFORM_TF_PREBUILT=tools/tradefederation/prebuilts/filegroups/tradefed/tradefed.sh +JDK_PATH=prebuilts/jdk/jdk11/linux-x86 +PLATFORM_JDK_PATH=prebuilts/jdk/jdk21/linux-x86 +DEFAULT_LOG_DIR=$PWD/out/test_logs/$(date +%Y%m%d_%H%M%S) +DOWNLOAD_PATH="/tmp/downloaded_tests" +GCOV=false +CREATE_TRACEFILE_SCRIPT="kernel/tests/tools/create-tracefile.py" +FETCH_SCRIPT="kernel/tests/tools/fetch_artifact.sh" +TRADEFED= +TRADEFED_GCOV_OPTIONS=" --coverage --coverage-toolchain GCOV_KERNEL --auto-collect GCOV_KERNEL_COVERAGE" +TEST_ARGS=() +TEST_DIR= +TEST_NAMES=() + +BOLD="$(tput bold)" +END="$(tput sgr0)" +GREEN="$(tput setaf 2)" +RED="$(tput setaf 198)" +YELLOW="$(tput setaf 3)" +BLUE="$(tput setaf 34)" + +function adb_checker() { + if ! which adb &> /dev/null; then + echo -e "\n${RED}Adb not found!${END}" + fi +} + +function go_to_repo_root() { + current_dir="$1" + while [ ! -d ".repo" ] && [ "$current_dir" != "/" ]; do + current_dir=$(dirname "$current_dir") # Go up one directory + cd "$current_dir" + done +} + +function print_info() { + local log_prompt=$MY_NAME + if [ ! -z "$2" ]; then + log_prompt+=" line $2" + fi + echo "[$log_prompt]: ${GREEN}$1${END}" +} + +function print_warn() { + local log_prompt=$MY_NAME + if [ ! -z "$2" ]; then + log_prompt+=" line $2" + fi + echo "[$log_prompt]: ${ORANGE}$1${END}" +} + +function print_error() { + local log_prompt=$MY_NAME + if [ ! -z "$2" ]; then + log_prompt+=" line $2" + fi + echo -e "[$log_prompt]: ${RED}$1${END}" + cd $OLD_PWD + exit 1 +} + +function print_help() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "This script will run tests on an Android device." + echo "" + echo "Available options:" + echo " -s <serial_number>, --serial=<serial_number>" + echo " The device serial number to run tests with." + echo " -td <test_dir>, --test-dir=<test_dir> or -tb <test_build>, --test-build=<test_build>" + echo " The test artifact file name or directory path." + echo " Can be a local file or directory or a remote file" + echo " as ab://<branch>/<build_target>/<build_id>/<file_name>." + echo " If not specified, it will use the tests in the local" + echo " repo." + echo " -tl <test_log_dir>, --test-log=<test_log_dir>" + echo " The test log dir. Use default out/test_logs if not specified." + echo " -ta <test_arg>, --test-arg=<test_arg>" + echo " Additional tradefed command arg. Can be repeated." + echo " -t <test_name>, --test=<test_name> The test name. Can be repeated." + echo " If test is not specified, no tests will be run." + echo " -tf <tradefed_binary_path>, --tradefed-bin=<tradefed_binary_path>" + echo " The alternative tradefed binary to run test with." + echo " --gcov Collect coverage data from the test result" + echo " -h, --help Display this help message and exit" + echo "" + echo "Examples:" + echo "$0 -s 127.0.0.1:33847 -t selftests" + echo "$0 -s 1C141FDEE003FH -t selftests:kselftest_binderfs_binderfs_test" + echo "$0 -s 127.0.0.1:33847 -t CtsAccessibilityTestCases -t CtsAccountManagerTestCases" + echo "$0 -s 127.0.0.1:33847 -t CtsAccessibilityTestCases -t CtsAccountManagerTestCases \ +-td ab://aosp-main/test_suites_x86_64-trunk_staging/latest/android-cts.zip" + echo "$0 -s 1C141FDEE003FH -t CtsAccessibilityTestCases -t CtsAccountManagerTestCases \ +-td ab://git_main/test_suites_arm64-trunk_staging/latest/android-cts.zip" + echo "$0 -s 1C141FDEE003FH -t CtsAccessibilityTestCases -td <your_path_to_platform_repo>" + echo "" + exit 0 +} + +function set_platform_repo() { + print_warn "Build target product '${TARGET_PRODUCT}' does not match device product '$PRODUCT'" + lunch_cli="source build/envsetup.sh && " + if [ -f "build/release/release_configs/trunk_staging.textproto" ]; then + lunch_cli+="lunch $PRODUCT-trunk_staging-$BUILD_TYPE" + else + lunch_cli+="lunch $PRODUCT-trunk_staging-$BUILD_TYPE" + fi + print_info "Setup build environment with: $lunch_cli" + eval "$lunch_cli" +} + +function run_test_in_platform_repo() { + if [ -z "${TARGET_PRODUCT}" ]; then + set_platform_repo + elif [[ "${TARGET_PRODUCT}" != *"x86"* && "${PRODUCT}" == *"x86"* ]] || \ + [[ "${TARGET_PRODUCT}" == *"x86"* && "${PRODUCT}" != *"x86"* ]]; then + set_platform_repo + fi + atest_cli="atest ${TEST_NAMES[*]} -s $SERIAL_NUMBER --" + if $GCOV; then + atest_cli+="$TRADEFED_GCOV_OPTIONS" + fi + eval "$atest_cli" "${TEST_ARGS[*]}" + exit_code=$? + + if $GCOV; then + atest_log_dir="/tmp/atest_result_$USER/LATEST" + create_tracefile_cli="$CREATE_TRACEFILE_SCRIPT -t $atest_log_dir/log -o $atest_log_dir/cov.info" + print_info "Skip creating tracefile. If you have full kernel source, run the following command:" + print_info "$create_tracefile_cli" + fi + cd $OLD_PWD + exit $exit_code +} + +OLD_PWD=$PWD +MY_NAME=$0 + +while test $# -gt 0; do + case "$1" in + -h|--help) + print_help + ;; + -s) + shift + if test $# -gt 0; then + SERIAL_NUMBER=$1 + else + print_error "device serial is not specified" + fi + shift + ;; + --serial*) + SERIAL_NUMBER=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + -tl) + shift + if test $# -gt 0; then + LOG_DIR=$1 + else + print_error "test log directory is not specified" + fi + shift + ;; + --test-log*) + LOG_DIR=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + -td | -tb ) + shift + if test $# -gt 0; then + TEST_DIR=$1 + else + print_error "test directory is not specified" + fi + shift + ;; + --test-dir* | --test-build*) + TEST_DIR=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + -ta) + shift + if test $# -gt 0; then + TEST_ARGS+=$1 + else + print_error "test arg is not specified" + fi + shift + ;; + --test-arg*) + TEST_ARGS+=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + -t) + shift + if test $# -gt 0; then + TEST_NAMES+=$1 + else + print_error "test name is not specified" + fi + shift + ;; + --test*) + TEST_NAMES+=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + -tf) + shift + if test $# -gt 0; then + TRADEFED=$1 + else + print_error "tradefed binary is not specified" + fi + shift + ;; + --tradefed-bin*) + TRADEFED=$(echo $1 | sed -e "s/^[^=]*=//g") + shift + ;; + --gcov) + GCOV=true + shift + ;; + *) + ;; + esac +done + +# Ensure SERIAL_NUMBER is provided +if [ -z "$SERIAL_NUMBER" ]; then + print_error "Device serial is not provided with flag -s <serial_number>." "$LINENO" +fi + +# Ensure TEST_NAMES is provided +if [ -z "$TEST_NAMES" ]; then + print_error "No test is specified with flag -t <test_name>." "$LINENO" +fi + +FULL_COMMAND_PATH=$(dirname "$PWD/$0") +REPO_LIST_OUT=$(repo list 2>&1) +if [[ "$REPO_LIST_OUT" == "error"* ]]; then + print_warn "Current path $PWD is not in an Android repo. Change path to repo root." "$LINENO" + go_to_repo_root "$FULL_COMMAND_PATH" + print_info "Changed path to $PWD" "$LINENO" +else + go_to_repo_root "$PWD" +fi + +REPO_ROOT_PATH="$PWD" +FETCH_SCRIPT="$REPO_ROOT_PATH/$FETCH_SCRIPT" + +adb_checker + +# Set default LOG_DIR if not provided +if [ -z "$LOG_DIR" ]; then + LOG_DIR="$DEFAULT_LOG_DIR" +fi + +BOARD=$(adb -s "$SERIAL_NUMBER" shell getprop ro.product.board) +ABI=$(adb -s "$SERIAL_NUMBER" shell getprop ro.product.cpu.abi) +PRODUCT=$(adb -s "$SERIAL_NUMBER" shell getprop ro.product.product.name) +BUILD_TYPE=$(adb -s "$SERIAL_NUMBER" shell getprop ro.build.type) + +if [ -z "$TEST_DIR" ]; then + print_warn "Flag -td <test_dir> is not provided. Will use the default test directory" "$LINENO" + if [[ "$REPO_LIST_OUT" == *"build/make"* ]]; then + # In the platform repo + print_info "Run test with atest" "$LINENO" + run_test_in_platform_repo + return + elif [[ "$BOARD" == "cutf"* ]] && [[ "$REPO_LIST_OUT" == *"common-modules/virtual-device"* ]]; then + # In the android kernel repo + if [[ "$ABI" == "arm64"* ]]; then + TEST_DIR="$REPO_ROOT_PATH/out/virtual_device_aarch64/dist/tests.zip" + elif [[ "$ABI" == "x86_64"* ]]; then + TEST_DIR="$REPO_ROOT_PATH/out/virtual_device_x86_64/dist/tests.zip" + else + print_error "No test builds for $ABI Cuttlefish in $REPO_ROOT_PATH" "$LINENO" + fi + elif [[ "$BOARD" == "raven"* || "$BOARD" == "oriole"* ]] && [[ "$REPO_LIST_OUT" == *"private/google-modules/display"* ]]; then + TEST_DIR="$REPO_ROOT_PATH/out/slider/dist/tests.zip" + elif [[ "$ABI" == "arm64"* ]] && [[ "$REPO_LIST_OUT" == *"kernel/common"* ]]; then + TEST_DIR="$REPO_ROOT_PATH/out/kernel_aarch64/dist/tests.zip" + else + print_error "No test builds for $ABI $BOARD in $REPO_ROOT_PATH" "$LINENO" + fi +fi + +TEST_FILTERS= +for i in "$TEST_NAMES"; do + TEST_NAME=$(echo $i | sed "s/:/ /g") + TEST_FILTERS+=" --include-filter '$TEST_NAME'" +done + +if [[ "$TEST_DIR" == ab://* ]]; then + if [ ! -d "$DOWNLOAD_PATH" ]; then + mkdir -p "$DOWNLOAD_PATH" || $(print_error "Fail to create directory $DOWNLOAD_PATH" "$LINENO") + fi + cd $DOWNLOAD_PATH || $(print_error "Fail to go to $DOWNLOAD_PATH" "$LINENO") + file_name=${TEST_DIR##*/} + eval "$FETCH_SCRIPT $TEST_DIR" + exit_code=$? + if [ $exit_code -eq 0 ]; then + print_info "$TEST_DIR is downloaded to $DOWNLOAD_PATH successfully" "$LINENO" + else + print_error "Failed to download $TEST_DIR" "$LINENO" + fi + + file_name=$(ls $file_name) + # Check if the download was successful + if [ ! -f "${file_name}" ]; then + print_error "Failed to download ${file_name}" "$LINENO" + fi + TEST_DIR="$DOWNLOAD_PATH/$file_name" +elif [ ! -z "$TEST_DIR" ]; then + if [ -d $TEST_DIR ]; then + test_file_path=$TEST_DIR + elif [ -f "$TEST_DIR" ]; then + test_file_path=$(dirname "$TEST_DIR") + else + print_error "$TEST_DIR is neither a directory or file" "$LINENO" + fi + cd "$test_file_path" || $(print_error "Failed to go to $test_file_path" "$LINENO") + TEST_REPO_LIST_OUT=$(repo list 2>&1) + if [[ "$TEST_REPO_LIST_OUT" == "error"* ]]; then + print_info "Test path $test_file_path is not in an Android repo. Will use $TEST_DIR directly." "$LINENO" + elif [[ "$TEST_REPO_LIST_OUT" == *"build/make"* ]]; then + # Test_dir is from the platform repo + print_info "Test_dir $TEST_DIR is from Android platform repo. Run test with atest" "$LINENO" + go_to_repo_root "$PWD" + run_test_in_platform_repo + return + fi +fi + +cd "$REPO_ROOT_PATH" +if [[ "$TEST_DIR" == *.zip ]]; then + filename=${TEST_DIR##*/} + new_test_dir="${TEST_DIR%.*}" + if [ -d "$new_test_dir" ]; then + rm -r "$new_test_dir" + fi + unzip -oq "$TEST_DIR" -d "$new_test_dir" || $(print_error "Failed to unzip $TEST_DIR to $new_test_dir" "$LINENO") + case $filename in + "android-vts.zip" | "android-cts.zip") + new_test_dir+="/$(echo $filename | sed "s/.zip//g")" + ;; + *) + ;; + esac + TEST_DIR="$new_test_dir" # Update TEST_DIR to the unzipped directory +fi + +print_info "Will run tests with test artifacts in $TEST_DIR" "$LINENO" + +if [ -f "${TEST_DIR}/tools/vts-tradefed" ]; then + TRADEFED="JAVA_HOME=${TEST_DIR}/jdk PATH=${TEST_DIR}/jdk/bin:$PATH ${TEST_DIR}/tools/vts-tradefed" + print_info "Will run tests with vts-tradefed from $TRADEFED" "$LINENO" + print_info "Many VTS tests need WIFI connection, please make sure WIFI is connected before you run the test." "$LINENO" + tf_cli="$TRADEFED run commandAndExit \ + vts --skip-device-info --log-level-display info --log-file-path=$LOG_DIR \ + $TEST_FILTERS -s $SERIAL_NUMBER" +elif [ -f "${TEST_DIR}/tools/cts-tradefed" ]; then + TRADEFED="JAVA_HOME=${TEST_DIR}/jdk PATH=${TEST_DIR}/jdk/bin:$PATH ${TEST_DIR}/tools/cts-tradefed" + print_info "Will run tests with cts-tradefed from $TRADEFED" "$LINENO" + print_info "Many CTS tests need WIFI connection, please make sure WIFI is connected before you run the test." "$LINENO" + tf_cli="$TRADEFED run commandAndExit cts --skip-device-info \ + --log-level-display info --log-file-path=$LOG_DIR \ + $TEST_FILTERS -s $SERIAL_NUMBER" +elif [ -f "${ANDROID_HOST_OUT}/bin/tradefed.sh" ] ; then + TRADEFED="${ANDROID_HOST_OUT}/bin/tradefed.sh" + print_info "Use the tradefed from the local built path $TRADEFED" "$LINENO" + tf_cli="$TRADEFED run commandAndExit template/local_min \ + --log-level-display info --log-file-path=$LOG_DIR \ + --template:map test=suite/test_mapping_suite --tests-dir=$TEST_DIR\ + $TEST_FILTERS -s $SERIAL_NUMBER" +elif [ -f "$PLATFORM_TF_PREBUILT" ]; then + TRADEFED="JAVA_HOME=$PLATFORM_JDK_PATH PATH=$PLATFORM_JDK_PATH/bin:$PATH $PLATFORM_TF_PREBUILT" + print_info "Local Tradefed is not built yet. Use the prebuilt from $PLATFORM_TF_PREBUILT" "$LINENO" + tf_cli="$TRADEFED run commandAndExit template/local_min \ + --log-level-display info --log-file-path=$LOG_DIR \ + --template:map test=suite/test_mapping_suite --tests-dir=$TEST_DIR\ + $TEST_FILTERS -s $SERIAL_NUMBER" +elif [ -f "$KERNEL_TF_PREBUILT" ]; then + TRADEFED="JAVA_HOME=$JDK_PATH PATH=$JDK_PATH/bin:$PATH $KERNEL_TF_PREBUILT" + print_info "Use the tradefed prebuilt from $KERNEL_TF_PREBUILT" "$LINENO" + tf_cli="$TRADEFED run commandAndExit template/local_min \ + --log-level-display info --log-file-path=$LOG_DIR \ + --template:map test=suite/test_mapping_suite --tests-dir=$TEST_DIR\ + $TEST_FILTERS -s $SERIAL_NUMBER" +# No Tradefed found +else + print_error "Can not find Tradefed binary. Please use flag -tf to specify the binary path." "$LINENO" +fi + +# Construct the TradeFed command + +# Add GCOV options if enabled +if $GCOV; then + tf_cli+=" --enable-root" + tf_cli+=$TRADEFED_GCOV_OPTIONS +fi + +# Evaluate the TradeFed command with extra arguments +print_info "Run test with: $tf_cli ${TEST_ARGS[*]}" "$LINENO" +eval "$tf_cli" "${TEST_ARGS[*]}" +exit_code=$? + +if $GCOV; then + create_tracefile_cli="$CREATE_TRACEFILE_SCRIPT -t $LOG_DIR -o $LOG_DIR/cov.info" + if [ -f $KERNEL_TF_PREBUILT ]; then + print_info "Create tracefile with $create_tracefile_cli" "$LINENO" + $create_tracefile_cli && \ + print_info "Created tracefile at $LOG_DIR/cov.info" "$LINENO" + else + print_info "Skip creating tracefile. If you have full kernel source, run the following command:" "$LINENO" + print_info "$create_tracefile_cli" "$LINENO" + fi +fi + +cd $OLD_PWD +exit $exit_code