Home | History | Annotate | Download | only in nfc
      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