Commit 4e2f70a7 authored by Sam Goto's avatar Sam Goto Committed by Commit Bot

[sms] Handle incoming remote requests with SmsFetcher

Design-Doc: https://docs.google.com/document/d/1da8CjO71DlFbBzDcSosFRXTvDsKII_XMCohpZLQ_QbM/edit#
Bug: 1015645
Change-Id: I81d0d8971b313bed6829614de431b2943173a0e4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1895319
Commit-Queue: Sam Goto <goto@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarAlex Chau <alexchau@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713221}
parent 8483978f
......@@ -1584,6 +1584,8 @@ jumbo_static_library("browser") {
"sharing/sharing_sync_preference.h",
"sharing/sharing_utils.cc",
"sharing/sharing_utils.h",
"sharing/sms/sms_fetch_request_handler.cc",
"sharing/sms/sms_fetch_request_handler.h",
"sharing/sms/sms_flags.cc",
"sharing/sms/sms_flags.h",
"sharing/sms/sms_remote_fetcher.cc",
......@@ -2865,8 +2867,6 @@ jumbo_static_library("browser") {
"search_engines/template_url_service_factory_android.h",
"sharing/shared_clipboard/shared_clipboard_message_handler_android.cc",
"sharing/shared_clipboard/shared_clipboard_message_handler_android.h",
"sharing/sms/sms_fetch_request_handler.cc",
"sharing/sms/sms_fetch_request_handler.h",
"signin/identity_services_provider_android.cc",
"signin/signin_manager_android_factory.cc",
"signin/signin_manager_android_factory.h",
......
......@@ -97,7 +97,8 @@ class ClickToCallUtilsTest : public testing::Test {
/* gcm_driver= */ nullptr,
/* device_info_tracker= */ nullptr,
/* local_device_info_provider= */ nullptr,
/* sync_service */ nullptr);
/* sync_service */ nullptr,
/* sms_fetcher= */ nullptr);
}
base::test::ScopedFeatureList scoped_feature_list_;
......
......@@ -27,6 +27,7 @@ MockSharingService::MockSharingService()
/*gcm_driver=*/nullptr,
/*device_info_tracker=*/nullptr,
/*local_device_info_provider=*/nullptr,
/*sync_service*/ nullptr) {}
/*sync_service*/ nullptr,
/*sms_fetcher=*/nullptr) {}
MockSharingService::~MockSharingService() = default;
......@@ -81,7 +81,8 @@ class SharedClipboardUtilsTest : public testing::Test {
/* gcm_driver= */ nullptr,
/* device_info_tracker= */ nullptr,
/* local_device_info_provider= */ nullptr,
/* sync_service */ nullptr);
/* sync_service */ nullptr,
/* sms_fetcher= */ nullptr);
}
base::test::ScopedFeatureList scoped_feature_list_;
......
......@@ -60,7 +60,8 @@ SharingService::SharingService(
gcm::GCMDriver* gcm_driver,
syncer::DeviceInfoTracker* device_info_tracker,
syncer::LocalDeviceInfoProvider* local_device_info_provider,
syncer::SyncService* sync_service)
syncer::SyncService* sync_service,
content::SmsFetcher* sms_fetcher)
: sync_prefs_(std::move(sync_prefs)),
vapid_key_manager_(std::move(vapid_key_manager)),
sharing_device_registration_(std::move(sharing_device_registration)),
......@@ -105,7 +106,8 @@ SharingService::SharingService(
}
if (sharing_device_registration_->IsSmsFetcherSupported()) {
sms_fetch_request_handler_ = std::make_unique<SmsFetchRequestHandler>();
sms_fetch_request_handler_ =
std::make_unique<SmsFetchRequestHandler>(sms_fetcher);
fcm_handler_->AddSharingHandler(
chrome_browser_sharing::SharingMessage::kSmsFetchRequest,
sms_fetch_request_handler_.get());
......
......@@ -34,6 +34,10 @@
#include "chrome/browser/sharing/sharing_service_proxy_android.h"
#endif // defined(OS_ANDROID)
namespace content {
class SmsFetcher;
}
namespace gcm {
class GCMDriver;
} // namespace gcm
......@@ -83,7 +87,8 @@ class SharingService : public KeyedService,
gcm::GCMDriver* gcm_driver,
syncer::DeviceInfoTracker* device_info_tracker,
syncer::LocalDeviceInfoProvider* local_device_info_provider,
syncer::SyncService* sync_service);
syncer::SyncService* sync_service,
content::SmsFetcher* sms_fetcher);
~SharingService() override;
// Returns the device matching |guid|, or nullptr if no match was found.
......
......@@ -27,6 +27,7 @@
#include "components/sync_device_info/device_info_sync_service.h"
#include "components/sync_device_info/local_device_info_provider.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/sms_fetcher.h"
namespace {
constexpr char kServiceName[] = "SharingService";
......@@ -98,11 +99,14 @@ KeyedService* SharingServiceFactory::BuildServiceInstanceFor(
std::make_unique<SharingMessageSender>(fcm_sender.get(), sync_prefs.get(),
local_device_info_provider);
content::SmsFetcher* sms_fetcher = content::SmsFetcher::Get(context);
return new SharingService(
profile, std::move(sync_prefs), std::move(vapid_key_manager),
std::move(sharing_device_registration), std::move(fcm_sender),
std::move(fcm_handler), std::move(sharing_message_sender), gcm_driver,
device_info_tracker, local_device_info_provider, sync_service);
device_info_tracker, local_device_info_provider, sync_service,
sms_fetcher);
}
content::BrowserContext* SharingServiceFactory::GetBrowserContextToUse(
......
......@@ -223,7 +223,8 @@ class SharingServiceTest : public testing::Test {
base::WrapUnique(sharing_message_sender_), nullptr,
fake_device_info_sync_service.GetDeviceInfoTracker(),
fake_device_info_sync_service.GetLocalDeviceInfoProvider(),
&test_sync_service_);
&test_sync_service_,
/* sms_fetcher= */ nullptr);
}
task_environment_.RunUntilIdle();
return sharing_service_.get();
......
......@@ -6,8 +6,12 @@
#include "base/logging.h"
#include "components/sync/protocol/sharing_sms_fetch_message.pb.h"
#include "content/public/browser/sms_fetcher.h"
#include "url/gurl.h"
#include "url/origin.h"
SmsFetchRequestHandler::SmsFetchRequestHandler() = default;
SmsFetchRequestHandler::SmsFetchRequestHandler(content::SmsFetcher* fetcher)
: fetcher_(fetcher) {}
SmsFetchRequestHandler::~SmsFetchRequestHandler() = default;
......@@ -15,6 +19,41 @@ void SmsFetchRequestHandler::OnMessage(
chrome_browser_sharing::SharingMessage message,
SharingMessageHandler::DoneCallback done_callback) {
DCHECK(message.has_sms_fetch_request());
// TODO(crbug.com/1015645): implementation left pending deliberately.
NOTIMPLEMENTED();
auto origin = url::Origin::Create(GURL(message.sms_fetch_request().origin()));
auto request = std::make_unique<Request>(this, fetcher_, origin,
std::move(done_callback));
requests_.insert(std::move(request));
}
void SmsFetchRequestHandler::RemoveRequest(Request* request) {
requests_.erase(request);
}
SmsFetchRequestHandler::Request::Request(
SmsFetchRequestHandler* handler,
content::SmsFetcher* fetcher,
const url::Origin& origin,
SharingMessageHandler::DoneCallback respond_callback)
: handler_(handler),
fetcher_(fetcher),
origin_(origin),
respond_callback_(std::move(respond_callback)) {
fetcher_->Subscribe(origin_, this);
}
SmsFetchRequestHandler::Request::~Request() {
fetcher_->Unsubscribe(origin_, this);
}
void SmsFetchRequestHandler::Request::OnReceive(
const std::string& one_time_code,
const std::string& sms) {
auto response = std::make_unique<chrome_browser_sharing::ResponseMessage>();
response->mutable_sms_fetch_response()->set_sms(sms);
response->mutable_sms_fetch_response()->set_one_time_code(one_time_code);
std::move(respond_callback_).Run(std::move(response));
handler_->RemoveRequest(this);
}
......@@ -5,14 +5,23 @@
#ifndef CHROME_BROWSER_SHARING_SMS_SMS_FETCH_REQUEST_HANDLER_H_
#define CHROME_BROWSER_SHARING_SMS_SMS_FETCH_REQUEST_HANDLER_H_
#include "base/bind.h"
#include "base/containers/flat_set.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/macros.h"
#include "chrome/browser/sharing/sharing_message_handler.h"
#include "components/sync/protocol/sharing_message.pb.h"
#include "content/public/browser/sms_fetcher.h"
#include "url/origin.h"
namespace content {
class SmsFetcher;
}
// Handles incoming messages for the sms fetcher feature.
class SmsFetchRequestHandler : public SharingMessageHandler {
public:
SmsFetchRequestHandler();
explicit SmsFetchRequestHandler(content::SmsFetcher* fetcher);
~SmsFetchRequestHandler() override;
// SharingMessageHandler
......@@ -20,6 +29,39 @@ class SmsFetchRequestHandler : public SharingMessageHandler {
SharingMessageHandler::DoneCallback done_callback) override;
private:
// Request represents an incoming request from a remote SmsService.
// It manages subscribing and unsubscribing for SMSes in SmsFetcher and
// responding to the callback.
// It also lets SmsFetchRequestHandler know when the request is fulfilled
// to allow its memory to be freed.
class Request : public content::SmsFetcher::Subscriber {
public:
Request(SmsFetchRequestHandler* handler,
content::SmsFetcher* fetcher,
const url::Origin& origin,
SharingMessageHandler::DoneCallback respond_callback);
~Request() override;
void OnReceive(const std::string& one_time_code,
const std::string& sms) override;
private:
SmsFetchRequestHandler* handler_;
content::SmsFetcher* fetcher_;
const url::Origin& origin_;
SharingMessageHandler::DoneCallback respond_callback_;
DISALLOW_COPY_AND_ASSIGN(Request);
};
void RemoveRequest(Request* Request);
// |fetcher_| is safe because it is owned by BrowserContext, which also
// owns (transitively, via SharingService) this class.
content::SmsFetcher* fetcher_;
base::flat_set<std::unique_ptr<Request>, base::UniquePtrComparator> requests_;
base::WeakPtrFactory<SmsFetchRequestHandler> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(SmsFetchRequestHandler);
};
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sharing/sms/sms_fetch_request_handler.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "chrome/browser/sharing/sharing_message_handler.h"
#include "components/sync/protocol/sharing_message.pb.h"
#include "content/public/browser/sms_fetcher.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
using base::BindLambdaForTesting;
using chrome_browser_sharing::ResponseMessage;
using chrome_browser_sharing::SharingMessage;
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;
namespace {
class MockSmsFetcher : public content::SmsFetcher {
public:
MockSmsFetcher() = default;
~MockSmsFetcher() = default;
MOCK_METHOD2(Subscribe,
void(const url::Origin& origin, Subscriber* subscriber));
MOCK_METHOD2(Unsubscribe,
void(const url::Origin& origin, Subscriber* subscriber));
MOCK_METHOD0(HasSubscribers, bool());
private:
DISALLOW_COPY_AND_ASSIGN(MockSmsFetcher);
};
SharingMessage CreateRequest(const std::string& origin) {
SharingMessage message;
message.mutable_sms_fetch_request()->set_origin(origin);
return message;
}
} // namespace
TEST(SmsFetchRequestHandlerTest, Basic) {
base::test::SingleThreadTaskEnvironment task_environment;
StrictMock<MockSmsFetcher> fetcher;
SmsFetchRequestHandler handler(&fetcher);
SharingMessage message = CreateRequest("https://a.com");
base::RunLoop loop;
content::SmsFetcher::Subscriber* subscriber;
EXPECT_CALL(fetcher, Subscribe(_, _)).WillOnce(SaveArg<1>(&subscriber));
EXPECT_CALL(fetcher, Unsubscribe(_, _));
handler.OnMessage(
message,
BindLambdaForTesting([&loop](std::unique_ptr<ResponseMessage> response) {
EXPECT_TRUE(response->has_sms_fetch_response());
EXPECT_EQ("123", response->sms_fetch_response().one_time_code());
EXPECT_EQ("hello", response->sms_fetch_response().sms());
loop.Quit();
}));
subscriber->OnReceive("123", "hello");
loop.Run();
}
TEST(SmsFetchRequestHandlerTest, OutOfOrder) {
base::test::SingleThreadTaskEnvironment task_environment;
StrictMock<MockSmsFetcher> fetcher;
SmsFetchRequestHandler handler(&fetcher);
SharingMessage message = CreateRequest("https://a.com");
base::RunLoop loop1;
content::SmsFetcher::Subscriber* request1;
EXPECT_CALL(fetcher, Subscribe(_, _)).WillOnce(SaveArg<1>(&request1));
EXPECT_CALL(fetcher, Unsubscribe(_, _)).Times(2);
handler.OnMessage(
message,
BindLambdaForTesting([&loop1](std::unique_ptr<ResponseMessage> response) {
EXPECT_TRUE(response->has_sms_fetch_response());
EXPECT_EQ("first", response->sms_fetch_response().sms());
loop1.Quit();
}));
base::RunLoop loop2;
content::SmsFetcher::Subscriber* request2;
EXPECT_CALL(fetcher, Subscribe(_, _)).WillOnce(SaveArg<1>(&request2));
handler.OnMessage(
message,
BindLambdaForTesting([&loop2](std::unique_ptr<ResponseMessage> response) {
EXPECT_TRUE(response->has_sms_fetch_response());
EXPECT_EQ("second", response->sms_fetch_response().sms());
loop2.Quit();
}));
request2->OnReceive("2", "second");
loop2.Run();
request1->OnReceive("1", "first");
loop1.Run();
}
TEST(SmsFetchRequestHandlerTest, HangingRequestUnsubscribedUponDestruction) {
base::test::SingleThreadTaskEnvironment task_environment;
StrictMock<MockSmsFetcher> fetcher;
SmsFetchRequestHandler handler(&fetcher);
SharingMessage message = CreateRequest("https://a.com");
content::SmsFetcher::Subscriber* subscriber;
EXPECT_CALL(fetcher, Subscribe(_, _)).WillOnce(SaveArg<1>(&subscriber));
// Expects Unsubscribe to be called when SmsFetchRequestHandler goes out of
// scope.
EXPECT_CALL(fetcher, Unsubscribe(_, _));
// Leaves the request deliberately hanging without a response to assert
// that it gets cleaned up.
handler.OnMessage(
message,
BindLambdaForTesting([&](std::unique_ptr<ResponseMessage> response) {}));
}
......@@ -11,12 +11,18 @@
#include "chrome/browser/sharing/sms/sms_flags.h"
#include "components/sync_device_info/device_info.h"
#include "content/public/browser/browser_context.h"
#include "url/origin.h"
namespace {
const uint32_t kDefaultTimeoutSeconds = 60;
} // namespace
void FetchRemoteSms(
content::BrowserContext* context,
const url::Origin& origin,
base::OnceCallback<void(base::Optional<std::string>)> callback) {
if (!base::FeatureList::IsEnabled(kSmsFetchRequestHandler)) {
std::move(callback).Run(base::nullopt);
return;
}
......@@ -25,21 +31,39 @@ void FetchRemoteSms(
SharingService::SharingDeviceList devices =
sharing_service->GetDeviceCandidates(
sync_pb::SharingSpecificFields::SMS_FETCHER);
for (const std::unique_ptr<syncer::DeviceInfo>& info : devices) {
chrome_browser_sharing::SharingMessage sharing_message;
if (devices.empty()) {
// No devices available to call.
std::move(callback).Run(base::nullopt);
return;
}
// Sends to the first device that has the capability enabled.
// TODO(crbug.com/1015645): figure out the routing strategy, possibly
// requiring UX to allow the users to specify the device.
const std::unique_ptr<syncer::DeviceInfo>& device = devices.front();
chrome_browser_sharing::SharingMessage request;
request.mutable_sms_fetch_request()->set_origin(origin.Serialize());
sharing_service->SendMessageToDevice(
info->guid(), kSendMessageTimeout, std::move(sharing_message),
device->guid(), base::TimeDelta::FromSeconds(kDefaultTimeoutSeconds),
std::move(request),
base::BindOnce(
[](SharingSendMessageResult result,
[](base::OnceCallback<void(base::Optional<std::string>)> callback,
SharingSendMessageResult result,
std::unique_ptr<chrome_browser_sharing::ResponseMessage>
response) {
// TODO(crbug.com/1015645): implementation pending.
NOTIMPLEMENTED();
}));
// Sends to the first device that has the capability enabled.
// TODO(crbug.com/1015645): figure out the routing strategy.
break;
if (result != SharingSendMessageResult::kSuccessful) {
std::move(callback).Run(base::nullopt);
return;
}
DCHECK(response);
DCHECK(response->has_sms_fetch_response());
std::move(callback).Run(response->sms_fetch_response().sms());
},
std::move(callback)));
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sharing/sms/sms_remote_fetcher.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/sharing/mock_sharing_service.h"
#include "chrome/browser/sharing/sharing_service.h"
#include "chrome/browser/sharing/sharing_service_factory.h"
#include "chrome/browser/sharing/sms/sms_flags.h"
#include "chrome/test/base/testing_profile.h"
#include "components/sync/protocol/sharing_message.pb.h"
#include "content/public/browser/sms_fetcher.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
using base::BindLambdaForTesting;
using chrome_browser_sharing::ResponseMessage;
using chrome_browser_sharing::SharingMessage;
using ::testing::_;
using ::testing::ByMove;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::SaveArg;
MockSharingService* CreateSharingService(content::BrowserContext* context) {
return static_cast<MockSharingService*>(
SharingServiceFactory::GetInstance()->SetTestingFactoryAndUse(
context, base::BindRepeating([](content::BrowserContext* context) {
return static_cast<std::unique_ptr<KeyedService>>(
std::make_unique<MockSharingService>());
})));
}
url::Origin GetOriginForURL(const std::string url) {
return url::Origin::Create(GURL(url));
}
std::unique_ptr<syncer::DeviceInfo> CreateDevice() {
return std::make_unique<syncer::DeviceInfo>(
"guid1", "name", "chrome_version", "user_agent",
sync_pb::SyncEnums_DeviceType_TYPE_PHONE, "device_id",
base::SysInfo::HardwareInfo(),
/*last_updated_timestamp=*/base::Time::Now(),
/*send_tab_to_self_receiving_enabled=*/false,
/*sharing_info=*/base::nullopt);
}
TEST(SmsRemoteFetcherTest, DisabledByDefault) {
content::BrowserTaskEnvironment task_environment;
TestingProfile profile;
base::RunLoop loop;
FetchRemoteSms(
&profile, GetOriginForURL("a.com"),
BindLambdaForTesting([&loop](base::Optional<std::string> result) {
ASSERT_FALSE(result);
loop.Quit();
}));
loop.Run();
}
TEST(SmsRemoteFetcherTest, NoDevicesAvailable) {
base::test::ScopedFeatureList flags;
content::BrowserTaskEnvironment task_environment;
TestingProfile profile;
flags.InitAndEnableFeature(kSmsFetchRequestHandler);
MockSharingService* service = CreateSharingService(&profile);
std::vector<std::unique_ptr<syncer::DeviceInfo>> devices;
EXPECT_CALL(*service, GetDeviceCandidates(_))
.WillOnce(Return(ByMove(std::move(devices))));
base::RunLoop loop;
FetchRemoteSms(
&profile, GetOriginForURL("a.com"),
BindLambdaForTesting([&loop](base::Optional<std::string> result) {
ASSERT_FALSE(result);
loop.Quit();
}));
loop.Run();
}
TEST(SmsRemoteFetcherTest, OneDevice) {
base::test::ScopedFeatureList flags;
content::BrowserTaskEnvironment task_environment;
TestingProfile profile;
flags.InitAndEnableFeature(kSmsFetchRequestHandler);
MockSharingService* service = CreateSharingService(&profile);
std::vector<std::unique_ptr<syncer::DeviceInfo>> devices;
devices.push_back(CreateDevice());
EXPECT_CALL(*service, GetDeviceCandidates(_))
.WillOnce(Return(ByMove(std::move(devices))));
base::RunLoop loop;
EXPECT_CALL(*service, SendMessageToDevice(_, _, _, _))
.WillOnce(Invoke([&](const std::string& device_guid,
base::TimeDelta response_timeout,
chrome_browser_sharing::SharingMessage message,
SharingMessageSender::ResponseCallback callback) {
auto response = std::make_unique<ResponseMessage>();
response->mutable_sms_fetch_response()->set_sms("hello");
std::move(callback).Run(SharingSendMessageResult::kSuccessful,
std::move(response));
}));
FetchRemoteSms(
&profile, GetOriginForURL("a.com"),
BindLambdaForTesting([&loop](base::Optional<std::string> result) {
ASSERT_TRUE(result);
ASSERT_EQ("hello", result);
loop.Quit();
}));
loop.Run();
}
TEST(SmsRemoteFetcherTest, OneDeviceTimesOut) {
base::test::ScopedFeatureList flags;
content::BrowserTaskEnvironment task_environment;
TestingProfile profile;
flags.InitAndEnableFeature(kSmsFetchRequestHandler);
MockSharingService* service = CreateSharingService(&profile);
std::vector<std::unique_ptr<syncer::DeviceInfo>> devices;
devices.push_back(CreateDevice());
EXPECT_CALL(*service, GetDeviceCandidates(_))
.WillOnce(Return(ByMove(std::move(devices))));
base::RunLoop loop;
EXPECT_CALL(*service, SendMessageToDevice(_, _, _, _))
.WillOnce(Invoke([&](const std::string& device_guid,
base::TimeDelta response_timeout,
chrome_browser_sharing::SharingMessage message,
SharingMessageSender::ResponseCallback callback) {
std::move(callback).Run(SharingSendMessageResult::kAckTimeout,
std::make_unique<ResponseMessage>());
}));
FetchRemoteSms(
&profile, GetOriginForURL("a.com"),
BindLambdaForTesting([&loop](base::Optional<std::string> result) {
ASSERT_FALSE(result);
loop.Quit();
}));
loop.Run();
}
......@@ -4017,6 +4017,8 @@ test("unit_tests") {
"../browser/sharing/shared_clipboard/shared_clipboard_test_base.h",
"../browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc",
"../browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc",
"../browser/sharing/sms/sms_fetch_request_handler_unittest.cc",
"../browser/sharing/sms/sms_remote_fetcher_unittest.cc",
"../browser/ui/autofill/payments/local_card_migration_bubble_controller_impl_unittest.cc",
"../browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc",
"../browser/ui/bluetooth/bluetooth_chooser_controller_unittest.cc",
......
......@@ -10,7 +10,19 @@ package chrome_browser_sharing;
option optimize_for = LITE_RUNTIME;
// Request message to fetch a SMS from a remote device.
message SmsFetchRequest {}
message SmsFetchRequest {
// The origin that is requesting the SMS. Remote devices use it to match
// against the metadata contained in the received SMS.
// required
string origin = 1;
}
// Response message to fetch a SMS from a remote device.
message SmsFetchResponse {}
message SmsFetchResponse {
// The full contents of the received SMS.
// required
string sms = 1;
// The parsed one time code of the received SMS.
// required
string one_time_code = 2;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment