1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "device/nfc/nfc_ndef_record_utils_chromeos.h" 6 7 #include "base/logging.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "device/nfc/nfc_ndef_record.h" 10 #include "third_party/cros_system_api/dbus/service_constants.h" 11 12 using device::NfcNdefRecord; 13 14 namespace chromeos { 15 namespace nfc_ndef_record_utils { 16 17 namespace { 18 19 // Maps the NDEF type |type| as returned by neard to the corresponding 20 // device::NfcNdefRecord::Type value. 21 NfcNdefRecord::Type DBusRecordTypeValueToNfcNdefRecordType( 22 const std::string& type) { 23 if (type == nfc_record::kTypeSmartPoster) 24 return NfcNdefRecord::kTypeSmartPoster; 25 if (type == nfc_record::kTypeText) 26 return NfcNdefRecord::kTypeText; 27 if (type == nfc_record::kTypeUri) 28 return NfcNdefRecord::kTypeURI; 29 if (type == nfc_record::kTypeHandoverRequest) 30 return NfcNdefRecord::kTypeHandoverRequest; 31 if (type == nfc_record::kTypeHandoverSelect) 32 return NfcNdefRecord::kTypeHandoverSelect; 33 if (type == nfc_record::kTypeHandoverCarrier) 34 return NfcNdefRecord::kTypeHandoverCarrier; 35 return NfcNdefRecord::kTypeUnknown; 36 } 37 38 // Maps the NDEF type |type| given as a NFC C++ API enumeration to the 39 // corresponding string value defined by neard. 40 std::string NfcRecordTypeEnumToPropertyValue(NfcNdefRecord::Type type) { 41 switch (type) { 42 case NfcNdefRecord::kTypeSmartPoster: 43 return nfc_record::kTypeSmartPoster; 44 case NfcNdefRecord::kTypeText: 45 return nfc_record::kTypeText; 46 case NfcNdefRecord::kTypeURI: 47 return nfc_record::kTypeUri; 48 case NfcNdefRecord::kTypeHandoverRequest: 49 return nfc_record::kTypeHandoverRequest; 50 case NfcNdefRecord::kTypeHandoverSelect: 51 return nfc_record::kTypeHandoverSelect; 52 case NfcNdefRecord::kTypeHandoverCarrier: 53 return nfc_record::kTypeHandoverCarrier; 54 default: 55 return ""; 56 } 57 } 58 59 // Maps the field name |field_name| given as defined in NfcNdefRecord to the 60 // Neard Record D-Bus property name. This handles all fields except for 61 // NfcNdefRecord::kFieldTitles and NfcNdefRecord::kFieldAction, which need 62 // special handling. 63 std::string NdefRecordFieldToDBusProperty(const std::string& field_name) { 64 if (field_name == NfcNdefRecord::kFieldEncoding) 65 return nfc_record::kEncodingProperty; 66 if (field_name == NfcNdefRecord::kFieldLanguageCode) 67 return nfc_record::kLanguageProperty; 68 if (field_name == NfcNdefRecord::kFieldText) 69 return nfc_record::kRepresentationProperty; 70 if (field_name == NfcNdefRecord::kFieldURI) 71 return nfc_record::kUriProperty; 72 if (field_name == NfcNdefRecord::kFieldMimeType) 73 return nfc_record::kMimeTypeProperty; 74 if (field_name == NfcNdefRecord::kFieldTargetSize) 75 return nfc_record::kSizeProperty; 76 return ""; 77 } 78 79 std::string NfcNdefRecordActionValueToDBusActionValue( 80 const std::string& action) { 81 // TODO(armansito): Add bindings for values returned by neard to 82 // service_constants.h. 83 if (action == device::NfcNdefRecord::kSmartPosterActionDo) 84 return "Do"; 85 if (action == device::NfcNdefRecord::kSmartPosterActionSave) 86 return "Save"; 87 if (action == device::NfcNdefRecord::kSmartPosterActionOpen) 88 return "Edit"; 89 return ""; 90 } 91 92 std::string DBusActionValueToNfcNdefRecordActionValue( 93 const std::string& action) { 94 // TODO(armansito): Add bindings for values returned by neard to 95 // service_constants.h. 96 if (action == "Do") 97 return device::NfcNdefRecord::kSmartPosterActionDo; 98 if (action == "Save") 99 return device::NfcNdefRecord::kSmartPosterActionSave; 100 if (action == "Edit") 101 return device::NfcNdefRecord::kSmartPosterActionOpen; 102 return ""; 103 } 104 105 106 // Translates the given dictionary of NDEF fields by recursively converting 107 // each key in |record_data| to its corresponding Record property name defined 108 // by the Neard D-Bus API. The output is stored in |out|. Returns false if an 109 // error occurs. 110 bool ConvertNdefFieldsToDBusAttributes( 111 const base::DictionaryValue& fields, 112 base::DictionaryValue* out) { 113 DCHECK(out); 114 for (base::DictionaryValue::Iterator iter(fields); 115 !iter.IsAtEnd(); iter.Advance()) { 116 // Special case the "titles" and "action" fields. 117 if (iter.key() == NfcNdefRecord::kFieldTitles) { 118 const base::ListValue* titles = NULL; 119 bool value_result = iter.value().GetAsList(&titles); 120 DCHECK(value_result); 121 DCHECK(titles->GetSize() != 0); 122 // TODO(armansito): For now, pick the first title in the list and write 123 // its contents directly to the top level of the field. This is due to an 124 // error in the Neard D-Bus API design. This code will need to be updated 125 // if the neard API changes to correct this. 126 const base::DictionaryValue* first_title = NULL; 127 value_result = titles->GetDictionary(0, &first_title); 128 DCHECK(value_result); 129 if (!ConvertNdefFieldsToDBusAttributes(*first_title, out)) { 130 LOG(ERROR) << "Invalid title field."; 131 return false; 132 } 133 } else if (iter.key() == NfcNdefRecord::kFieldAction) { 134 // The value of the action field needs to be translated. 135 std::string action_value; 136 bool value_result = iter.value().GetAsString(&action_value); 137 DCHECK(value_result); 138 std::string action = 139 NfcNdefRecordActionValueToDBusActionValue(action_value); 140 if (action.empty()) { 141 VLOG(1) << "Invalid action value: \"" << action_value << "\""; 142 return false; 143 } 144 out->SetString(nfc_record::kActionProperty, action); 145 } else { 146 std::string dbus_property = NdefRecordFieldToDBusProperty(iter.key()); 147 if (dbus_property.empty()) { 148 LOG(ERROR) << "Invalid field: " << iter.key(); 149 return false; 150 } 151 out->Set(dbus_property, iter.value().DeepCopy()); 152 } 153 } 154 return true; 155 } 156 157 } // namespace 158 159 bool NfcNdefRecordToDBusAttributes( 160 const NfcNdefRecord* record, 161 base::DictionaryValue* out) { 162 DCHECK(record); 163 DCHECK(out); 164 if (!record->IsPopulated()) { 165 LOG(ERROR) << "Record is not populated."; 166 return false; 167 } 168 out->SetString(nfc_record::kTypeProperty, 169 NfcRecordTypeEnumToPropertyValue(record->type())); 170 return ConvertNdefFieldsToDBusAttributes(record->data(), out); 171 } 172 173 bool RecordPropertiesToNfcNdefRecord( 174 const NfcRecordClient::Properties* properties, 175 device::NfcNdefRecord* out) { 176 if (out->IsPopulated()) { 177 LOG(ERROR) << "Record is already populated!"; 178 return false; 179 } 180 NfcNdefRecord::Type type = 181 DBusRecordTypeValueToNfcNdefRecordType(properties->type.value()); 182 if (type == NfcNdefRecord::kTypeUnknown) { 183 LOG(ERROR) << "Record type is unknown."; 184 return false; 185 } 186 187 // Extract each property. 188 base::DictionaryValue attributes; 189 if (!properties->uri.value().empty()) 190 attributes.SetString(NfcNdefRecord::kFieldURI, properties->uri.value()); 191 if (!properties->mime_type.value().empty()) { 192 attributes.SetString(NfcNdefRecord::kFieldMimeType, 193 properties->mime_type.value()); 194 } 195 if (properties->size.value() != 0) { 196 attributes.SetDouble(NfcNdefRecord::kFieldTargetSize, 197 static_cast<double>(properties->size.value())); 198 } 199 std::string action_value = 200 DBusActionValueToNfcNdefRecordActionValue(properties->action.value()); 201 if (!action_value.empty()) 202 attributes.SetString(NfcNdefRecord::kFieldAction, action_value); 203 204 // The "representation", "encoding", and "language" properties will be stored 205 // differently, depending on whether the record type is "SmartPoster" or 206 // "Text". 207 { 208 scoped_ptr<base::DictionaryValue> text_attributes( 209 new base::DictionaryValue()); 210 if (!properties->representation.value().empty()) { 211 text_attributes->SetString(NfcNdefRecord::kFieldText, 212 properties->representation.value()); 213 } 214 if (!properties->encoding.value().empty()) { 215 text_attributes->SetString(NfcNdefRecord::kFieldEncoding, 216 properties->encoding.value()); 217 } 218 if (!properties->language.value().empty()) { 219 text_attributes->SetString(NfcNdefRecord::kFieldLanguageCode, 220 properties->language.value()); 221 } 222 if (!text_attributes->empty()) { 223 if (type == NfcNdefRecord::kTypeSmartPoster) { 224 base::ListValue* titles = new base::ListValue(); 225 titles->Append(text_attributes.release()); 226 attributes.Set(NfcNdefRecord::kFieldTitles, titles); 227 } else { 228 attributes.MergeDictionary(text_attributes.get()); 229 } 230 } 231 } 232 233 // Populate the given record. 234 return out->Populate(type, &attributes); 235 } 236 237 } // namespace nfc_ndef_record_utils 238 } // namespace chromeos 239