private void addProxyMethods(TypeSpec.Builder classBuilder, Element member, int methodIndex) {
        ExecutableElement executableElement = (ExecutableElement) member;
        String methodName = executableElement.getSimpleName().toString();
        boolean isOnewayAnnotated = member.getAnnotation(Oneway.class) != null;
        boolean isOneWay = executableElement.getReturnType().getKind() == TypeKind.VOID
                && isOnewayAnnotated;

        if (!isOneWay && isOnewayAnnotated) {
            logWarning("@Oneway is expected only for methods with void return. Ignoring it for " + member.getSimpleName());
        }


        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(methodName)
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Override.class)
                .returns(TypeName.get(executableElement.getReturnType()));

        //add Exceptions
        for (TypeMirror exceptions : executableElement.getThrownTypes()) {
            methodBuilder.addException(ClassName.bestGuess(exceptions.toString()));
        }

        //add parameters
        int paramIndex = 0;
        for (VariableElement params : executableElement.getParameters()) {
            methodBuilder.addParameter(TypeName.get(params.asType()), params.getSimpleName().toString() + "_" + paramIndex);
            paramIndex++;
        }

        methodBuilder
                .addStatement("__checkProxy()");
        //add statements
        methodBuilder
                .addStatement("android.os.Parcel data = android.os.Parcel.obtain()");

        if (!isOneWay) {
            methodBuilder.addStatement("android.os.Parcel reply = android.os.Parcel.obtain()");
        }

        //add return if any
        if (executableElement.getReturnType().getKind() != TypeKind.VOID) {
            TypeElement genericParceler = getBindingManager().getGenericType(executableElement.getReturnType());
            if (genericParceler != null) {
                methodBuilder.addStatement("$T<$T> result", List.class, genericParceler);
            } else {
                methodBuilder.addStatement(executableElement.getReturnType().toString() + " result");
            }
        }

        //start main body block
        methodBuilder.beginControlFlow("try");

        //write the descriptor
        methodBuilder.addStatement("data.writeInterfaceToken(DESCRIPTOR)");

        List<VariableElement> outParams = new ArrayList<>();

        //pass parameters
        paramIndex = 0;
        for (VariableElement param : executableElement.getParameters()) {
            ParamBuilder.ParamType paramType = param.getAnnotation(ParamIn.class) != null ? ParamBuilder.ParamType.IN
                    : param.getAnnotation(ParamOut.class) != null ? ParamBuilder.ParamType.OUT : ParamBuilder.ParamType.IN_OUT;

            VariableElement wrappedParam = new VariableElementWrapper(param, paramIndex);
            if (paramType != ParamBuilder.ParamType.IN) {
                outParams.add(wrappedParam);
            }
            ParamBuilder paramBuilder = getBindingManager().getBuilderForParam(param.asType());
            if (paramBuilder != null) {
                paramBuilder.writeParamsToProxy(wrappedParam, paramType, methodBuilder);
            } else {
                logError("Parameter cannot be marshalled " + param.getSimpleName());
            }
            paramIndex ++;
        }

        //send remote command
        if (isOneWay) {
            methodBuilder.addStatement("mRemote.transact(TRANSACTION_" + methodName + "_" + methodIndex + ", data, null, android.os.IBinder.FLAG_ONEWAY)");
        } else {
            methodBuilder.addStatement("mRemote.transact(TRANSACTION_" + methodName + "_" + methodIndex + ", data, reply, 0)");
            //read exception if any
            methodBuilder.addStatement("Throwable exception = checkException(reply)");
            methodBuilder.beginControlFlow("if(exception != null)");

            for (TypeMirror exceptions : executableElement.getThrownTypes()) {
                ClassName exceptionCName = ClassName.bestGuess(exceptions.toString());
                methodBuilder.beginControlFlow("if(exception instanceof $T)", exceptionCName);
                methodBuilder.addStatement("throw ($T)exception", exceptionCName);
                methodBuilder.endControlFlow();
            }
            methodBuilder.addStatement("throw ($T)exception", RuntimeException.class);
            methodBuilder.endControlFlow();

            //read result
            if (executableElement.getReturnType().getKind() != TypeKind.VOID) {
                ParamBuilder paramBuilder = getBindingManager().getBuilderForParam(executableElement.getReturnType());
                if (paramBuilder != null) {
                    paramBuilder.readResultsFromProxy(executableElement.getReturnType(), methodBuilder);
                } else {
                    logError("Unmarshellable return type " + executableElement.getReturnType());
                    methodBuilder.addStatement("result = null");
                }
            }

            for (VariableElement param : outParams) {
                ParamBuilder.ParamType paramType = param.getAnnotation(ParamIn.class) != null ? ParamBuilder.ParamType.IN
                        : param.getAnnotation(ParamOut.class) != null ? ParamBuilder.ParamType.OUT : ParamBuilder.ParamType.IN_OUT;
                ParamBuilder paramBuilder = getBindingManager().getBuilderForParam(param.asType());
                if (paramBuilder != null) {
                    paramBuilder.readOutParamsFromProxy(param, paramType, methodBuilder);
                }
            }
        }

        //end of try
        methodBuilder.endControlFlow();


        //catch rethrow
        methodBuilder.beginControlFlow("catch ($T re)", ClassName.get("android.os", "RemoteException"));
        methodBuilder.addStatement("throw new $T(re)", RuntimeException.class);
        methodBuilder.endControlFlow();


        //finally block
        methodBuilder.beginControlFlow("finally");
        if (!isOneWay) {
            methodBuilder.addStatement("reply.recycle()");
        }
        methodBuilder.addStatement("data.recycle()");
        methodBuilder.endControlFlow();
        if (executableElement.getReturnType().getKind() != TypeKind.VOID) {
            methodBuilder.addStatement("return result");
        }

        classBuilder.addMethod(methodBuilder.build());
    }
