buffet: Add support for DEVICE_DELETED XMPP notification

When DEVICE_DELETED notification is received over XMPP channel,
buffet will remove any cloud registration information (credentials,
robot account) and close server connections (XMPP channel, etc).

BUG=brillo:1215
TEST=`FEATURES=test emerge-link buffet`
     Test manually on the device.

Change-Id: I86e19659b9fbc06685bcabb6c659a633e797cae5
Reviewed-on: https://chromium-review.googlesource.com/281666
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Trybot-Ready: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index b76c79e..f1c84b8 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -1037,6 +1037,15 @@
                 base::Bind(&IgnoreCloudError));
 }
 
+void DeviceRegistrationInfo::OnDeviceDeleted(const std::string& device_id) {
+  if (device_id != config_->device_id()) {
+    LOG(WARNING) << "Unexpected device deletion notification for device ID '"
+                 << device_id << "'";
+    return;
+  }
+  MarkDeviceUnregistered();
+}
+
 void DeviceRegistrationInfo::MarkDeviceUnregistered() {
   if (!HaveRegistrationCredentials())
     return;
diff --git a/buffet/device_registration_info.h b/buffet/device_registration_info.h
index 19c698c..43c70a4 100644
--- a/buffet/device_registration_info.h
+++ b/buffet/device_registration_info.h
@@ -275,11 +275,12 @@
   void OnCommandDefsChanged();
   void OnStateChanged();
 
-  // Overrides from NotificationDelegate
+  // Overrides from NotificationDelegate.
   void OnConnected(const std::string& channel_name) override;
   void OnDisconnected() override;
   void OnPermanentFailure() override;
   void OnCommandCreated(const base::DictionaryValue& command) override;
+  void OnDeviceDeleted(const std::string& device_id) override;
 
   // Wipes out the device registration information and stops server connections.
   void MarkDeviceUnregistered();
diff --git a/buffet/notification/notification_delegate.h b/buffet/notification/notification_delegate.h
index b2d7184..529e39d 100644
--- a/buffet/notification/notification_delegate.h
+++ b/buffet/notification/notification_delegate.h
@@ -19,6 +19,8 @@
   virtual void OnPermanentFailure() = 0;
   // Called when a new command is sent via the notification channel.
   virtual void OnCommandCreated(const base::DictionaryValue& command) = 0;
+  // Called when DEVICE_DELETED notification is received.
+  virtual void OnDeviceDeleted(const std::string& device_id) = 0;
 
  protected:
   virtual ~NotificationDelegate() = default;
diff --git a/buffet/notification/notification_parser.cc b/buffet/notification/notification_parser.cc
index 5885afa..8cde3fa 100644
--- a/buffet/notification/notification_parser.cc
+++ b/buffet/notification/notification_parser.cc
@@ -23,6 +23,19 @@
   return true;
 }
 
+// Processes DEVICE_DELETED notifications.
+bool ParseDeviceDeleted(const base::DictionaryValue& notification,
+                        NotificationDelegate* delegate) {
+  std::string device_id;
+  if (!notification.GetString("deviceId", &device_id)) {
+    LOG(ERROR) << "DEVICE_DELETED notification is missing 'deviceId' property";
+    return false;
+  }
+
+  delegate->OnDeviceDeleted(device_id);
+  return true;
+}
+
 }  // anonymous namespace
 
 bool ParseNotificationJson(const base::DictionaryValue& notification,
@@ -46,6 +59,9 @@
   if (type == "COMMAND_CREATED")
     return ParseCommandCreated(notification, delegate);
 
+  if (type == "DEVICE_DELETED")
+    return ParseDeviceDeleted(notification, delegate);
+
   // Here we ignore other types of notifications for now.
   LOG(INFO) << "Ignoring push notification of type " << type;
   return true;
diff --git a/buffet/notification/notification_parser_unittest.cc b/buffet/notification/notification_parser_unittest.cc
index c6be507..d578b55 100644
--- a/buffet/notification/notification_parser_unittest.cc
+++ b/buffet/notification/notification_parser_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "buffet/commands/unittest_utils.h"
 
+using testing::SaveArg;
 using testing::Invoke;
 using testing::_;
 
@@ -22,6 +23,7 @@
   MOCK_METHOD0(OnDisconnected, void());
   MOCK_METHOD0(OnPermanentFailure, void());
   MOCK_METHOD1(OnCommandCreated, void(const base::DictionaryValue& command));
+  MOCK_METHOD1(OnDeviceDeleted, void(const std::string&));
 };
 
 class NotificationParserTest : public ::testing::Test {
@@ -72,6 +74,19 @@
   EXPECT_JSON_EQ(expected_json, command_instance);
 }
 
+TEST_F(NotificationParserTest, DeviceDeleted) {
+  auto json = CreateDictionaryValue(R"({
+    "kind":"clouddevices#notification",
+    "type":"DEVICE_DELETED",
+    "deviceId":"some_device_id"
+  })");
+
+  std::string device_id;
+  EXPECT_CALL(delegate_, OnDeviceDeleted(_)).WillOnce(SaveArg<0>(&device_id));
+  EXPECT_TRUE(ParseNotificationJson(*json, &delegate_));
+  EXPECT_EQ("some_device_id", device_id);
+}
+
 TEST_F(NotificationParserTest, Failure_NoKind) {
   auto json = CreateDictionaryValue(R"({
     "type": "COMMAND_CREATED",