shill_setup_wifi: Add Binder support

Add support for Binder to shill_setup_wifi.

BUG: 28572520
TEST: 1) Manually set SHILL_USE_BINDER flag in Android.mk
         to true, to build shill with Binder support.
      2) Build dragonboard image and flash device.
      3) Run 'adb shell ping www.google.com' and verify that
         ping fails.
      4) Run 'adb shell shill_setup_wifi --ssid=$NETWORK',
         where $NETWORK is the SSID of an open network in
         range of the dragonboard, and verify that the
         command does not fail.
      5) Repeat step 3, but verify that the ping succeeds.
      6) Repeat step 1, but this time, set the variable to
         false to build shill without Binder support (i.e.
         use D-Bus).
      7) Repeat steps 2-5 to verify that shill_setup_wifi
         continues to work when shill is built with D-Bus
         support.

Change-Id: Id6773e0072627047116c5a254ba294ca3f592380
diff --git a/Android.mk b/Android.mk
index 2ec4d4e..239b5e2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -626,32 +626,50 @@
     bin/ff_debug
 include $(BUILD_MULTI_PREBUILT)
 
-# The following  two targets use the shill D-Bus API, which we do not expose
-# if we are using Binder.
-ifneq ($(SHILL_USE_BINDER), true)
-
 # setup_wifi
 # ========================================================
 include $(CLEAR_VARS)
 # The module name can't be the same of a directory in the source code.
 LOCAL_MODULE := shill_setup_wifi
 LOCAL_CPPFLAGS := $(shill_cpp_flags)
+LOCAL_SRC_FILES := setup_wifi/main.cc
+ifeq ($(SHILL_USE_BINDER), true)
 LOCAL_SHARED_LIBRARIES := \
     $(shill_shared_libraries) \
-    libshill-client \
+    libbinder \
+    libbinderwrapper \
+    libbrillo \
+    libbrillo-binder \
+    libutils
+LOCAL_AIDL_INCLUDES := \
+    frameworks/native/aidl/binder \
+    system/connectivity/shill/binder
+LOCAL_SRC_FILES += \
+    binder/android/system/connectivity/shill/IManager.aidl \
+    binder/android/system/connectivity/shill/IService.aidl \
+    setup_wifi/binder_client.cc
+else
+LOCAL_SHARED_LIBRARIES := \
+    $(shill_shared_libraries) \
     libbrillo-dbus \
-    libchrome-dbus
+    libchrome-dbus \
+    libshill-client
+LOCAL_SRC_FILES += setup_wifi/dbus_client.cc
+endif # SHILL_USE_BINDER
 ifdef BRILLO
 LOCAL_MODULE_TAGS := eng
 endif
 LOCAL_STATIC_LIBRARIES := libgtest_prod
-LOCAL_C_INCLUDES := $(shill_c_includes)
-LOCAL_SRC_FILES := \
-    setup_wifi/dbus_client.cc \
-    setup_wifi/main.cc
+LOCAL_C_INCLUDES := \
+    $(shill_c_includes) \
+    external/cros/system_api/
 $(eval $(shill_cpp_common))
 include $(BUILD_EXECUTABLE)
 
+# The following  target uses the shill D-Bus API, which we do not expose
+# if we are using Binder.
+ifneq ($(SHILL_USE_BINDER), true)
+
 # test-rpc-proxy
 # ========================================================
 include $(CLEAR_VARS)
