| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/optimization_guide/android/optimization_guide_bridge.h" |
| |
| #include <jni.h> |
| |
| #include <string> |
| #include <typeinfo> |
| #include <vector> |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_array.h" |
| #include "base/android/jni_string.h" |
| #include "chrome/browser/optimization_guide/chrome_hints_manager.h" |
| #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h" |
| #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" |
| #include "components/optimization_guide/core/hints/hint_cache.h" |
| #include "components/optimization_guide/core/hints/optimization_guide_decider.h" |
| #include "components/optimization_guide/core/hints/optimization_guide_store.h" |
| #include "components/optimization_guide/core/hints/push_notification_manager.h" |
| #include "components/optimization_guide/proto/hints.pb.h" |
| #include "url/android/gurl_android.h" |
| #include "url/gurl.h" |
| |
| // Must come after all headers that specialize FromJniType() / ToJniType(). |
| #include "chrome/browser/optimization_guide/android/jni_headers/OptimizationGuideBridge_jni.h" |
| |
| using base::android::AttachCurrentThread; |
| using base::android::ConvertJavaStringToUTF8; |
| using base::android::JavaArrayOfByteArrayToBytesVector; |
| using base::android::JavaByteArrayToString; |
| using base::android::JavaIntArrayToIntVector; |
| using base::android::JavaParamRef; |
| using base::android::JavaRef; |
| using base::android::ScopedJavaGlobalRef; |
| using base::android::ScopedJavaLocalRef; |
| using base::android::ToJavaByteArray; |
| |
| namespace optimization_guide { |
| namespace android { |
| |
| namespace { |
| |
| ScopedJavaLocalRef<jbyteArray> ToJavaSerializedAnyMetadata( |
| JNIEnv* env, |
| const optimization_guide::OptimizationMetadata& optimization_metadata) { |
| // We do not expect the following metadatas to be populated for optimization |
| // types getting called from Java. |
| DCHECK(!optimization_metadata.loading_predictor_metadata()); |
| |
| if (optimization_metadata.any_metadata()) { |
| std::string serialized; |
| optimization_metadata.any_metadata().value().SerializeToString(&serialized); |
| return ToJavaByteArray(env, serialized); |
| } |
| return nullptr; |
| } |
| |
| void OnOptimizationGuideDecision( |
| const JavaRef<jobject>& java_callback, |
| optimization_guide::OptimizationGuideDecision decision, |
| const optimization_guide::OptimizationMetadata& metadata) { |
| JNIEnv* env = AttachCurrentThread(); |
| Java_OptimizationGuideBridge_onOptimizationGuideDecision( |
| env, java_callback, static_cast<int>(decision), |
| ToJavaSerializedAnyMetadata(env, metadata)); |
| } |
| |
| base::flat_set<proto::OptimizationType> JavaIntArrayToOptTypesSet( |
| JNIEnv* env, |
| const JavaParamRef<jintArray>& joptimization_types) { |
| std::vector<int> joptimization_types_vector; |
| JavaIntArrayToIntVector(env, joptimization_types, |
| &joptimization_types_vector); |
| base::flat_set<optimization_guide::proto::OptimizationType> |
| optimization_types; |
| for (const int joptimization_type : joptimization_types_vector) { |
| // Handles parsing of reserved tag numbers. |
| if (proto::OptimizationType_IsValid(joptimization_type)) { |
| optimization_types.insert( |
| static_cast<proto::OptimizationType>(joptimization_type)); |
| } |
| } |
| return optimization_types; |
| } |
| |
| void OnOnDemandOptimizationGuideDecision( |
| const JavaRef<jobject>& java_callback, |
| const GURL& url, |
| const base::flat_map<proto::OptimizationType, |
| OptimizationGuideDecisionWithMetadata>& metadata) { |
| JNIEnv* env = AttachCurrentThread(); |
| for (const auto& type_and_decision : metadata) { |
| Java_OptimizationGuideBridge_onOnDemandOptimizationGuideDecision( |
| env, java_callback, url::GURLAndroid::FromNativeGURL(env, url), |
| static_cast<int>(type_and_decision.first), |
| static_cast<int>(type_and_decision.second.decision), |
| ToJavaSerializedAnyMetadata(env, type_and_decision.second.metadata)); |
| } |
| } |
| |
| } // namespace |
| |
| // static |
| std::vector<proto::HintNotificationPayload> |
| OptimizationGuideBridge::GetCachedNotifications( |
| proto::OptimizationType optimization_type) { |
| JNIEnv* env = AttachCurrentThread(); |
| const JavaRef<jobjectArray>& j_encoded_notifications = |
| Java_OptimizationGuideBridge_getEncodedPushNotifications( |
| env, static_cast<int>(optimization_type)); |
| if (!j_encoded_notifications) |
| return {}; |
| |
| std::vector<std::vector<uint8_t>> encoded_notifications; |
| JavaArrayOfByteArrayToBytesVector(env, j_encoded_notifications, |
| &encoded_notifications); |
| |
| std::vector<proto::HintNotificationPayload> notifications; |
| for (const auto& encoded_notification : encoded_notifications) { |
| proto::HintNotificationPayload notification; |
| if (notification.ParseFromString(std::string(encoded_notification.begin(), |
| encoded_notification.end()))) { |
| notifications.push_back(notification); |
| } |
| } |
| |
| return notifications; |
| } |
| |
| // static |
| base::flat_set<proto::OptimizationType> |
| OptimizationGuideBridge::GetOptTypesWithPushNotifications() { |
| JNIEnv* env = AttachCurrentThread(); |
| std::vector<int> cached_int_types; |
| JavaIntArrayToIntVector( |
| env, Java_OptimizationGuideBridge_getOptTypesWithPushNotifications(env), |
| &cached_int_types); |
| |
| base::flat_set<proto::OptimizationType> cached_types; |
| for (int int_type : cached_int_types) { |
| // Handles parsing of reserved tag numbers. |
| if (proto::OptimizationType_IsValid(int_type)) { |
| cached_types.insert(static_cast<proto::OptimizationType>(int_type)); |
| } |
| } |
| return cached_types; |
| } |
| |
| // static |
| base::flat_set<proto::OptimizationType> |
| OptimizationGuideBridge::GetOptTypesThatOverflowedPushNotifications() { |
| JNIEnv* env = AttachCurrentThread(); |
| std::vector<int> overflowed_int_types; |
| JavaIntArrayToIntVector( |
| env, |
| Java_OptimizationGuideBridge_getOptTypesThatOverflowedPushNotifications( |
| env), |
| &overflowed_int_types); |
| |
| base::flat_set<proto::OptimizationType> overflowed_types; |
| for (int int_type : overflowed_int_types) { |
| // Handles parsing of reserved tag numbers. |
| if (proto::OptimizationType_IsValid(int_type)) { |
| overflowed_types.insert(static_cast<proto::OptimizationType>(int_type)); |
| } |
| } |
| return overflowed_types; |
| } |
| |
| // static |
| void OptimizationGuideBridge::ClearCacheForOptimizationType( |
| proto::OptimizationType opt_type) { |
| JNIEnv* env = AttachCurrentThread(); |
| Java_OptimizationGuideBridge_clearCachedPushNotifications( |
| env, static_cast<int>(opt_type)); |
| } |
| |
| // static |
| void OptimizationGuideBridge::OnNotificationNotHandledByNative( |
| proto::HintNotificationPayload notification) { |
| std::string encoded_notification; |
| if (!notification.SerializeToString(&encoded_notification)) |
| return; |
| |
| JNIEnv* env = AttachCurrentThread(); |
| Java_OptimizationGuideBridge_onPushNotificationNotHandledByNative( |
| env, ToJavaByteArray(env, encoded_notification)); |
| } |
| |
| void OptimizationGuideBridge::OnDeferredStartup(JNIEnv* env) { |
| optimization_guide_keyed_service_->GetHintsManager()->OnDeferredStartup(); |
| } |
| |
| OptimizationGuideBridge::OptimizationGuideBridge( |
| OptimizationGuideKeyedService* optimization_guide_keyed_service) |
| : optimization_guide_keyed_service_(optimization_guide_keyed_service) { |
| DCHECK(optimization_guide_keyed_service_); |
| } |
| |
| OptimizationGuideBridge::~OptimizationGuideBridge() = default; |
| |
| ScopedJavaLocalRef<jobject> OptimizationGuideBridge::GetJavaObject() { |
| JNIEnv* env = AttachCurrentThread(); |
| if (!java_ref_) { |
| java_ref_.Reset(Java_OptimizationGuideBridge_Constructor( |
| env, reinterpret_cast<intptr_t>(this))); |
| } |
| return ScopedJavaLocalRef<jobject>(java_ref_); |
| } |
| |
| void OptimizationGuideBridge::RegisterOptimizationTypes( |
| JNIEnv* env, |
| const JavaParamRef<jintArray>& joptimization_types) { |
| base::flat_set<proto::OptimizationType> opt_types_set = |
| JavaIntArrayToOptTypesSet(env, joptimization_types); |
| optimization_guide_keyed_service_->RegisterOptimizationTypes( |
| {opt_types_set.begin(), opt_types_set.end()}); |
| } |
| |
| void OptimizationGuideBridge::CanApplyOptimization( |
| JNIEnv* env, |
| GURL& url, |
| jint optimization_type, |
| const JavaParamRef<jobject>& java_callback) { |
| optimization_guide_keyed_service_->CanApplyOptimization( |
| url, |
| static_cast<optimization_guide::proto::OptimizationType>( |
| optimization_type), |
| base::BindOnce(&OnOptimizationGuideDecision, |
| ScopedJavaGlobalRef<jobject>(env, java_callback))); |
| } |
| |
| base::android::ScopedJavaLocalRef<jobject> |
| OptimizationGuideBridge::CanApplyOptimizationSync(JNIEnv* env, |
| GURL& url, |
| jint optimization_type) { |
| optimization_guide::OptimizationMetadata metadata; |
| |
| auto decision = optimization_guide_keyed_service_->CanApplyOptimization( |
| url, |
| static_cast<optimization_guide::proto::OptimizationType>( |
| optimization_type), |
| /* optimization_metadata = */ &metadata); |
| |
| return Java_OptimizationGuideBridge_createDecisionWithMetadata( |
| env, static_cast<int>(decision), |
| ToJavaSerializedAnyMetadata(env, metadata)); |
| } |
| |
| void OptimizationGuideBridge::CanApplyOptimizationOnDemand( |
| JNIEnv* env, |
| std::vector<GURL>& urls, |
| const JavaParamRef<jintArray>& optimization_types, |
| jint request_context, |
| const JavaParamRef<jobject>& java_callback, |
| jni_zero::ByteArrayView& request_context_metadata_serialized) { |
| proto::RequestContextMetadata request_context_metadata_deserialized; |
| request_context_metadata_deserialized.ParseFromArray( |
| request_context_metadata_serialized.data(), |
| request_context_metadata_serialized.size()); |
| std::optional<optimization_guide::proto::RequestContextMetadata> |
| request_context_metadata = |
| request_context_metadata_serialized.empty() |
| ? std::nullopt |
| : std::make_optional(request_context_metadata_deserialized); |
| |
| optimization_guide_keyed_service_->CanApplyOptimizationOnDemand( |
| urls, JavaIntArrayToOptTypesSet(env, optimization_types), |
| static_cast<proto::RequestContext>(request_context), |
| base::BindRepeating(&OnOnDemandOptimizationGuideDecision, |
| ScopedJavaGlobalRef<jobject>(env, java_callback)), |
| request_context_metadata); |
| } |
| |
| void OptimizationGuideBridge::OnNewPushNotification( |
| JNIEnv* env, |
| const JavaRef<jbyteArray>& j_encoded_notification) { |
| if (!j_encoded_notification) |
| return; |
| |
| proto::HintNotificationPayload notification; |
| std::string encoded_notification; |
| JavaByteArrayToString(env, j_encoded_notification, &encoded_notification); |
| if (!notification.ParseFromString(encoded_notification)) |
| return; |
| |
| if (!notification.has_hint_key()) |
| return; |
| |
| optimization_guide::ChromeHintsManager* hints_manager = |
| optimization_guide_keyed_service_->GetHintsManager(); |
| PushNotificationManager* push_manager = |
| hints_manager ? hints_manager->push_notification_manager() : nullptr; |
| if (!push_manager) |
| return; |
| |
| push_manager->OnNewPushNotification(notification); |
| } |
| |
| } // namespace android |
| } // namespace optimization_guide |