private void addProxyMethods ( TypeSpec . Builder classBuilder , Element member , int methodIndex ) { ExecutableElement executableElement = ( ExecutableElement ) member ; String methodName = executableElement . getSimpleName ( ) . toString ( ) ; boolean isOnewayAnnotated = member . getAnnotation ( Oneway . class ) != null ; boolean isOneWay = executableElement . getReturnType ( ) . getKind ( ) == TypeKind . VOID && isOnewayAnnotated ; if ( ! isOneWay && isOnewayAnnotated ) { logWarning ( "@Oneway is expected only for methods with void return. Ignoring it for " + member . getSimpleName ( ) ) ; } MethodSpec . Builder methodBuilder = MethodSpec . methodBuilder ( methodName ) . addModifiers ( Modifier . PUBLIC ) . addAnnotation ( Override . class ) . returns ( TypeName . get ( executableElement . getReturnType ( ) ) ) ; for ( TypeMirror exceptions : executableElement . getThrownTypes ( ) ) { methodBuilder . addException ( ClassName . bestGuess ( exceptions . toString ( ) ) ) ; } int paramIndex = 0 ; for ( VariableElement params : executableElement . getParameters ( ) ) { methodBuilder . addParameter ( TypeName . get ( params . asType ( ) ) , params . getSimpleName ( ) . toString ( ) + "_" + paramIndex ) ; paramIndex ++ ; } methodBuilder . addStatement ( "__checkProxy()" ) ; methodBuilder . addStatement ( "android.os.Parcel data = android.os.Parcel.obtain()" ) ; if ( ! isOneWay ) { methodBuilder . addStatement ( "android.os.Parcel reply = android.os.Parcel.obtain()" ) ; } if ( executableElement . getReturnType ( ) . getKind ( ) != TypeKind . VOID ) { TypeElement genericParceler = getBindingManager ( ) . getGenericType ( executableElement . getReturnType ( ) ) ; if ( genericParceler != null ) { methodBuilder . addStatement ( "$T<$T> result" , List . class , genericParceler ) ; } else { methodBuilder . addStatement ( executableElement . getReturnType ( ) . toString ( ) + " result" ) ; } } methodBuilder . beginControlFlow ( "try" ) ; methodBuilder . addStatement ( "data.writeInterfaceToken(DESCRIPTOR)" ) ; List < VariableElement > outParams = new ArrayList < > ( ) ; paramIndex = 0 ; for ( VariableElement param : executableElement . getParameters ( ) ) { ParamBuilder . ParamType paramType = param . getAnnotation ( ParamIn . class ) != null ? ParamBuilder . ParamType . IN : param . getAnnotation ( ParamOut . class ) != null ? ParamBuilder . ParamType . OUT : ParamBuilder . ParamType . IN_OUT ; VariableElement wrappedParam = new VariableElementWrapper ( param , paramIndex ) ; if ( paramType != ParamBuilder . ParamType . IN ) { outParams . add ( wrappedParam ) ; } ParamBuilder paramBuilder = getBindingManager ( ) . getBuilderForParam ( param . asType ( ) ) ; if ( paramBuilder != null ) { paramBuilder . writeParamsToProxy ( wrappedParam , paramType , methodBuilder ) ; } else { logError ( "Parameter cannot be marshalled " + param . getSimpleName ( ) ) ; } paramIndex ++ ; } if ( isOneWay ) { methodBuilder . addStatement ( "mRemote.transact(TRANSACTION_" + methodName + "_" + methodIndex + ", data, null, android.os.IBinder.FLAG_ONEWAY)" ) ; } else { methodBuilder . addStatement ( "mRemote.transact(TRANSACTION_" + methodName + "_" + methodIndex + ", data, reply, 0)" ) ; methodBuilder . addStatement ( "Throwable exception = checkException(reply)" ) ; methodBuilder . beginControlFlow ( "if(exception != null)" ) ; for ( TypeMirror exceptions : executableElement . getThrownTypes ( ) ) { ClassName exceptionCName = ClassName . bestGuess ( exceptions . toString ( ) ) ; methodBuilder . beginControlFlow ( "if(exception instanceof $T)" , exceptionCName ) ; methodBuilder . addStatement ( "throw ($T)exception" , exceptionCName ) ; methodBuilder . endControlFlow ( ) ; } methodBuilder . addStatement ( "throw ($T)exception" , RuntimeException . class ) ; methodBuilder . endControlFlow ( ) ; if ( executableElement . getReturnType ( ) . getKind ( ) != TypeKind . VOID ) { ParamBuilder paramBuilder = getBindingManager ( ) . getBuilderForParam ( executableElement . getReturnType ( ) ) ; if ( paramBuilder != null ) { paramBuilder . readResultsFromProxy ( executableElement . getReturnType ( ) , methodBuilder ) ; } else { logError ( "Unmarshellable return type " + executableElement . getReturnType ( ) ) ; methodBuilder . addStatement ( "result = null" ) ; } } for ( VariableElement param : outParams ) { ParamBuilder . ParamType paramType = param . getAnnotation ( ParamIn . class ) != null ? ParamBuilder . ParamType . IN : param . getAnnotation ( ParamOut . class ) != null ? ParamBuilder . ParamType . OUT : ParamBuilder . ParamType . IN_OUT ; ParamBuilder paramBuilder = getBindingManager ( ) . getBuilderForParam ( param . asType ( ) ) ; if ( paramBuilder != null ) { paramBuilder . readOutParamsFromProxy ( param , paramType , methodBuilder ) ; } } } methodBuilder . endControlFlow ( ) ; methodBuilder . beginControlFlow ( "catch ($T re)" , ClassName . get ( "android.os" , "RemoteException" ) ) ; methodBuilder . addStatement ( "throw new $T(re)" , RuntimeException . class ) ; methodBuilder . endControlFlow ( ) ; methodBuilder . beginControlFlow ( "finally" ) ; if ( ! isOneWay ) { methodBuilder . addStatement ( "reply.recycle()" ) ; } methodBuilder . addStatement ( "data.recycle()" ) ; methodBuilder . endControlFlow ( ) ; if ( executableElement . getReturnType ( ) . getKind ( ) != TypeKind . VOID ) { methodBuilder . addStatement ( "return result" ) ; } classBuilder . addMethod ( methodBuilder . build ( ) ) ; }
Build the proxy methods