diff --git a/cobalt/demos/content/pre-app-recommendation-demo/pre-app-recommendation-demo.html b/cobalt/demos/content/pre-app-recommendation-demo/pre-app-recommendation-demo.html
new file mode 100644
index 000000000000..1fd5827d4a18
--- /dev/null
+++ b/cobalt/demos/content/pre-app-recommendation-demo/pre-app-recommendation-demo.html
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
diff --git a/starboard/linux/shared/BUILD.gn b/starboard/linux/shared/BUILD.gn
index ef9228453e72..07daff0131ba 100644
--- a/starboard/linux/shared/BUILD.gn
+++ b/starboard/linux/shared/BUILD.gn
@@ -81,6 +81,8 @@ static_library("starboard_platform_sources") {
"//starboard/linux/shared/platform_service.cc",
"//starboard/linux/shared/platform_service.h",
"//starboard/linux/shared/player_components_factory.cc",
+ "//starboard/linux/shared/pre_app_recommendation_service.cc",
+ "//starboard/linux/shared/pre_app_recommendation_service.h",
"//starboard/linux/shared/routes.cc",
"//starboard/linux/shared/routes.h",
"//starboard/linux/shared/soft_mic_platform_service.cc",
diff --git a/starboard/linux/shared/platform_service.cc b/starboard/linux/shared/platform_service.cc
index 5962884169b9..f6423ae7bc1e 100644
--- a/starboard/linux/shared/platform_service.cc
+++ b/starboard/linux/shared/platform_service.cc
@@ -21,6 +21,7 @@
#include "starboard/common/log.h"
#include "starboard/common/string.h"
#include "starboard/configuration.h"
+#include "starboard/linux/shared/pre_app_recommendation_service.h"
#include "starboard/linux/shared/soft_mic_platform_service.h"
#include "starboard/shared/starboard/application.h"
#if SB_IS(EVERGREEN_COMPATIBLE)
@@ -40,7 +41,8 @@ struct cmp_str {
};
std::map platform_service_registry = {
- {kSoftMicPlatformServiceName, GetSoftMicPlatformServiceApi}};
+ {kSoftMicPlatformServiceName, GetSoftMicPlatformServiceApi},
+ {kPreappRecommendationServiceName, GetPreappRecommendationServiceApi}};
bool Has(const char* name) {
// Checks whether Cobalt platform service registry has service name
diff --git a/starboard/linux/shared/pre_app_recommendation_service.cc b/starboard/linux/shared/pre_app_recommendation_service.cc
new file mode 100644
index 000000000000..9700663a18b3
--- /dev/null
+++ b/starboard/linux/shared/pre_app_recommendation_service.cc
@@ -0,0 +1,193 @@
+// Copyright 2024 The Cobalt Authors. All Rights Reserved.
+//
+// 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 "starboard/linux/shared/pre_app_recommendation_service.h"
+
+#include
+#include
+
+#include "starboard/common/log.h"
+#include "starboard/common/string.h"
+#include "starboard/configuration.h"
+#include "starboard/linux/shared/platform_service.h"
+#include "starboard/shared/starboard/application.h"
+
+// Omit namespace 'linux' due to symbol name conflict with macro 'linux'
+namespace starboard {
+namespace shared {
+namespace {
+typedef struct PreAppRecommendationsPlatformServiceImpl
+ : public PlatformServiceImpl {
+ // Define additional data field.
+ // variable_1, variable_2,...
+ PreAppRecommendationsPlatformServiceImpl(
+ void* context,
+ ReceiveMessageCallback receive_callback)
+ : PlatformServiceImpl(context, receive_callback) {}
+
+ // Default constructor.
+ PreAppRecommendationsPlatformServiceImpl() = default;
+
+} PreAppRecommendationsPlatformServiceImpl;
+
+// Use HTTP status code in response to YouTube application's method recommend
+// call.
+const char kSuccess[] = "200";
+const char kBadRequest[] = "400";
+// Methods supported
+const char kGetPartnerIdMethod[] = "getPartnerId";
+const char kRecommendMethod[] = "recommend";
+// Operations supported
+const char kUpsertOp[] = "upsert";
+const char kDeleteOp[] = "delete";
+// Configure partner Id
+const char kPartnerId[] = "dummy_partner_id";
+
+bool Has(const char* name) {
+ // Check if platform has service name.
+ return strcmp(name, kPreappRecommendationServiceName) == 0;
+}
+
+PlatformServiceImpl* Open(void* context,
+ ReceiveMessageCallback receive_callback) {
+ SB_DCHECK(context);
+ SB_LOG(INFO) << "Open() service created: "
+ << kPreappRecommendationServiceName;
+
+ return new PreAppRecommendationsPlatformServiceImpl(context,
+ receive_callback);
+}
+
+void Close(PlatformServiceImpl* service) {
+ // Function Close shouldn't manually delete PlatformServiceImpl pointer,
+ // because it is managed by unique_ptr in Platform Service.
+ SB_LOG(INFO)
+ << kPreappRecommendationServiceName
+ << " Perform actions before gracefully shutting down the service";
+}
+
+std::string extractJsonValue(const std::string& jsonLikeString,
+ const std::string& key) {
+ std::string result;
+ std::size_t start = jsonLikeString.find("\"" + key + "\"");
+
+ if (start != std::string::npos) {
+ start = jsonLikeString.find(":", start);
+ if (start != std::string::npos) {
+ start = jsonLikeString.find_first_of("\"", start);
+ if (start != std::string::npos) {
+ ++start; // Skip the opening quote
+ std::size_t end = jsonLikeString.find("\"", start);
+ if (end != std::string::npos) {
+ result = jsonLikeString.substr(start, end - start);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+void* Send(PlatformServiceImpl* service,
+ void* data,
+ uint64_t length,
+ uint64_t* output_length,
+ bool* invalid_state) {
+ SB_DCHECK(service);
+ SB_DCHECK(data);
+ SB_DCHECK(output_length);
+
+ char message[length + 1];
+ std::memcpy(message, data, length);
+ message[length] = '\0';
+
+ std::string response = "";
+
+ // TODO: Replace extractJsonValue function with a robust JSON parsing
+ // library for production use. The current implementation has limited
+ // capabilities that nested object isn't supported.
+ std::string method_name = extractJsonValue(message, "method");
+
+ if (method_name.length() == 0) {
+ SB_LOG(ERROR) << "Could not extract method value from the input JSON file:"
+ << message;
+ }
+
+ // When method_name = getPartnerId, platform returns partner Id to get
+ // authenticated by YouTube app.
+ if (method_name == kGetPartnerIdMethod) {
+ // Populate Partner Id in the response.
+ // Partner Id will be used by YouTube app to authenticate platforms and
+ // retrieve specified topics for a platform.
+ // auto partner_id = "\"dummmy_partner_id\"";
+ response = FormatString("{\"partner_id\": \"%s\"}", kPartnerId);
+ }
+
+ // When method_name = recommend, platform processes data according to
+ // operation field.
+ if (method_name == kRecommendMethod) {
+ std::string operation = extractJsonValue(message, "operation");
+ if (operation.length() != 0) {
+ // When operation = upsert, platform parses recs_response field and
+ // insert/update data in local storage.
+ if (operation == kUpsertOp) {
+ std::string recs_response = extractJsonValue(message, "recs_response");
+
+ if (recs_response.length() != 0) {
+ // Store recommendations data in the local storage, such as local
+ // database or local filesystem.
+ SB_LOG(INFO) << "operation = " << operation
+ << ", parse recs_response and store data locally";
+ } else {
+ SB_LOG(ERROR)
+ << "Could not extract recsResponse from the input JSON file:"
+ << message;
+ }
+ } else if (operation == kDeleteOp) {
+ // When operation = delete, platform delete YouTube data in local
+ // storage.
+ SB_LOG(INFO) << "operation = " << operation
+ << ", data in local storage is deleted";
+ }
+ // Populate response with 200 to indicate data is processed
+ // successfully.
+ response = kSuccess;
+ } else {
+ // Populate response with error code if data isn't processed successfully.
+ response = kBadRequest;
+ }
+ }
+
+ *output_length = response.length();
+ auto ptr = malloc(*output_length);
+ response.copy(reinterpret_cast(ptr), response.length());
+ return ptr;
+}
+
+const CobaltPlatformServiceApi kGetPreappRecommendationServiceApi = {
+ kPreappRecommendationServiceName,
+ 1, // API version that's implemented.
+ &Has,
+ &Open,
+ &Close,
+ &Send};
+
+} // namespace
+
+const void* GetPreappRecommendationServiceApi() {
+ return &kGetPreappRecommendationServiceApi;
+}
+
+} // namespace shared
+} // namespace starboard
diff --git a/starboard/linux/shared/pre_app_recommendation_service.h b/starboard/linux/shared/pre_app_recommendation_service.h
new file mode 100644
index 000000000000..33b17cb9e4e8
--- /dev/null
+++ b/starboard/linux/shared/pre_app_recommendation_service.h
@@ -0,0 +1,30 @@
+// Copyright 2024 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#ifndef STARBOARD_LINUX_SHARED_PRE_APP_RECOMMENDATION_SERVICE_H_
+#define STARBOARD_LINUX_SHARED_PRE_APP_RECOMMENDATION_SERVICE_H_
+
+// Omit namespace 'linux' due to symbol name conflict with macro 'linux'
+namespace starboard {
+namespace shared {
+
+const char* const kPreappRecommendationServiceName =
+ "com.google.youtube.tv.Recommendations";
+
+const void* GetPreappRecommendationServiceApi();
+
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_LINUX_SHARED_PRE_APP_RECOMMENDATION_SERVICE_H_