Merge "Add force downgrade to vendor stability test"
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 9de344a..3ebdeee 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -14,6 +14,7 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
srcs: [
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 0dbab4e..2f55249 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -239,7 +239,8 @@
#endif // !VENDORSERVICEMANAGER
// implicitly unlinked when the binder is removed
- if (binder->remoteBinder() != nullptr && binder->linkToDeath(this) != OK) {
+ if (binder->remoteBinder() != nullptr &&
+ binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) {
LOG(ERROR) << "Could not linkToDeath when adding " << name;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
@@ -307,7 +308,9 @@
return Status::fromExceptionCode(Status::EX_NULL_POINTER);
}
- if (OK != IInterface::asBinder(callback)->linkToDeath(this)) {
+ if (OK !=
+ IInterface::asBinder(callback)->linkToDeath(
+ sp<ServiceManager>::fromExisting(this))) {
LOG(ERROR) << "Could not linkToDeath when adding " << name;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
@@ -461,7 +464,8 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
}
- if (OK != IInterface::asBinder(cb)->linkToDeath(this)) {
+ if (OK !=
+ IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) {
LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
@@ -491,7 +495,7 @@
}
ssize_t ServiceManager::Service::getNodeStrongRefCount() {
- sp<BpBinder> bpBinder = binder->remoteBinder();
+ sp<BpBinder> bpBinder = sp<BpBinder>::fromExisting(binder->remoteBinder());
if (bpBinder == nullptr) return -1;
return ProcessState::self()->getStrongRefCountForNode(bpBinder);
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 627dfe6..8c1beac 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -39,7 +39,7 @@
class BinderCallback : public LooperCallback {
public:
static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
- sp<BinderCallback> cb = new BinderCallback;
+ sp<BinderCallback> cb = sp<BinderCallback>::make();
int binder_fd = -1;
IPCThreadState::self()->setupPolling(&binder_fd);
@@ -65,7 +65,7 @@
class ClientCallbackCallback : public LooperCallback {
public:
static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
- sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager);
+ sp<ClientCallbackCallback> cb = sp<ClientCallbackCallback>::make(manager);
int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
@@ -105,6 +105,7 @@
return 1; // Continue receiving callbacks.
}
private:
+ friend sp<ClientCallbackCallback>;
ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {}
sp<ServiceManager> mManager;
};
@@ -120,7 +121,7 @@
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
- sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
+ sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
}
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index fb9f9df..5d5a75e 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -46,7 +46,7 @@
}
};
- return new LinkableBinder;
+ return sp<LinkableBinder>::make();
}
class MockAccess : public Access {
@@ -71,7 +71,7 @@
ON_CALL(*access, canFind(_, _)).WillByDefault(Return(true));
ON_CALL(*access, canList(_)).WillByDefault(Return(true));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
return sm;
}
@@ -119,7 +119,7 @@
.uid = uid,
}));
EXPECT_CALL(*access, canAdd(_, _)).Times(0);
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -161,7 +161,7 @@
EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(false));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -194,7 +194,7 @@
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -218,7 +218,7 @@
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
sp<IBinder> service = getBinder();
EXPECT_TRUE(sm->addService("foo", service, true /*allowIsolated*/,
@@ -244,7 +244,7 @@
// TODO(b/136023468): when security check is first, this should be called first
// EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -261,7 +261,7 @@
EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canList(_)).WillOnce(Return(false));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
std::vector<std::string> out;
EXPECT_FALSE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &out).isOk());
@@ -329,9 +329,9 @@
EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false));
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = sp<ServiceManager>::make(std::move(access));
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
EXPECT_EQ(sm->registerForNotifications("foofoo", cb).exceptionCode(),
Status::EX_SECURITY);
@@ -343,9 +343,9 @@
EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false));
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = sp<ServiceManager>::make(std::move(access));
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
// should always hit security error first
EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(),
@@ -355,7 +355,7 @@
TEST(ServiceNotifications, InvalidName) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
EXPECT_EQ(sm->registerForNotifications("foo@foo", cb).exceptionCode(),
Status::EX_ILLEGAL_ARGUMENT);
@@ -371,7 +371,7 @@
TEST(ServiceNotifications, Unregister) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk());
EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), 0);
@@ -380,7 +380,7 @@
TEST(ServiceNotifications, UnregisterWhenNoRegistrationExists) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(),
Status::EX_ILLEGAL_STATE);
@@ -389,7 +389,7 @@
TEST(ServiceNotifications, NoNotification) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk());
EXPECT_TRUE(sm->addService("otherservice", getBinder(),
@@ -402,7 +402,7 @@
TEST(ServiceNotifications, GetNotification) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
sp<IBinder> service = getBinder();
@@ -417,7 +417,7 @@
TEST(ServiceNotifications, GetNotificationForAlreadyRegisteredService) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
sp<IBinder> service = getBinder();
@@ -433,7 +433,7 @@
TEST(ServiceNotifications, GetMultipleNotification) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
sp<IBinder> binder1 = getBinder();
sp<IBinder> binder2 = getBinder();
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index f28af96..2715d6a 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -69,7 +69,6 @@
"IUidObserver.cpp",
"PermissionCache.cpp",
"PermissionController.cpp",
- "IpPrefix.cpp",
":activity_manager_procstate_aidl",
]
diff --git a/libs/binder/IpPrefix.cpp b/libs/binder/IpPrefix.cpp
deleted file mode 100644
index 4edc493..0000000
--- a/libs/binder/IpPrefix.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "IpPrefix"
-
-#include <binder/IpPrefix.h>
-#include <vector>
-
-#include <binder/IBinder.h>
-#include <binder/Parcel.h>
-#include <log/log.h>
-#include <utils/Errors.h>
-
-using android::BAD_VALUE;
-using android::NO_ERROR;
-using android::Parcel;
-using android::status_t;
-
-namespace android {
-
-namespace net {
-
-#define RETURN_IF_FAILED(calledOnce) \
- { \
- status_t returnStatus = calledOnce; \
- if (returnStatus) { \
- ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
- return returnStatus; \
- } \
- }
-
-status_t IpPrefix::writeToParcel(Parcel* parcel) const {
- /*
- * Keep implementation in sync with writeToParcel() in
- * frameworks/base/core/java/android/net/IpPrefix.java.
- */
- std::vector<uint8_t> byte_vector;
-
- if (mIsIpv6) {
- const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mIn6Addr);
- byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
- } else {
- const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mInAddr);
- byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
- }
-
- RETURN_IF_FAILED(parcel->writeByteVector(byte_vector));
- RETURN_IF_FAILED(parcel->writeInt32(static_cast<int32_t>(mPrefixLength)));
-
- return NO_ERROR;
-}
-
-status_t IpPrefix::readFromParcel(const Parcel* parcel) {
- /*
- * Keep implementation in sync with readFromParcel() in
- * frameworks/base/core/java/android/net/IpPrefix.java.
- */
- std::vector<uint8_t> byte_vector;
-
- RETURN_IF_FAILED(parcel->readByteVector(&byte_vector));
- RETURN_IF_FAILED(parcel->readInt32(&mPrefixLength));
-
- if (byte_vector.size() == 16) {
- mIsIpv6 = true;
- memcpy((void*)&mUnion.mIn6Addr, &byte_vector[0], sizeof(mUnion.mIn6Addr));
-
- } else if (byte_vector.size() == 4) {
- mIsIpv6 = false;
- memcpy((void*)&mUnion.mInAddr, &byte_vector[0], sizeof(mUnion.mInAddr));
-
- } else {
- ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
- return BAD_VALUE;
- }
-
- return NO_ERROR;
-}
-
-const struct in6_addr& IpPrefix::getAddressAsIn6Addr() const
-{
- return mUnion.mIn6Addr;
-}
-
-const struct in_addr& IpPrefix::getAddressAsInAddr() const
-{
- return mUnion.mInAddr;
-}
-
-bool IpPrefix::getAddressAsIn6Addr(struct in6_addr* addr) const
-{
- if (isIpv6()) {
- *addr = mUnion.mIn6Addr;
- return true;
- }
- return false;
-}
-
-bool IpPrefix::getAddressAsInAddr(struct in_addr* addr) const
-{
- if (isIpv4()) {
- *addr = mUnion.mInAddr;
- return true;
- }
- return false;
-}
-
-bool IpPrefix::isIpv6() const
-{
- return mIsIpv6;
-}
-
-bool IpPrefix::isIpv4() const
-{
- return !mIsIpv6;
-}
-
-int32_t IpPrefix::getPrefixLength() const
-{
- return mPrefixLength;
-}
-
-void IpPrefix::setAddress(const struct in6_addr& addr)
-{
- mUnion.mIn6Addr = addr;
- mIsIpv6 = true;
-}
-
-void IpPrefix::setAddress(const struct in_addr& addr)
-{
- mUnion.mInAddr = addr;
- mIsIpv6 = false;
-}
-
-void IpPrefix::setPrefixLength(int32_t prefix)
-{
- mPrefixLength = prefix;
-}
-
-bool operator==(const IpPrefix& lhs, const IpPrefix& rhs)
-{
- if (lhs.mIsIpv6 != rhs.mIsIpv6) {
- return false;
- }
-
- if (lhs.mPrefixLength != rhs.mPrefixLength) {
- return false;
- }
-
- if (lhs.mIsIpv6) {
- return 0 == memcmp(lhs.mUnion.mIn6Addr.s6_addr, rhs.mUnion.mIn6Addr.s6_addr, sizeof(struct in6_addr));
- }
-
- return 0 == memcmp(&lhs.mUnion.mInAddr, &rhs.mUnion.mInAddr, sizeof(struct in_addr));
-}
-
-} // namespace net
-
-} // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 34a474b..3f0b0df 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -555,12 +555,17 @@
}
void Parcel::markForBinder(const sp<IBinder>& binder) {
+ LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written");
+
if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection());
}
}
void Parcel::markForRpc(const sp<RpcConnection>& connection) {
+ LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr,
+ "format must be set before data is written OR on IPC data");
+
LOG_ALWAYS_FATAL_IF(connection == nullptr, "markForRpc requires connection");
mConnection = connection;
}
@@ -2100,6 +2105,9 @@
void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
const binder_size_t* objects, size_t objectsCount, release_func relFunc)
{
+ // this code uses 'mOwner == nullptr' to understand whether it owns memory
+ LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function");
+
freeData();
mData = const_cast<uint8_t*>(data);
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 3b3adca..755ff35 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -312,8 +312,8 @@
return waitForReply(fd, connection, reply);
}
-static void cleanup_data(Parcel* p, const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsCount) {
+static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsCount) {
(void)p;
delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data));
(void)dataSize;
@@ -351,7 +351,7 @@
if (rpcReply->status != OK) return rpcReply->status;
reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
- nullptr, 0, cleanup_data);
+ nullptr, 0, cleanup_reply_data);
reply->markForRpc(connection);
@@ -427,6 +427,15 @@
return processTransactInternal(fd, connection, std::move(transactionData));
}
+static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsCount) {
+ (void)p;
+ (void)data;
+ (void)dataSize;
+ (void)objects;
+ (void)objectsCount;
+}
+
status_t RpcState::processTransactInternal(const base::unique_fd& fd,
const sp<RpcConnection>& connection,
std::vector<uint8_t>&& transactionData) {
@@ -490,7 +499,12 @@
}
Parcel data;
- data.setData(transaction->data, transactionData.size() - offsetof(RpcWireTransaction, data));
+ // transaction->data is owned by this function. Parcel borrows this data and
+ // only holds onto it for the duration of this function call. Parcel will be
+ // deleted before the 'transactionData' object.
+ data.ipcSetDataReference(transaction->data,
+ transactionData.size() - offsetof(RpcWireTransaction, data),
+ nullptr /*object*/, 0 /*objectCount*/, do_nothing_to_transact_data);
data.markForRpc(connection);
Parcel reply;
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index f4a21dd..ea91753 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -186,7 +186,7 @@
inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
const String16& _descriptor)
{
- if (_descriptor == INTERFACE::descriptor) return this;
+ if (_descriptor == INTERFACE::descriptor) return sp<IInterface>::fromExisting(this);
return nullptr;
}
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h
deleted file mode 100644
index a8faa3f..0000000
--- a/libs/binder/include/binder/IpPrefix.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#pragma once
-
-#ifndef __ANDROID_VNDK__
-
-#include <netinet/in.h>
-
-#include <binder/Parcelable.h>
-#include <utils/String16.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-
-namespace net {
-
-/*
- * C++ implementation of the Java class android.net.IpPrefix
- */
-class IpPrefix : public Parcelable {
-public:
- IpPrefix() = default;
- virtual ~IpPrefix() = default;
- IpPrefix(const IpPrefix& prefix) = default;
-
- IpPrefix(const struct in6_addr& addr, int32_t plen):
- mUnion(addr), mPrefixLength(plen), mIsIpv6(true) { }
-
- IpPrefix(const struct in_addr& addr, int32_t plen):
- mUnion(addr), mPrefixLength(plen), mIsIpv6(false) { }
-
- bool getAddressAsIn6Addr(struct in6_addr* addr) const;
- bool getAddressAsInAddr(struct in_addr* addr) const;
-
- const struct in6_addr& getAddressAsIn6Addr() const;
- const struct in_addr& getAddressAsInAddr() const;
-
- bool isIpv6() const;
- bool isIpv4() const;
-
- int32_t getPrefixLength() const;
-
- void setAddress(const struct in6_addr& addr);
- void setAddress(const struct in_addr& addr);
-
- void setPrefixLength(int32_t prefix);
-
- friend bool operator==(const IpPrefix& lhs, const IpPrefix& rhs);
-
- friend bool operator!=(const IpPrefix& lhs, const IpPrefix& rhs) {
- return !(lhs == rhs);
- }
-
-public:
- // Overrides
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
-private:
- union InternalUnion {
- InternalUnion() = default;
- explicit InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }
- explicit InternalUnion(const struct in_addr &addr):mInAddr(addr) { }
- struct in6_addr mIn6Addr;
- struct in_addr mInAddr;
- } mUnion;
- int32_t mPrefixLength;
- bool mIsIpv6;
-};
-
-} // namespace net
-
-} // namespace android
-
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9578372..211790d 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -101,10 +101,6 @@
// is for an RPC transaction).
void markForBinder(const sp<IBinder>& binder);
- // Whenever possible, markForBinder should be preferred. This method is
- // called automatically on reply Parcels for RPC transactions.
- void markForRpc(const sp<RpcConnection>& connection);
-
// Whether this Parcel is written for RPC transactions (after calls to
// markForBinder or markForRpc).
bool isForRpc() const;
@@ -540,6 +536,10 @@
const binder_size_t* objects, size_t objectsCount,
release_func relFunc);
+ // Whenever possible, markForBinder should be preferred. This method is
+ // called automatically on reply Parcels for RPC transactions.
+ void markForRpc(const sp<RpcConnection>& connection);
+
status_t finishWrite(size_t len);
void releaseObjects();
void acquireObjects();
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
index a300575..efa922d 100644
--- a/libs/binder/include/binder/RpcConnection.h
+++ b/libs/binder/include/binder/RpcConnection.h
@@ -106,6 +106,7 @@
};
private:
+ friend sp<RpcConnection>;
RpcConnection();
bool addServer(const SocketAddress& address);
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 0f59de4..76afd4c 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -598,6 +598,8 @@
}
*in = new AParcel(binder);
+ (*in)->get()->markForBinder(binder->getBinder());
+
status_t status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor());
binder_status_t ret = PruneStatusT(status);
diff --git a/libs/binder/ndk/parcel_internal.h b/libs/binder/ndk/parcel_internal.h
index aebc7f4..b4f6358 100644
--- a/libs/binder/ndk/parcel_internal.h
+++ b/libs/binder/ndk/parcel_internal.h
@@ -29,11 +29,7 @@
explicit AParcel(AIBinder* binder) : AParcel(binder, new ::android::Parcel, true /*owns*/) {}
AParcel(AIBinder* binder, ::android::Parcel* parcel, bool owns)
- : mBinder(binder), mParcel(parcel), mOwns(owns) {
- if (binder != nullptr) {
- parcel->markForBinder(binder->getBinder());
- }
- }
+ : mBinder(binder), mParcel(parcel), mOwns(owns) {}
~AParcel() {
if (mOwns) {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 5f68a25..985d086 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -44,6 +44,13 @@
namespace android {
+TEST(BinderRpcParcel, EntireParcelFormatted) {
+ Parcel p;
+ p.writeInt32(3);
+
+ EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
+}
+
using android::binder::Status;
#define EXPECT_OK(status) \
diff --git a/services/memtrackproxy/Android.bp b/services/memtrackproxy/Android.bp
new file mode 100644
index 0000000..ee8b247
--- /dev/null
+++ b/services/memtrackproxy/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2021 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.
+
+cc_library_shared {
+ name: "libmemtrackproxy",
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libcutils",
+ "libutils",
+ "android.hardware.memtrack@1.0",
+ "android.hardware.memtrack-V1-ndk_platform",
+ ],
+ srcs: [
+ "MemtrackProxy.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ local_include_dirs: [
+ "include/memtrackproxy",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.memtrack@1.0",
+ "android.hardware.memtrack-V1-ndk_platform",
+ ],
+}
diff --git a/services/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp
new file mode 100644
index 0000000..8da6e89
--- /dev/null
+++ b/services/memtrackproxy/MemtrackProxy.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include "MemtrackProxy.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <private/android_filesystem_config.h>
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+// Check Memtrack Flags
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SMAPS_ACCOUNTED) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SMAPS_ACCOUNTED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SMAPS_UNACCOUNTED) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SMAPS_UNACCOUNTED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SHARED) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SHARED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SHARED_PSS) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SHARED_PSS));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::PRIVATE) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_PRIVATE));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SYSTEM) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SYSTEM));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::DEDICATED) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_DEDICATED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::NONSECURE) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_NONSECURE));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SECURE) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SECURE));
+
+// Check Memtrack Types
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::OTHER) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackType::OTHER));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::GL) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackType::GL));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::GRAPHICS) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackType::GRAPHICS));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::MULTIMEDIA) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackType::MULTIMEDIA));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::CAMERA) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackType::CAMERA));
+
+__attribute__((warn_unused_result)) bool translate(const V1_0_hidl::MemtrackRecord& in,
+ V1_aidl::MemtrackRecord* out) {
+ // Convert uint64_t to int64_t (long in AIDL). AIDL doesn't support unsigned types.
+ if (in.sizeInBytes > std::numeric_limits<int64_t>::max() || in.sizeInBytes < 0) {
+ return false;
+ }
+ out->sizeInBytes = static_cast<int64_t>(in.sizeInBytes);
+
+ // It's ok to just assign directly, since this is a bitmap.
+ out->flags = in.flags;
+ return true;
+}
+
+sp<V1_0_hidl::IMemtrack> MemtrackProxy::MemtrackHidlInstance() {
+ return V1_0_hidl::IMemtrack::getService();
+}
+
+std::shared_ptr<V1_aidl::IMemtrack> MemtrackProxy::MemtrackAidlInstance() {
+ const auto instance = std::string() + V1_aidl::IMemtrack::descriptor + "/default";
+ bool declared = AServiceManager_isDeclared(instance.c_str());
+ if (!declared) {
+ return nullptr;
+ }
+ ndk::SpAIBinder memtrack_binder =
+ ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str()));
+ return V1_aidl::IMemtrack::fromBinder(memtrack_binder);
+}
+
+bool MemtrackProxy::CheckUid(uid_t calling_uid) {
+ // Allow AID_SHELL for adb shell dumpsys meminfo
+ return calling_uid == AID_SYSTEM || calling_uid == AID_ROOT || calling_uid == AID_SHELL;
+}
+
+bool MemtrackProxy::CheckPid(pid_t calling_pid, pid_t request_pid) {
+ return calling_pid == request_pid;
+}
+
+MemtrackProxy::MemtrackProxy()
+ : memtrack_hidl_instance_(MemtrackProxy::MemtrackHidlInstance()),
+ memtrack_aidl_instance_(MemtrackProxy::MemtrackAidlInstance()) {}
+
+ndk::ScopedAStatus MemtrackProxy::getMemory(int pid, MemtrackType type,
+ std::vector<MemtrackRecord>* _aidl_return) {
+ if (pid < 0) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (!MemtrackProxy::CheckPid(AIBinder_getCallingPid(), pid) &&
+ !MemtrackProxy::CheckUid(AIBinder_getCallingUid())) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_SECURITY,
+ "Only AID_ROOT, AID_SYSTEM and AID_SHELL can request getMemory() for PIDs other "
+ "than the calling PID");
+ }
+
+ if (type != MemtrackType::OTHER && type != MemtrackType::GL && type != MemtrackType::GRAPHICS &&
+ type != MemtrackType::MULTIMEDIA && type != MemtrackType::CAMERA) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+
+ _aidl_return->clear();
+
+ if (memtrack_aidl_instance_ ||
+ (memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance())) {
+ return memtrack_aidl_instance_->getMemory(pid, type, _aidl_return);
+ } else if (memtrack_hidl_instance_ ||
+ (memtrack_hidl_instance_ = MemtrackProxy::MemtrackHidlInstance())) {
+ ndk::ScopedAStatus aidl_status;
+
+ Return<void> ret = memtrack_hidl_instance_->getMemory(
+ pid, static_cast<V1_0_hidl::MemtrackType>(type),
+ [&_aidl_return, &aidl_status](V1_0_hidl::MemtrackStatus status,
+ hidl_vec<V1_0_hidl::MemtrackRecord> records) {
+ switch (status) {
+ case V1_0_hidl::MemtrackStatus::SUCCESS:
+ aidl_status = ndk::ScopedAStatus::ok();
+ break;
+ case V1_0_hidl::MemtrackStatus::MEMORY_TRACKING_NOT_SUPPORTED:
+ [[fallthrough]];
+ case V1_0_hidl::MemtrackStatus::TYPE_NOT_SUPPORTED:
+ [[fallthrough]];
+ default:
+ aidl_status =
+ ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ return;
+ }
+
+ _aidl_return->resize(records.size());
+ for (size_t i = 0; i < records.size(); i++) {
+ if (!translate(records[i], &(*_aidl_return)[i])) {
+ aidl_status = ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_SERVICE_SPECIFIC,
+ "Failed to convert HIDL MemtrackRecord to AIDL");
+ return;
+ }
+ }
+ });
+
+ // Check HIDL return
+ if (!ret.isOk()) {
+ const char* err_msg = "HIDL Memtrack::getMemory() failed";
+ aidl_status =
+ ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC, err_msg);
+ LOG(ERROR) << err_msg << ": " << ret.description();
+ }
+
+ return aidl_status;
+ }
+
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
+ "Memtrack HAL service not available");
+}
+
+ndk::ScopedAStatus MemtrackProxy::getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) {
+ if (!MemtrackProxy::CheckUid(AIBinder_getCallingUid())) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_SECURITY,
+ "Only AID_ROOT, AID_SYSTEM and AID_SHELL can request getGpuDeviceInfo()");
+ }
+
+ _aidl_return->clear();
+
+ if (memtrack_aidl_instance_ ||
+ (memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance())) {
+ return memtrack_aidl_instance_->getGpuDeviceInfo(_aidl_return);
+ }
+
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
+ "Memtrack HAL service not available");
+}
+
+} // namespace memtrack
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h b/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h
new file mode 100644
index 0000000..5ac1fbf
--- /dev/null
+++ b/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/memtrack/BnMemtrack.h>
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/IMemtrack.h>
+#include <aidl/android/hardware/memtrack/MemtrackRecord.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+#include <android/hardware/memtrack/1.0/IMemtrack.h>
+
+using ::android::sp;
+
+namespace V1_0_hidl = ::android::hardware::memtrack::V1_0;
+namespace V1_aidl = ::aidl::android::hardware::memtrack;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+__attribute__((warn_unused_result)) bool translate(const V1_0_hidl::MemtrackRecord& in,
+ V1_aidl::MemtrackRecord* out);
+
+class MemtrackProxy : public BnMemtrack {
+public:
+ MemtrackProxy();
+ ndk::ScopedAStatus getMemory(int pid, MemtrackType type,
+ std::vector<MemtrackRecord>* _aidl_return) override;
+ ndk::ScopedAStatus getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) override;
+
+private:
+ static sp<V1_0_hidl::IMemtrack> MemtrackHidlInstance();
+ static std::shared_ptr<V1_aidl::IMemtrack> MemtrackAidlInstance();
+ static bool CheckUid(uid_t calling_uid);
+ static bool CheckPid(pid_t calling_pid, pid_t request_pid);
+
+ sp<V1_0_hidl::IMemtrack> memtrack_hidl_instance_;
+ std::shared_ptr<V1_aidl::IMemtrack> memtrack_aidl_instance_;
+};
+
+} // namespace memtrack
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/services/memtrackproxy/test/Android.bp b/services/memtrackproxy/test/Android.bp
new file mode 100644
index 0000000..e5ad8dc
--- /dev/null
+++ b/services/memtrackproxy/test/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 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.
+
+cc_test {
+ name: "memtrackproxy_test",
+ srcs: [
+ "MemtrackProxyTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libmemtrackproxy",
+ "android.hardware.memtrack-V1-ndk_platform",
+ ],
+ test_suites: ["general-tests"],
+ require_root: true,
+}
diff --git a/services/memtrackproxy/test/MemtrackProxyTest.cpp b/services/memtrackproxy/test/MemtrackProxyTest.cpp
new file mode 100644
index 0000000..16dfba0
--- /dev/null
+++ b/services/memtrackproxy/test/MemtrackProxyTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/IMemtrack.h>
+#include <aidl/android/hardware/memtrack/MemtrackRecord.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+using aidl::android::hardware::memtrack::DeviceInfo;
+using aidl::android::hardware::memtrack::IMemtrack;
+using aidl::android::hardware::memtrack::MemtrackRecord;
+using aidl::android::hardware::memtrack::MemtrackType;
+
+class MemtrackProxyTest : public ::testing::Test {
+public:
+ virtual void SetUp() override {
+ const char* kMemtrackProxyService = "memtrack.proxy";
+ auto memtrackProxyBinder =
+ ndk::SpAIBinder(AServiceManager_waitForService(kMemtrackProxyService));
+ memtrack_proxy_ = IMemtrack::fromBinder(memtrackProxyBinder);
+ ASSERT_NE(memtrack_proxy_, nullptr);
+ }
+
+ std::shared_ptr<IMemtrack> memtrack_proxy_;
+};
+
+TEST_F(MemtrackProxyTest, GetMemoryForInvalidPid) {
+ int pid = -1;
+
+ for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+ std::vector<MemtrackRecord> records;
+
+ auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+ EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+ }
+}
+
+TEST_F(MemtrackProxyTest, GetMemoryForCallingPid) {
+ int pid = getpid();
+
+ for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+ std::vector<MemtrackRecord> records;
+
+ auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+ EXPECT_TRUE(status.isOk());
+ }
+}
+
+TEST_F(MemtrackProxyTest, GetMemoryForOtherPid) {
+ int pid = 1;
+
+ for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+ std::vector<MemtrackRecord> records;
+
+ auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+ // Test is run as root
+ EXPECT_TRUE(status.isOk());
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}