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_peer_chromeos.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/logging.h" 11 #include "base/stl_util.h" 12 #include "chromeos/dbus/dbus_thread_manager.h" 13 #include "chromeos/dbus/nfc_device_client.h" 14 #include "device/nfc/nfc_ndef_record_utils_chromeos.h" 15 #include "third_party/cros_system_api/dbus/service_constants.h" 16 17 using device::NfcNdefMessage; 18 using device::NfcNdefRecord; 19 20 namespace chromeos { 21 22 namespace { 23 24 typedef std::vector<dbus::ObjectPath> ObjectPathVector; 25 26 } // namespace 27 28 NfcPeerChromeOS::NfcPeerChromeOS(const dbus::ObjectPath& object_path) 29 : object_path_(object_path), 30 weak_ptr_factory_(this) { 31 // Create record objects for all records that were received before. 32 const ObjectPathVector& records = 33 DBusThreadManager::Get()->GetNfcRecordClient()-> 34 GetRecordsForDevice(object_path_); 35 for (ObjectPathVector::const_iterator iter = records.begin(); 36 iter != records.end(); ++iter) { 37 AddRecord(*iter); 38 } 39 DBusThreadManager::Get()->GetNfcRecordClient()->AddObserver(this); 40 } 41 42 NfcPeerChromeOS::~NfcPeerChromeOS() { 43 DBusThreadManager::Get()->GetNfcRecordClient()->RemoveObserver(this); 44 STLDeleteValues(&records_); 45 } 46 47 void NfcPeerChromeOS::AddObserver(device::NfcPeer::Observer* observer) { 48 DCHECK(observer); 49 observers_.AddObserver(observer); 50 } 51 52 void NfcPeerChromeOS::RemoveObserver(device::NfcPeer::Observer* observer) { 53 DCHECK(observer); 54 observers_.RemoveObserver(observer); 55 } 56 57 std::string NfcPeerChromeOS::GetIdentifier() const { 58 return object_path_.value(); 59 } 60 61 const NfcNdefMessage& NfcPeerChromeOS::GetNdefMessage() const { 62 return message_; 63 } 64 65 void NfcPeerChromeOS::PushNdef(const NfcNdefMessage& message, 66 const base::Closure& callback, 67 const ErrorCallback& error_callback) { 68 if (message.records().empty()) { 69 LOG(ERROR) << "Given NDEF message is empty. Cannot push it."; 70 error_callback.Run(); 71 return; 72 } 73 // TODO(armansito): neard currently supports pushing only one NDEF record 74 // to a remote device and won't support multiple records until 0.15. Until 75 // then, report failure if |message| contains more than one record. 76 if (message.records().size() > 1) { 77 LOG(ERROR) << "Currently, pushing only 1 NDEF record is supported."; 78 error_callback.Run(); 79 return; 80 } 81 const NfcNdefRecord* record = message.records()[0]; 82 base::DictionaryValue attributes; 83 if (!nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes( 84 record, &attributes)) { 85 LOG(ERROR) << "Failed to extract NDEF record fields for NDEF push."; 86 error_callback.Run(); 87 return; 88 } 89 DBusThreadManager::Get()->GetNfcDeviceClient()->Push( 90 object_path_, 91 attributes, 92 base::Bind(&NfcPeerChromeOS::OnPushNdef, 93 weak_ptr_factory_.GetWeakPtr(), 94 callback), 95 base::Bind(&NfcPeerChromeOS::OnPushNdefError, 96 weak_ptr_factory_.GetWeakPtr(), 97 error_callback)); 98 } 99 100 void NfcPeerChromeOS::StartHandover(HandoverType handover_type, 101 const base::Closure& callback, 102 const ErrorCallback& error_callback) { 103 // TODO(armansito): Initiating handover with a peer is currently not 104 // supported. For now, return an error immediately. 105 LOG(ERROR) << "NFC Handover currently not supported."; 106 error_callback.Run(); 107 } 108 109 void NfcPeerChromeOS::RecordAdded(const dbus::ObjectPath& object_path) { 110 // Don't create the record object yet. Instead, wait until all record 111 // properties have been received and contruct the object and notify observers 112 // then. 113 VLOG(1) << "Record added: " << object_path.value() << ". Waiting until " 114 << "all properties have been fetched to create record object."; 115 } 116 117 void NfcPeerChromeOS::RecordRemoved(const dbus::ObjectPath& object_path) { 118 NdefRecordMap::iterator iter = records_.find(object_path); 119 if (iter == records_.end()) 120 return; 121 VLOG(1) << "Lost remote NDEF record object: " << object_path.value() 122 << ", removing record."; 123 NfcNdefRecord* record = iter->second; 124 message_.RemoveRecord(record); 125 delete record; 126 records_.erase(iter); 127 } 128 129 void NfcPeerChromeOS::RecordPropertiesReceived( 130 const dbus::ObjectPath& object_path) { 131 VLOG(1) << "Record properties received for: " << object_path.value(); 132 133 // Check if the found record belongs to this device. 134 bool record_found = false; 135 const ObjectPathVector& records = 136 DBusThreadManager::Get()->GetNfcRecordClient()-> 137 GetRecordsForDevice(object_path_); 138 for (ObjectPathVector::const_iterator iter = records.begin(); 139 iter != records.end(); ++iter) { 140 if (*iter == object_path) { 141 record_found = true; 142 break; 143 } 144 } 145 if (!record_found) { 146 VLOG(1) << "Record \"" << object_path.value() << "\" doesn't belong to this" 147 << " device. Ignoring."; 148 return; 149 } 150 151 AddRecord(object_path); 152 } 153 154 void NfcPeerChromeOS::OnPushNdef(const base::Closure& callback) { 155 callback.Run(); 156 } 157 158 void NfcPeerChromeOS::OnPushNdefError(const ErrorCallback& error_callback, 159 const std::string& error_name, 160 const std::string& error_message) { 161 LOG(ERROR) << object_path_.value() << ": Failed to Push NDEF message: " 162 << error_name << ": " << error_message; 163 error_callback.Run(); 164 } 165 166 void NfcPeerChromeOS::AddRecord(const dbus::ObjectPath& object_path) { 167 // Ignore this call if an entry for this record already exists. 168 if (records_.find(object_path) != records_.end()) { 169 VLOG(1) << "Record object for remote \"" << object_path.value() 170 << "\" already exists."; 171 return; 172 } 173 174 NfcRecordClient::Properties* record_properties = 175 DBusThreadManager::Get()->GetNfcRecordClient()-> 176 GetProperties(object_path); 177 DCHECK(record_properties); 178 179 NfcNdefRecord* record = new NfcNdefRecord(); 180 if (!nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord( 181 record_properties, record)) { 182 LOG(ERROR) << "Failed to create record object for record with object " 183 << "path \"" << object_path.value() << "\""; 184 delete record; 185 return; 186 } 187 188 message_.AddRecord(record); 189 records_[object_path] = record; 190 FOR_EACH_OBSERVER(NfcPeer::Observer, observers_, 191 RecordReceived(this, record)); 192 } 193 194 } // namespace chromeos 195