blob: 885bb45d823c0a7a317dee084ca59e2ab96381a6 [file] [log] [blame]
Andrei Homescu9a9b1b42022-10-14 01:40:59 +00001/*
2 * Copyright (C) 2022 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 <chrono>
18#include <cstdlib>
19#include <type_traits>
20
21#include "binderRpcTestCommon.h"
22#include "binderRpcTestFixture.h"
23
24using namespace std::chrono_literals;
25using namespace std::placeholders;
26using testing::AssertionFailure;
27using testing::AssertionResult;
28using testing::AssertionSuccess;
29
30namespace android {
31
32static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
33 RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
34
35TEST(BinderRpcParcel, EntireParcelFormatted) {
36 Parcel p;
37 p.writeInt32(3);
38
39 EXPECT_DEATH_IF_SUPPORTED(p.markForBinder(sp<BBinder>::make()),
40 "format must be set before data is written");
41}
42
43TEST(BinderRpc, CannotUseNextWireVersion) {
44 auto session = RpcSession::make();
45 EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
46 EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
47 EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
48 EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
49}
50
51TEST(BinderRpc, CanUseExperimentalWireVersion) {
52 auto session = RpcSession::make();
Steven Moreland0884c552023-10-17 18:23:31 +000053 EXPECT_EQ(hasExperimentalRpc(),
54 session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
Andrei Homescu9a9b1b42022-10-14 01:40:59 +000055}
56
57TEST_P(BinderRpc, Ping) {
58 auto proc = createRpcTestSocketServerProcess({});
59 ASSERT_NE(proc.rootBinder, nullptr);
60 EXPECT_EQ(OK, proc.rootBinder->pingBinder());
61}
62
63TEST_P(BinderRpc, GetInterfaceDescriptor) {
64 auto proc = createRpcTestSocketServerProcess({});
65 ASSERT_NE(proc.rootBinder, nullptr);
66 EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
67}
68
69TEST_P(BinderRpc, MultipleSessions) {
70 if (serverSingleThreaded()) {
71 // Tests with multiple sessions require a multi-threaded service,
72 // but work fine on a single-threaded client
73 GTEST_SKIP() << "This test requires a multi-threaded service";
74 }
75
76 auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
77 for (auto session : proc.proc->sessions) {
78 ASSERT_NE(nullptr, session.root);
79 EXPECT_EQ(OK, session.root->pingBinder());
80 }
81}
82
83TEST_P(BinderRpc, SeparateRootObject) {
84 if (serverSingleThreaded()) {
85 GTEST_SKIP() << "This test requires a multi-threaded service";
86 }
87
Steven Morelandb469f432023-07-28 22:13:47 +000088 SocketType type = GetParam().type;
Andrei Homescu9a9b1b42022-10-14 01:40:59 +000089 if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
Alice Wang893a9912022-10-24 10:44:09 +000090 type == SocketType::UNIX_BOOTSTRAP || type == SocketType::UNIX_RAW) {
Andrei Homescu9a9b1b42022-10-14 01:40:59 +000091 // we can't get port numbers for unix sockets
92 return;
93 }
94
95 auto proc = createRpcTestSocketServerProcess({.numSessions = 2});
96
97 int port1 = 0;
98 EXPECT_OK(proc.rootIface->getClientPort(&port1));
99
100 sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root);
101 int port2;
102 EXPECT_OK(rootIface2->getClientPort(&port2));
103
104 // we should have a different IBinderRpcTest object created for each
105 // session, because we use setPerSessionRootObject
106 EXPECT_NE(port1, port2);
107}
108
109TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
110 auto proc = createRpcTestSocketServerProcess({});
111 Parcel data;
112 Parcel reply;
113 EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
114}
115
116TEST_P(BinderRpc, AppendSeparateFormats) {
Andrei Homescu68a55612022-08-02 01:25:15 +0000117 if (socketType() == SocketType::TIPC) {
118 GTEST_SKIP() << "Trusty does not support multiple server processes";
119 }
120
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000121 auto proc1 = createRpcTestSocketServerProcess({});
122 auto proc2 = createRpcTestSocketServerProcess({});
123
124 Parcel pRaw;
125
126 Parcel p1;
127 p1.markForBinder(proc1.rootBinder);
128 p1.writeInt32(3);
129
130 EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize()));
131 EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize()));
132
133 Parcel p2;
134 p2.markForBinder(proc2.rootBinder);
135 p2.writeInt32(7);
136
137 EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
138 EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
139}
140
141TEST_P(BinderRpc, UnknownTransaction) {
142 auto proc = createRpcTestSocketServerProcess({});
143 Parcel data;
144 data.markForBinder(proc.rootBinder);
145 Parcel reply;
146 EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
147}
148
149TEST_P(BinderRpc, SendSomethingOneway) {
150 auto proc = createRpcTestSocketServerProcess({});
151 EXPECT_OK(proc.rootIface->sendString("asdf"));
152}
153
154TEST_P(BinderRpc, SendAndGetResultBack) {
155 auto proc = createRpcTestSocketServerProcess({});
156 std::string doubled;
157 EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
158 EXPECT_EQ("cool cool ", doubled);
159}
160
161TEST_P(BinderRpc, SendAndGetResultBackBig) {
162 auto proc = createRpcTestSocketServerProcess({});
Andrei Homescu68a55612022-08-02 01:25:15 +0000163 // Trusty has a limit of 4096 bytes for the entire RPC Binder message
164 size_t singleLen = socketType() == SocketType::TIPC ? 512 : 4096;
165 std::string single = std::string(singleLen, 'a');
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000166 std::string doubled;
167 EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
168 EXPECT_EQ(single + single, doubled);
169}
170
171TEST_P(BinderRpc, InvalidNullBinderReturn) {
172 auto proc = createRpcTestSocketServerProcess({});
173
174 sp<IBinder> outBinder;
175 EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL);
176}
177
178TEST_P(BinderRpc, CallMeBack) {
179 auto proc = createRpcTestSocketServerProcess({});
180
181 int32_t pingResult;
182 EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult));
183 EXPECT_EQ(OK, pingResult);
184
185 EXPECT_EQ(0, MyBinderRpcSession::gNum);
186}
187
188TEST_P(BinderRpc, RepeatBinder) {
189 auto proc = createRpcTestSocketServerProcess({});
190
191 sp<IBinder> inBinder = new MyBinderRpcSession("foo");
192 sp<IBinder> outBinder;
193 EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
194 EXPECT_EQ(inBinder, outBinder);
195
196 wp<IBinder> weak = inBinder;
197 inBinder = nullptr;
198 outBinder = nullptr;
199
200 // Force reading a reply, to process any pending dec refs from the other
201 // process (the other process will process dec refs there before processing
202 // the ping here).
203 EXPECT_EQ(OK, proc.rootBinder->pingBinder());
204
205 EXPECT_EQ(nullptr, weak.promote());
206
207 EXPECT_EQ(0, MyBinderRpcSession::gNum);
208}
209
210TEST_P(BinderRpc, RepeatTheirBinder) {
211 auto proc = createRpcTestSocketServerProcess({});
212
213 sp<IBinderRpcSession> session;
214 EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
215
216 sp<IBinder> inBinder = IInterface::asBinder(session);
217 sp<IBinder> outBinder;
218 EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
219 EXPECT_EQ(inBinder, outBinder);
220
221 wp<IBinder> weak = inBinder;
222 session = nullptr;
223 inBinder = nullptr;
224 outBinder = nullptr;
225
226 // Force reading a reply, to process any pending dec refs from the other
227 // process (the other process will process dec refs there before processing
228 // the ping here).
229 EXPECT_EQ(OK, proc.rootBinder->pingBinder());
230
231 EXPECT_EQ(nullptr, weak.promote());
232}
233
234TEST_P(BinderRpc, RepeatBinderNull) {
235 auto proc = createRpcTestSocketServerProcess({});
236
237 sp<IBinder> outBinder;
238 EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
239 EXPECT_EQ(nullptr, outBinder);
240}
241
242TEST_P(BinderRpc, HoldBinder) {
243 auto proc = createRpcTestSocketServerProcess({});
244
245 IBinder* ptr = nullptr;
246 {
247 sp<IBinder> binder = new BBinder();
248 ptr = binder.get();
249 EXPECT_OK(proc.rootIface->holdBinder(binder));
250 }
251
252 sp<IBinder> held;
253 EXPECT_OK(proc.rootIface->getHeldBinder(&held));
254
255 EXPECT_EQ(held.get(), ptr);
256
257 // stop holding binder, because we test to make sure references are cleaned
258 // up
259 EXPECT_OK(proc.rootIface->holdBinder(nullptr));
260 // and flush ref counts
261 EXPECT_EQ(OK, proc.rootBinder->pingBinder());
262}
263
264// START TESTS FOR LIMITATIONS OF SOCKET BINDER
265// These are behavioral differences form regular binder, where certain usecases
266// aren't supported.
267
268TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
Andrei Homescu68a55612022-08-02 01:25:15 +0000269 if (socketType() == SocketType::TIPC) {
270 GTEST_SKIP() << "Trusty does not support multiple server processes";
271 }
272
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000273 auto proc1 = createRpcTestSocketServerProcess({});
274 auto proc2 = createRpcTestSocketServerProcess({});
275
276 sp<IBinder> outBinder;
277 EXPECT_EQ(INVALID_OPERATION,
278 proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
279}
280
281TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
282 if (serverSingleThreaded()) {
283 GTEST_SKIP() << "This test requires a multi-threaded service";
284 }
285
286 auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
287
288 sp<IBinder> outBinder;
289 EXPECT_EQ(INVALID_OPERATION,
290 proc.rootIface->repeatBinder(proc.proc->sessions.at(1).root, &outBinder)
291 .transactionError());
292}
293
294TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
295 if (!kEnableKernelIpc || noKernel()) {
296 GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
297 "at build time.";
298 }
299
300 auto proc = createRpcTestSocketServerProcess({});
301
302 sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
303 sp<IBinder> outBinder;
304 EXPECT_EQ(INVALID_OPERATION,
305 proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
306}
307
308TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
309 if (!kEnableKernelIpc || noKernel()) {
310 GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
311 "at build time.";
312 }
313
314 auto proc = createRpcTestSocketServerProcess({});
315
316 // for historical reasons, IServiceManager interface only returns the
317 // exception code
318 EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED,
319 defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder));
320}
321
322// END TESTS FOR LIMITATIONS OF SOCKET BINDER
323
324TEST_P(BinderRpc, RepeatRootObject) {
325 auto proc = createRpcTestSocketServerProcess({});
326
327 sp<IBinder> outBinder;
328 EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
329 EXPECT_EQ(proc.rootBinder, outBinder);
330}
331
332TEST_P(BinderRpc, NestedTransactions) {
Andrei Homescu68a55612022-08-02 01:25:15 +0000333 auto fileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX;
334 if (socketType() == SocketType::TIPC) {
335 // TIPC does not support file descriptors yet
336 fileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE;
337 }
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000338 auto proc = createRpcTestSocketServerProcess({
339 // Enable FD support because it uses more stack space and so represents
340 // something closer to a worst case scenario.
Andrei Homescu68a55612022-08-02 01:25:15 +0000341 .clientFileDescriptorTransportMode = fileDescriptorTransportMode,
342 .serverSupportedFileDescriptorTransportModes = {fileDescriptorTransportMode},
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000343 });
344
Andrei Homescu9d8adb12022-08-02 04:38:30 +0000345 auto nastyNester = sp<MyBinderRpcTestDefault>::make();
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000346 EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
347
348 wp<IBinder> weak = nastyNester;
349 nastyNester = nullptr;
350 EXPECT_EQ(nullptr, weak.promote());
351}
352
353TEST_P(BinderRpc, SameBinderEquality) {
354 auto proc = createRpcTestSocketServerProcess({});
355
356 sp<IBinder> a;
357 EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
358
359 sp<IBinder> b;
360 EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
361
362 EXPECT_EQ(a, b);
363}
364
365TEST_P(BinderRpc, SameBinderEqualityWeak) {
366 auto proc = createRpcTestSocketServerProcess({});
367
368 sp<IBinder> a;
369 EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
370 wp<IBinder> weak = a;
371 a = nullptr;
372
373 sp<IBinder> b;
374 EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
375
376 // this is the wrong behavior, since BpBinder
377 // doesn't implement onIncStrongAttempted
378 // but make sure there is no crash
379 EXPECT_EQ(nullptr, weak.promote());
380
381 GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder.";
382
383 // In order to fix this:
384 // - need to have incStrongAttempted reflected across IPC boundary (wait for
385 // response to promote - round trip...)
386 // - sendOnLastWeakRef, to delete entries out of RpcState table
387 EXPECT_EQ(b, weak.promote());
388}
389
Andrei Homescu4d9420e2023-01-31 01:38:48 +0000390#define EXPECT_SESSIONS(expected, iface) \
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000391 do { \
392 int session; \
393 EXPECT_OK((iface)->getNumOpenSessions(&session)); \
Andrei Homescu4d9420e2023-01-31 01:38:48 +0000394 EXPECT_EQ(static_cast<int>(expected), session); \
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000395 } while (false)
396
397TEST_P(BinderRpc, SingleSession) {
398 auto proc = createRpcTestSocketServerProcess({});
399
400 sp<IBinderRpcSession> session;
401 EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
402 std::string out;
403 EXPECT_OK(session->getName(&out));
404 EXPECT_EQ("aoeu", out);
405
Andrei Homescu4d9420e2023-01-31 01:38:48 +0000406 EXPECT_SESSIONS(1, proc.rootIface);
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000407 session = nullptr;
Andrei Homescu4d9420e2023-01-31 01:38:48 +0000408 EXPECT_SESSIONS(0, proc.rootIface);
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000409}
410
411TEST_P(BinderRpc, ManySessions) {
412 auto proc = createRpcTestSocketServerProcess({});
413
414 std::vector<sp<IBinderRpcSession>> sessions;
415
416 for (size_t i = 0; i < 15; i++) {
Andrei Homescu4d9420e2023-01-31 01:38:48 +0000417 EXPECT_SESSIONS(i, proc.rootIface);
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000418 sp<IBinderRpcSession> session;
419 EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
420 sessions.push_back(session);
421 }
Andrei Homescu4d9420e2023-01-31 01:38:48 +0000422 EXPECT_SESSIONS(sessions.size(), proc.rootIface);
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000423 for (size_t i = 0; i < sessions.size(); i++) {
424 std::string out;
425 EXPECT_OK(sessions.at(i)->getName(&out));
426 EXPECT_EQ(std::to_string(i), out);
427 }
Andrei Homescu4d9420e2023-01-31 01:38:48 +0000428 EXPECT_SESSIONS(sessions.size(), proc.rootIface);
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000429
430 while (!sessions.empty()) {
431 sessions.pop_back();
Andrei Homescu4d9420e2023-01-31 01:38:48 +0000432 EXPECT_SESSIONS(sessions.size(), proc.rootIface);
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000433 }
Andrei Homescu4d9420e2023-01-31 01:38:48 +0000434 EXPECT_SESSIONS(0, proc.rootIface);
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000435}
436
437TEST_P(BinderRpc, OnewayCallDoesNotWait) {
438 constexpr size_t kReallyLongTimeMs = 100;
439 constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
440
441 auto proc = createRpcTestSocketServerProcess({});
442
443 size_t epochMsBefore = epochMillis();
444
445 EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs));
446
447 size_t epochMsAfter = epochMillis();
448 EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
449}
450
451TEST_P(BinderRpc, Callbacks) {
452 const static std::string kTestString = "good afternoon!";
453
454 for (bool callIsOneway : {true, false}) {
455 for (bool callbackIsOneway : {true, false}) {
456 for (bool delayed : {true, false}) {
457 if (clientOrServerSingleThreaded() &&
458 (callIsOneway || callbackIsOneway || delayed)) {
459 // we have no incoming connections to receive the callback
460 continue;
461 }
462
463 size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1;
464 auto proc = createRpcTestSocketServerProcess(
465 {.numThreads = 1,
466 .numSessions = 1,
Steven Moreland67f85902023-03-15 01:13:49 +0000467 .numIncomingConnectionsBySession = {numIncomingConnections}});
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000468 auto cb = sp<MyBinderRpcCallback>::make();
469
470 if (callIsOneway) {
471 EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed,
472 kTestString));
473 } else {
474 EXPECT_OK(
475 proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString));
476 }
477
478 // if both transactions are synchronous and the response is sent back on the
479 // same thread, everything should have happened in a nested call. Otherwise,
480 // the callback will be processed on another thread.
481 if (callIsOneway || callbackIsOneway || delayed) {
482 using std::literals::chrono_literals::operator""s;
483 RpcMutexUniqueLock _l(cb->mMutex);
484 cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
485 }
486
Andrei Homescu4d9420e2023-01-31 01:38:48 +0000487 EXPECT_EQ(cb->mValues.size(), 1UL)
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000488 << "callIsOneway: " << callIsOneway
489 << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
490 if (cb->mValues.empty()) continue;
491 EXPECT_EQ(cb->mValues.at(0), kTestString)
492 << "callIsOneway: " << callIsOneway
493 << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
494
Steven Moreland67f85902023-03-15 01:13:49 +0000495 proc.forceShutdown();
Andrei Homescu9a9b1b42022-10-14 01:40:59 +0000496 }
497 }
498 }
499}
500
501TEST_P(BinderRpc, OnewayCallbackWithNoThread) {
502 auto proc = createRpcTestSocketServerProcess({});
503 auto cb = sp<MyBinderRpcCallback>::make();
504
505 Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything");
506 EXPECT_EQ(WOULD_BLOCK, status.transactionError());
507}
508
509TEST_P(BinderRpc, AidlDelegatorTest) {
510 auto proc = createRpcTestSocketServerProcess({});
511 auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface);
512 ASSERT_NE(nullptr, myDelegator);
513
514 std::string doubled;
515 EXPECT_OK(myDelegator->doubleString("cool ", &doubled));
516 EXPECT_EQ("cool cool ", doubled);
517}
518
519} // namespace android