| // Copyright 2026 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/autofill/android/entity_instance_android.h" |
| |
| #include <optional> |
| |
| #include "base/android/jni_array.h" |
| #include "base/android/jni_string.h" |
| #include "base/notreached.h" |
| #include "components/autofill/android/main_autofill_jni_headers/EntityInstance_jni.h" |
| #include "components/autofill/core/browser/data_model/autofill_ai/entity_instance.h" |
| #include "components/autofill/core/browser/permissions/autofill_ai/autofill_ai_permission_util.h" |
| |
| namespace autofill { |
| |
| base::android::ScopedJavaLocalRef<jobject> EntityInstanceAndroid::Create( |
| JNIEnv* env, |
| const EntityInstanceAndroid& entity_instance, |
| bool requires_reauth_to_see, |
| bool is_masked_server_entity) { |
| return Java_EntityInstance_Constructor( |
| env, static_cast<int>(entity_instance.record_type), |
| entity_instance.entity_type, entity_instance.attribute_instances, |
| entity_instance.nickname, entity_instance.metadata, |
| requires_reauth_to_see, is_masked_server_entity); |
| } |
| |
| EntityInstanceAndroid EntityInstanceAndroid::FromJavaEntityInstance( |
| JNIEnv* env, |
| const base::android::JavaRef<jobject>& j_entity_instance) { |
| EntityInstance::RecordType record_type = |
| static_cast<EntityInstance::RecordType>( |
| Java_EntityInstance_getRecordType(env, j_entity_instance)); |
| EntityTypeAndroid entity_type = |
| Java_EntityInstance_getEntityType(env, j_entity_instance); |
| |
| std::vector<AttributeInstanceAndroid> attributes = |
| Java_EntityInstance_getAttributes(env, j_entity_instance); |
| |
| std::string nickname = |
| Java_EntityInstance_getNickname(env, j_entity_instance); |
| |
| EntityMetadataAndroid metadata = |
| EntityMetadataAndroid::FromJavaEntityMetadata( |
| env, Java_EntityInstance_getMetadata(env, j_entity_instance)); |
| |
| bool requires_reauth_to_see = |
| Java_EntityInstance_requiresReauthToSee(env, j_entity_instance); |
| |
| bool is_masked_server_entity = |
| Java_EntityInstance_isMaskedServerEntity(env, j_entity_instance); |
| |
| return EntityInstanceAndroid(std::move(entity_type), record_type, |
| std::move(attributes), std::move(nickname), |
| std::move(metadata), requires_reauth_to_see, |
| is_masked_server_entity); |
| } |
| |
| EntityInstanceAndroid::EntityInstanceAndroid( |
| const EntityInstance& entity_instance, |
| bool is_enabled, |
| bool is_eligible_for_wallet_storage, |
| bool requires_reauth_to_see) |
| : entity_type(entity_instance.type(), |
| is_enabled, |
| is_eligible_for_wallet_storage, |
| GetWalletPassType(entity_instance.type(), |
| entity_instance.record_type()) == |
| EntityInstance::WalletPassType::kPrivate), |
| record_type(entity_instance.record_type()), |
| nickname(entity_instance.nickname()), |
| metadata(entity_instance.guid().value(), |
| entity_instance.date_modified(), |
| entity_instance.use_count(), |
| entity_instance.use_date()), |
| requires_reauth_to_see(requires_reauth_to_see), |
| is_masked_server_entity(entity_instance.IsMaskedEntity() && |
| entity_instance.IsServerInstance()) { |
| for (const auto& attr : entity_instance.attributes()) { |
| attribute_instances.emplace_back(attr); |
| } |
| } |
| |
| EntityInstanceAndroid::EntityInstanceAndroid( |
| EntityTypeAndroid entity_type, |
| EntityInstance::RecordType record_type, |
| std::vector<AttributeInstanceAndroid> attribute_instances, |
| std::string nickname, |
| EntityMetadataAndroid metadata, |
| bool requires_reauth_to_see, |
| bool is_masked_server_entity) |
| : entity_type(std::move(entity_type)), |
| record_type(record_type), |
| attribute_instances(std::move(attribute_instances)), |
| nickname(std::move(nickname)), |
| metadata(std::move(metadata)), |
| requires_reauth_to_see(requires_reauth_to_see), |
| is_masked_server_entity(is_masked_server_entity) {} |
| |
| EntityInstanceAndroid::EntityInstanceAndroid(const EntityInstanceAndroid&) = |
| default; |
| EntityInstanceAndroid::EntityInstanceAndroid(EntityInstanceAndroid&&) = default; |
| EntityInstanceAndroid::~EntityInstanceAndroid() = default; |
| |
| EntityInstance EntityInstanceAndroid::ToEntityInstance( |
| base::optional_ref<const EntityInstance> existing_entity) const { |
| CHECK(!existing_entity || existing_entity->guid().value() == metadata.guid); |
| |
| base::flat_set<AttributeInstance, AttributeInstance::CompareByType> |
| attributes_set; |
| |
| for (const AttributeInstanceAndroid& attr : attribute_instances) { |
| AttributeInstance maybe_new_attribute = attr.ToAttributeInstance(); |
| base::optional_ref<const AttributeInstance> existing_attribute_instance = |
| existing_entity ? existing_entity->attribute(maybe_new_attribute.type()) |
| : std::nullopt; |
| const std::optional<std::u16string> existing_attribute_raw_value = |
| existing_attribute_instance |
| ? std::optional<std::u16string>( |
| existing_attribute_instance->GetCompleteRawInfo()) |
| : std::nullopt; |
| const std::u16string maybe_new_attribute_raw_value = |
| maybe_new_attribute.GetCompleteRawInfo(); |
| |
| // Only use the attribute instance coming from the management page if it is |
| // new or it has actually changed. This guarantees information such |
| // sub-types definitions (such as NAME_FIRST, NAME_LAST etc) are not missed. |
| if (!existing_attribute_raw_value || |
| maybe_new_attribute_raw_value != existing_attribute_raw_value.value()) { |
| attributes_set.insert(attr.ToAttributeInstance()); |
| } else { |
| attributes_set.insert( |
| AttributeInstance(existing_attribute_instance.value())); |
| } |
| } |
| |
| // TODO(crbug.com/542083924): The Java EntityInstance doesn't support |
| // RecordTypeData. Until RecordTypeData exists that is relevant in Android or |
| // can be/ created by Android, copying the RecordTypeData from the existing |
| // entity (if available) or default constructing it suffices. |
| auto record_type_data = [&] -> EntityInstance::RecordTypeData { |
| if (existing_entity) { |
| return existing_entity->record_type_data(); |
| } |
| // If there is no existing entity, we default construct the record type |
| // data. |
| switch (record_type) { |
| case EntityInstance::RecordType::kLocal: |
| return EntityInstance::LocalRecordTypePayload{}; |
| case EntityInstance::RecordType::kServerWallet: |
| // If there was no existing entity, the newly created entity cannot yet |
| // have a management URL, which is provided by the Wallet server. |
| return EntityInstance::WalletRecordTypePayload{.management_url = |
| GURL()}; |
| case EntityInstance::RecordType::kPersonalContext: |
| // pContext entities cannot be created from settings. |
| NOTREACHED(); |
| } |
| NOTREACHED(); |
| }(); |
| return EntityInstance( |
| entity_type.ToEntityType(), std::move(attributes_set), |
| EntityInstance::EntityId( |
| metadata.guid.empty() |
| ? base::Uuid::GenerateRandomV4().AsLowercaseString() |
| : metadata.guid), |
| nickname, metadata.date_modified, metadata.use_count, metadata.use_date, |
| std::move(record_type_data), EntityInstance::AreAttributesReadOnly(false), |
| /*frecency_override=*/""); |
| } |
| |
| } // namespace autofill |