Annotate lambdas capturing newer-framework classes

b/391766151 is caused by the combination of three things :
• Newer compilers, such as the Kotlin K2 compiler, will use the
  `invokedynamic` instruction introduced in Java 7.
  → Before then, lambdas had to be wrapped in synthetic classes,
    which would be generated by the compiler. The compiler would
    then generate code that instantiates the synthetic class and
    pass it to the callee as the lambda.
  → After then, there is no need for synthetic classes. Instead, the
    compiler synthesizes a static method that corresponds to the
    lambda and emits an invokedynamic opcode at that call site.
    The JVM will then lookup the class dynamically and create a
    MethodHandle object from which it can substitute an immediate
    method call at runtime.
  → The new way means a much smaller footprint (because
    there are less classes in bytecode) and typically much better
    call performance after the initial resolution, at the cost of linking
    the method at the first execution of the lambda. For most
    modern apps, the trade-off is very advantageous.
  → In this case, the methods that accept the lambda are of the
    `runAsShell` family.
  → Because there are captured local variables of the *WaiterCallback
    types, the generated static methods take instances of these types.
• JUnit uses reflection to enumerate the methods of test classes.
  It does not take care to only enumerate public methods, and as
  such will try to resolve all private methods including the types of
  their arguments.
  → Note that the `Class` class has a way to retrieve all methods
    of public visibility including inherited methods, a way to
    retrieve all non-inherited methods including all visibilities, but
    not a way to retrieve only public locally-declared methods, so
    JUnit's implementation is understandable.
  → In this case, this means JUnit has code that will try to resolve
    the *WaiterCallback classes.
• The test code is compiled against newer SDKs but run with the
  JUnit test harness on older SDKs which don't have the base classes
  for the *WaiterCallback classes.

The net result is that as JUnit enumerates methods, it finds the
private synthetic methods emitted by the newer compilers, which
causes the JVM to try and resolve the type of the arguments, which
in turn is not possible on older frameworks. The test run promptly
crashes with ClassNotFoundException before it can run any test
method.

Note that this would also happen with newer Java compilers.

This patch solves this method by annotating the relevant
lambdas with @JvmSerializableLambda, which causes the
Kotlin compiler to use the old behavior on just those lambdas.

The drawback of this patch is that the @JvmSerializableLambda
feature is pretty obscure and it needs to be managed per-lambda.

Bug: 391766151
Test: revert aosp/3464183 on top of this, then atest NetworkAgentTest
      on an S device, then atest CtsNetTestCases to test whether there
      are other instances of this issue
Change-Id: Iacaadddb597c4a5dfdd8c497e6ebc1243ae1e923
1 file changed