1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <inttypes.h>
18 #include <utils/StrongPointer.h>
19 #include <chrono>
20 #include <cstdint>
21 #include <future>
22 #include <thread>
23 
24 #include "chre/util/nanoapp/app_id.h"
25 #include "chre_host/host_protocol_host.h"
26 #include "chre_host/log.h"
27 #include "chre_host/pigweed/hal_rpc_client.h"
28 #include "chre_host/socket_client.h"
29 #include "rpc_world.pb.h"
30 #include "rpc_world.rpc.pb.h"
31 
32 /**
33  * @file
34  * Test RPC by calling a service provided by the rpc_world nanoapp.
35  *
36  * Usage:
37  * 1. Compile and push the rpc_world nanoapp to the device.
38  *
39  * 2. Load the nanoapp:
40  *    adb shell chre_test_client load_with_header \
41  *      /vendor/etc/chre/rpc_world.napp_header \
42  *      /vendor/etc/chre/rpc_world.so
43  *
44  * 3. Build this test and push it to the device:
45  *    m chre_test_rpc
46  *    adb push \
47  *      out/target/product/<product>/vendor/bin/chre_test_rpc \
48  *      /vendor/bin/chre_test_rpc
49  *
50  * 4. Launch the test:
51  *    adb shell chre_test_rpc
52  */
53 
54 namespace {
55 
56 using ::android::sp;
57 using ::android::chre::HalRpcClient;
58 using ::android::chre::HostProtocolHost;
59 using ::android::chre::IChreMessageHandlers;
60 using ::android::chre::SocketClient;
61 using ::flatbuffers::FlatBufferBuilder;
62 
63 // Aliased for consistency with the way these symbols are referenced in
64 // CHRE-side code
65 namespace fbs = ::chre::fbs;
66 
67 constexpr uint16_t kHostEndpoint = 0x8006;
68 
69 constexpr uint32_t kRequestNumber = 10;
70 std::promise<uint32_t> gResponsePromise;
71 
72 class SocketCallbacks : public SocketClient::ICallbacks,
73                         public IChreMessageHandlers {
74  public:
onMessageReceived(const void * data,size_t length)75   void onMessageReceived(const void *data, size_t length) override {
76     if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
77       LOGE("Failed to decode message");
78     }
79   }
80 
handleNanoappListResponse(const fbs::NanoappListResponseT & response)81   void handleNanoappListResponse(
82       const fbs::NanoappListResponseT &response) override {
83     LOGI("Got nanoapp list response with %zu apps", response.nanoapps.size());
84   }
85 };
86 
87 }  // namespace
88 
incrementResponse(const chre_rpc_NumberMessage & response,pw::Status status)89 void incrementResponse(const chre_rpc_NumberMessage &response,
90                        pw::Status status) {
91   if (status.ok()) {
92     gResponsePromise.set_value(response.number);
93   } else {
94     LOGE("Increment failed with status %d", static_cast<int>(status.code()));
95   }
96 }
97 
main(int argc,char * argv[])98 int main(int argc, char *argv[]) {
99   UNUSED_VAR(argc);
100   UNUSED_VAR(argv);
101 
102   SocketClient socketClient;
103 
104   auto callbacks = sp<SocketCallbacks>::make();
105 
106   std::unique_ptr<HalRpcClient> rpcClient =
107       HalRpcClient::createClient("chre_test_rpc", socketClient, callbacks,
108                                  kHostEndpoint, chre::kRpcWorldAppId);
109 
110   if (rpcClient == nullptr) {
111     LOGE("Failed to create the RPC client");
112     return -1;
113   }
114 
115   if (!rpcClient->hasService(/* id= */ 0xca8f7150a3f05847,
116                              /* version= */ 0x01020034)) {
117     LOGE("RpcWorld service not found");
118     return -1;
119   }
120 
121   auto client =
122       rpcClient->get<chre::rpc::pw_rpc::nanopb::RpcWorldService::Client>();
123 
124   chre_rpc_NumberMessage incrementRequest;
125   incrementRequest.number = kRequestNumber;
126 
127   pw::rpc::NanopbUnaryReceiver<chre_rpc_NumberMessage> call =
128       client->Increment(incrementRequest, incrementResponse);
129 
130   if (!call.active()) {
131     LOGE("Failed to call the service");
132     return -1;
133   }
134 
135   std::future<uint32_t> response = gResponsePromise.get_future();
136 
137   if (response.wait_for(std::chrono::seconds(2)) != std::future_status::ready) {
138     LOGE("No response received from RPC");
139   } else {
140     const uint32_t value = response.get();
141     LOGI("The RPC service says %" PRIu32 " + 1 = %" PRIu32, kRequestNumber,
142          value);
143   }
144 
145   rpcClient->close();
146 
147   return 0;
148 }
149