// Copyright 2013 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 "device/nfc/nfc_ndef_record_utils_chromeos.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "device/nfc/nfc_ndef_record.h" #include "third_party/cros_system_api/dbus/service_constants.h" using device::NfcNdefRecord; namespace chromeos { namespace nfc_ndef_record_utils { namespace { // Maps the NDEF type |type| as returned by neard to the corresponding // device::NfcNdefRecord::Type value. NfcNdefRecord::Type DBusRecordTypeValueToNfcNdefRecordType( const std::string& type) { if (type == nfc_record::kTypeSmartPoster) return NfcNdefRecord::kTypeSmartPoster; if (type == nfc_record::kTypeText) return NfcNdefRecord::kTypeText; if (type == nfc_record::kTypeUri) return NfcNdefRecord::kTypeURI; if (type == nfc_record::kTypeHandoverRequest) return NfcNdefRecord::kTypeHandoverRequest; if (type == nfc_record::kTypeHandoverSelect) return NfcNdefRecord::kTypeHandoverSelect; if (type == nfc_record::kTypeHandoverCarrier) return NfcNdefRecord::kTypeHandoverCarrier; return NfcNdefRecord::kTypeUnknown; } // Maps the NDEF type |type| given as a NFC C++ API enumeration to the // corresponding string value defined by neard. std::string NfcRecordTypeEnumToPropertyValue(NfcNdefRecord::Type type) { switch (type) { case NfcNdefRecord::kTypeSmartPoster: return nfc_record::kTypeSmartPoster; case NfcNdefRecord::kTypeText: return nfc_record::kTypeText; case NfcNdefRecord::kTypeURI: return nfc_record::kTypeUri; case NfcNdefRecord::kTypeHandoverRequest: return nfc_record::kTypeHandoverRequest; case NfcNdefRecord::kTypeHandoverSelect: return nfc_record::kTypeHandoverSelect; case NfcNdefRecord::kTypeHandoverCarrier: return nfc_record::kTypeHandoverCarrier; default: return ""; } } // Maps the field name |field_name| given as defined in NfcNdefRecord to the // Neard Record D-Bus property name. This handles all fields except for // NfcNdefRecord::kFieldTitles and NfcNdefRecord::kFieldAction, which need // special handling. std::string NdefRecordFieldToDBusProperty(const std::string& field_name) { if (field_name == NfcNdefRecord::kFieldEncoding) return nfc_record::kEncodingProperty; if (field_name == NfcNdefRecord::kFieldLanguageCode) return nfc_record::kLanguageProperty; if (field_name == NfcNdefRecord::kFieldText) return nfc_record::kRepresentationProperty; if (field_name == NfcNdefRecord::kFieldURI) return nfc_record::kUriProperty; if (field_name == NfcNdefRecord::kFieldMimeType) return nfc_record::kMimeTypeProperty; if (field_name == NfcNdefRecord::kFieldTargetSize) return nfc_record::kSizeProperty; return ""; } std::string NfcNdefRecordActionValueToDBusActionValue( const std::string& action) { // TODO(armansito): Add bindings for values returned by neard to // service_constants.h. if (action == device::NfcNdefRecord::kSmartPosterActionDo) return "Do"; if (action == device::NfcNdefRecord::kSmartPosterActionSave) return "Save"; if (action == device::NfcNdefRecord::kSmartPosterActionOpen) return "Edit"; return ""; } std::string DBusActionValueToNfcNdefRecordActionValue( const std::string& action) { // TODO(armansito): Add bindings for values returned by neard to // service_constants.h. if (action == "Do") return device::NfcNdefRecord::kSmartPosterActionDo; if (action == "Save") return device::NfcNdefRecord::kSmartPosterActionSave; if (action == "Edit") return device::NfcNdefRecord::kSmartPosterActionOpen; return ""; } // Translates the given dictionary of NDEF fields by recursively converting // each key in |record_data| to its corresponding Record property name defined // by the Neard D-Bus API. The output is stored in |out|. Returns false if an // error occurs. bool ConvertNdefFieldsToDBusAttributes( const base::DictionaryValue& fields, base::DictionaryValue* out) { DCHECK(out); for (base::DictionaryValue::Iterator iter(fields); !iter.IsAtEnd(); iter.Advance()) { // Special case the "titles" and "action" fields. if (iter.key() == NfcNdefRecord::kFieldTitles) { const base::ListValue* titles = NULL; bool value_result = iter.value().GetAsList(&titles); DCHECK(value_result); DCHECK(titles->GetSize() != 0); // TODO(armansito): For now, pick the first title in the list and write // its contents directly to the top level of the field. This is due to an // error in the Neard D-Bus API design. This code will need to be updated // if the neard API changes to correct this. const base::DictionaryValue* first_title = NULL; value_result = titles->GetDictionary(0, &first_title); DCHECK(value_result); if (!ConvertNdefFieldsToDBusAttributes(*first_title, out)) { LOG(ERROR) << "Invalid title field."; return false; } } else if (iter.key() == NfcNdefRecord::kFieldAction) { // The value of the action field needs to be translated. std::string action_value; bool value_result = iter.value().GetAsString(&action_value); DCHECK(value_result); std::string action = NfcNdefRecordActionValueToDBusActionValue(action_value); if (action.empty()) { VLOG(1) << "Invalid action value: \"" << action_value << "\""; return false; } out->SetString(nfc_record::kActionProperty, action); } else { std::string dbus_property = NdefRecordFieldToDBusProperty(iter.key()); if (dbus_property.empty()) { LOG(ERROR) << "Invalid field: " << iter.key(); return false; } out->Set(dbus_property, iter.value().DeepCopy()); } } return true; } } // namespace bool NfcNdefRecordToDBusAttributes( const NfcNdefRecord* record, base::DictionaryValue* out) { DCHECK(record); DCHECK(out); if (!record->IsPopulated()) { LOG(ERROR) << "Record is not populated."; return false; } out->SetString(nfc_record::kTypeProperty, NfcRecordTypeEnumToPropertyValue(record->type())); return ConvertNdefFieldsToDBusAttributes(record->data(), out); } bool RecordPropertiesToNfcNdefRecord( const NfcRecordClient::Properties* properties, device::NfcNdefRecord* out) { if (out->IsPopulated()) { LOG(ERROR) << "Record is already populated!"; return false; } NfcNdefRecord::Type type = DBusRecordTypeValueToNfcNdefRecordType(properties->type.value()); if (type == NfcNdefRecord::kTypeUnknown) { LOG(ERROR) << "Record type is unknown."; return false; } // Extract each property. base::DictionaryValue attributes; if (!properties->uri.value().empty()) attributes.SetString(NfcNdefRecord::kFieldURI, properties->uri.value()); if (!properties->mime_type.value().empty()) { attributes.SetString(NfcNdefRecord::kFieldMimeType, properties->mime_type.value()); } if (properties->size.value() != 0) { attributes.SetDouble(NfcNdefRecord::kFieldTargetSize, static_cast(properties->size.value())); } std::string action_value = DBusActionValueToNfcNdefRecordActionValue(properties->action.value()); if (!action_value.empty()) attributes.SetString(NfcNdefRecord::kFieldAction, action_value); // The "representation", "encoding", and "language" properties will be stored // differently, depending on whether the record type is "SmartPoster" or // "Text". { scoped_ptr text_attributes( new base::DictionaryValue()); if (!properties->representation.value().empty()) { text_attributes->SetString(NfcNdefRecord::kFieldText, properties->representation.value()); } if (!properties->encoding.value().empty()) { text_attributes->SetString(NfcNdefRecord::kFieldEncoding, properties->encoding.value()); } if (!properties->language.value().empty()) { text_attributes->SetString(NfcNdefRecord::kFieldLanguageCode, properties->language.value()); } if (!text_attributes->empty()) { if (type == NfcNdefRecord::kTypeSmartPoster) { base::ListValue* titles = new base::ListValue(); titles->Append(text_attributes.release()); attributes.Set(NfcNdefRecord::kFieldTitles, titles); } else { attributes.MergeDictionary(text_attributes.get()); } } } // Populate the given record. return out->Populate(type, &attributes); } } // namespace nfc_ndef_record_utils } // namespace chromeos