diff --git a/setup_wifi/binder_client.cc b/setup_wifi/binder_client.cc
new file mode 100644
index 0000000..e6523e3
--- /dev/null
+++ b/setup_wifi/binder_client.cc
@@ -0,0 +1,144 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "shill/setup_wifi/binder_client.h"
+
+#include <sysexits.h>
+
+#include <android/system/connectivity/shill/IManager.h>
+#include <base/bind.h>
+#include <base/logging.h>
+#include <binderwrapper/binder_wrapper.h>
+#include <brillo/daemons/daemon.h>
+#include <shill/key_value_store.h>
+#include <utils/String16.h>
+// TODO(samueltan): remove these includes once b/27270173 is resolved,
+// and Manager is no longer reliant on D-Bus service constants.
+#if defined(__ANDROID__)
+#include <dbus/service_constants.h>
+#else
+#include <chromeos/dbus/service_constants.h>
+#endif  // __ANDROID__
+
+using android::IBinder;
+using android::os::PersistableBundle;
+using android::sp;
+using android::String16;
+using android::system::connectivity::shill::IManager;
+using android::system::connectivity::shill::IService;
+using std::string;
+
+namespace {
+const int kTimeoutBetweenStateChecksMs = 100;
+const char kManagerName[] = "android.system.connectivity.shill.IManager";
+}  // namespace
+
+namespace setup_wifi {
+
+BinderClient::BinderClient(const string& ssid, const string& psk,
+                           bool is_hex_ssid, int timeout)
+    : ssid_(ssid), psk_(psk), is_hex_ssid_(is_hex_ssid), timeout_(timeout) {}
+
+int BinderClient::OnInit() {
+  int ret = Daemon::OnInit();
+  if (ret != EX_OK) {
+    return ret;
+  }
+  ConfigureAndConnect();
+  // Timeout if we can't get online.
+  brillo::MessageLoop::current()->PostDelayedTask(
+      base::Bind(&BinderClient::Quit, base::Unretained(this)),
+      base::TimeDelta::FromSeconds(timeout_));
+  return EX_OK;
+}
+
+bool BinderClient::ConfigureAndConnect() {
+  android::BinderWrapper::Create();
+  if (!binder_watcher_.Init()) {
+    LOG(ERROR) << "Binder connection failed.";
+    return false;
+  }
+
+  sp<IBinder> manager_binder =
+      android::BinderWrapper::Get()->GetService(kManagerName);
+  if (!manager_binder.get()) {
+    LOG(ERROR) << "Manager service not registered.";
+    return false;
+  }
+
+  sp<IManager> manager = android::interface_cast<IManager>(manager_binder);
+  if (!manager->ConfigureService(GetServiceConfig(), &shill_service_proxy_)
+           .isOk()) {
+    LOG(ERROR) << "Configure service failed.";
+    return false;
+  }
+
+  if (!shill_service_proxy_->Connect().isOk()) {
+    LOG(ERROR) << "Connect service failed.";
+    return false;
+  }
+
+  PostCheckWifiStatusTask();
+  return true;
+}
+
+void BinderClient::PostCheckWifiStatusTask() {
+  LOG(INFO) << "Sleeping now. Will check wifi status in 100 ms.";
+  brillo::MessageLoop::current()->PostDelayedTask(
+      base::Bind(&BinderClient::QuitIfOnline, base::Unretained(this)),
+      base::TimeDelta::FromMilliseconds(kTimeoutBetweenStateChecksMs));
+}
+
+// Check if the device is online. If it is, quit.
+void BinderClient::QuitIfOnline() {
+  if (IsOnline())
+    Quit();
+  else
+    PostCheckWifiStatusTask();
+}
+
+bool BinderClient::IsOnline() {
+  int state;
+  if (!shill_service_proxy_->GetState(&state).isOk()) {
+    LOG(ERROR) << "Get state failed.";
+    PostCheckWifiStatusTask();
+    return false;
+  }
+
+  return state == android::system::connectivity::shill::IService::STATE_ONLINE;
+}
+
+PersistableBundle BinderClient::GetServiceConfig() {
+  PersistableBundle configure_bundle;
+  configure_bundle.putString(String16(shill::kTypeProperty),
+                             String16(shill::kTypeWifi));
+  if (is_hex_ssid_) {
+    configure_bundle.putString(String16(shill::kWifiHexSsid),
+                               String16(ssid_.c_str()));
+  } else {
+    configure_bundle.putString(String16(shill::kSSIDProperty),
+                               String16(ssid_.c_str()));
+  }
+  if (!psk_.empty()) {
+    configure_bundle.putString(String16(shill::kPassphraseProperty),
+                               String16(psk_.c_str()));
+    configure_bundle.putString(String16(shill::kSecurityProperty),
+                               String16(shill::kSecurityPsk));
+  }
+  return configure_bundle;
+}
+
+}  // namespace setup_wifi
diff --git a/setup_wifi/binder_client.h b/setup_wifi/binder_client.h
new file mode 100644
index 0000000..800dae4
--- /dev/null
+++ b/setup_wifi/binder_client.h
@@ -0,0 +1,58 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef SHILL_SETUP_WIFI_BINDER_CLIENT_H_
+#define SHILL_SETUP_WIFI_BINDER_CLIENT_H_
+
+#include <map>
+#include <string>
+
+#include <android/system/connectivity/shill/IService.h>
+#include <binder/PersistableBundle.h>
+#include <brillo/binder_watcher.h>
+#include <brillo/daemons/daemon.h>
+#include <utils/StrongPointer.h>
+
+namespace setup_wifi {
+
+class BinderClient : public brillo::Daemon {
+ public:
+  BinderClient(const std::string& ssid, const std::string& psk,
+               bool is_hex_ssid, int timeout);
+  ~BinderClient() override {}
+
+ protected:
+  int OnInit() override;
+
+ private:
+  bool ConfigureAndConnect();
+  void PostCheckWifiStatusTask();
+  void QuitIfOnline();
+  bool IsOnline();
+  android::os::PersistableBundle GetServiceConfig();
+
+  brillo::BinderWatcher binder_watcher_;
+  android::sp<android::system::connectivity::shill::IService>
+      shill_service_proxy_;
+  std::string ssid_;
+  std::string psk_;
+  bool is_hex_ssid_;
+  int timeout_;
+};
+
+}  // namespace setup_wifi
+
+#endif  // SHILL_SETUP_WIFI_BINDER_CLIENT_H_
diff --git a/setup_wifi/main.cc b/setup_wifi/main.cc
index 949cda1..daa5c48 100644
--- a/setup_wifi/main.cc
+++ b/setup_wifi/main.cc
@@ -25,7 +25,11 @@
 #include <base/strings/string_number_conversions.h>
 #include <brillo/daemons/daemon.h>
 
+#ifdef ENABLE_BINDER
+#include "shill/setup_wifi/binder_client.h"
+#else  // !ENABLE_BINDER
 #include "shill/setup_wifi/dbus_client.h"
+#endif  // ENABLE_BINDER || !ENABLE_BINDER
 
 namespace {
 static const char kHelp[] = "help";
@@ -77,8 +81,12 @@
     }
   }
 
-  std::unique_ptr<brillo::Daemon> client(
-      new setup_wifi::DBusClient(ssid, psk, is_hex_ssid, timeout));
+  std::unique_ptr<brillo::Daemon> client;
+#ifdef ENABLE_BINDER
+  client.reset(new setup_wifi::BinderClient(ssid, psk, is_hex_ssid, timeout));
+#else  // !ENABLE_BINDER
+  client.reset(new setup_wifi::DBusClient(ssid, psk, is_hex_ssid, timeout));
+#endif  // ENABLE_BINDER || !ENABLE_BINDER
   client->Run();
   LOG(INFO) << "Process exiting.";