普通文本  |  239行  |  8.63 KB

// 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<double>(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<base::DictionaryValue> 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