Added new DIAL app for Oregano
Added callbacks to populate Cast servicedata and activity-status
Implement servicedata callback to publish Basil URL

Change-Id: I69835e93290ff196fbfaeb71774e0326d62b05d5
diff --git a/src/server/dial_options.h b/src/server/dial_options.h
index 6c24a1b..3c32e29 100644
--- a/src/server/dial_options.h
+++ b/src/server/dial_options.h
@@ -38,6 +38,10 @@
 #define UUID_OPTION_LONG "--uuid-name"
 #define UUID_DESCRIPTION "UUID of the device"
 
+#define UI_TYPE_OPTION "-I"
+#define UI_TYPE_OPTION_LONG "--ui-type"
+#define UI_TYPE_DESCRIPTION "UI type"
+
 struct dial_options
 {
     const char * pOption;
@@ -61,6 +65,11 @@
         UUID_OPTION,
         UUID_OPTION_LONG,
         UUID_DESCRIPTION
+    },
+    {
+        UI_TYPE_OPTION,
+        UI_TYPE_OPTION_LONG,
+        UI_TYPE_DESCRIPTION
     }
 };
 
diff --git a/src/server/dial_server.c b/src/server/dial_server.c
index b60da30..ac7c210 100644
--- a/src/server/dial_server.c
+++ b/src/server/dial_server.c
@@ -166,12 +166,36 @@
                       "<service xmlns=\"urn:dial-multiscreen-org:schemas:dial\">\r\n"
                       "  <name>%s</name>\r\n"
                       "  <options allowStop=\"%s\"/>\r\n"
-                      "  <state>%s</state>\r\n"
-                      "%s"
-                      "</service>\r\n", app->name, canStop ? "true":"false",
-                      app->state ? "running" : "stopped",
-                      app->state ==  kDIALStatusStopped ?
-                      "" : "  <link rel=\"run\" href=\"run\"/>\r\n" );
+                      "  <state>%s</state>\r\n",
+                      app->name, canStop ? "true":"false",
+                      app->state ? "running" : "stopped");
+        if (app->state != kDIALStatusStopped) {
+            mg_printf(conn,
+                      "  <link rel=\"run\" href=\"run\"/>\r\n");
+        }
+        if (app->callbacks.service_data_cb != NULL) {
+            struct CastServiceData serviceData = app->callbacks.service_data_cb(ds, app_name, app->run_id, app->callback_data);
+            mg_printf(conn,
+                      "  <servicedata xmlns=\"urn:chrome.google.com:cast\">\r\n"
+                      "    <connectionSvcURL>http://%s:%d%s</connectionSvcURL>\r\n"
+                      "    <protocols>\r\n"
+                      "      <protocol>%s</protocol>\r\n"
+                      "    </protocols>\r\n"
+                      "  </servicedata>\r\n",
+                      serviceData.connection_svc_host,
+                      serviceData.connection_svc_port,
+                      serviceData.connection_svc_path,
+                      serviceData.protocol);
+        }
+        if (app->callbacks.activity_status_cb != NULL) {
+            struct CastActivityStatus activityStatus = app->callbacks.activity_status_cb(ds, app_name, app->run_id, app->callback_data);
+            mg_printf(conn,
+                      "  <activity-status xmlns=\"urn:chrome.google.com:cast\">\r\n"
+                      "    <description>%s</description>\r\n"
+                      "  </activity-status>\r\n"
+                      "  </servicedata>\r\n", activityStatus.description);
+        }
+        mg_printf(conn, "</service>\r\n");
     }
     ds_unlock(ds);
 }
diff --git a/src/server/dial_server.h b/src/server/dial_server.h
index fa309ad..dee72d3 100644
--- a/src/server/dial_server.h
+++ b/src/server/dial_server.h
@@ -42,6 +42,23 @@
 #define DIAL_MAX_PAYLOAD (4096)
 
 /*
+ * Cast service data
+ */
+struct CastServiceData {
+    const char *connection_svc_host;
+    int connection_svc_port;
+    const char *connection_svc_path;
+    const char *protocol;
+};
+
+/*
+ * Cast activity status
+ */
+struct CastActivityStatus {
+    const char *description;
+};
+
+/*
  * Opaque DIAL server handle
  */
 struct DIALServer_;
@@ -69,6 +86,17 @@
 typedef DIALStatus (*DIAL_app_status_cb)(DIALServer *ds, const char *app_name,
                                          DIAL_run_t run_id, int* pCanStop,
                                          void *callback_data);
+/*
+ * Cast service data callback
+ */
+typedef struct CastServiceData (*CAST_app_service_data_cb)(DIALServer *ds, const char *app_name,
+                                 DIAL_run_t run_id, void *callback_data);
+
+/*
+ * Cast service data callback
+ */
+typedef struct CastActivityStatus (*CAST_app_activity_status_cb)(DIALServer *ds, const char *app_name,
+                                 DIAL_run_t run_id, void *callback_data);
 
 /*
  * DIAL callbacks
@@ -77,6 +105,8 @@
     DIAL_app_start_cb start_cb;
     DIAL_app_stop_cb stop_cb;
     DIAL_app_status_cb status_cb;
+    CAST_app_service_data_cb service_data_cb;
+    CAST_app_activity_status_cb activity_status_cb;
 };
 
 /*
diff --git a/src/server/main.c b/src/server/main.c
index 57bc0eb..7eabcdc 100644
--- a/src/server/main.c
+++ b/src/server/main.c
@@ -46,6 +46,7 @@
 static char spFriendlyName[BUFSIZE];
 static char spModelName[BUFSIZE];
 static char spUuid[BUFSIZE];
+static char spUiType[BUFSIZE];
 static int gDialPort;
 
 static char *spAppNetflix = "netflix";      // name of the netflix executable
@@ -57,9 +58,14 @@
 static char *spAppFiberTV = "miniclient";
 static char *spAppFiberTVMatch = NULL;
 
+static char *spAppOregano = "content_shell";
+static char *spAppOreganoMatch = "fiber.google.com/oregano";
+
 // Adding 20 bytes for prepended source_type for Netflix
 static char sQueryParam[DIAL_MAX_PAYLOAD+20];
 
+static const char *spIpAddress;
+
 static int doesMatch( char* pzExp, char* pzStr)
 {
     regex_t exp;
@@ -365,7 +371,46 @@
     }
 }
 
-void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid);
+static DIALStatus oregano_start(DIALServer *ds, const char *appname,
+                                const char *args, size_t arglen,
+                                DIAL_run_t *run_id, void *callback_data) {
+    fprintf(stderr, "** LAUNCH GoogleFiberTV **\n");
+    // TODO(pbaldwin): Add ability to start Oregano when not already running
+    pid_t pid;
+    if ((pid = isAppRunning( spAppOregano, spAppOreganoMatch ))) {
+        *run_id = (void *)(long)pid;
+        return kDIALStatusRunning;
+    } else {
+        return kDIALStatusStopped;
+    }
+}
+
+static DIALStatus oregano_status(DIALServer *ds, const char *appname,
+                                 DIAL_run_t run_id, int *pCanStop, void *callback_data) {
+    *pCanStop = 1;
+    return isAppRunning( spAppOregano, spAppOreganoMatch ) ? kDIALStatusRunning : kDIALStatusStopped;
+}
+
+static struct CastServiceData oregano_service_data(DIALServer *ds, const char *appname,
+                                 DIAL_run_t run_id, void *callback_data) {
+    struct CastServiceData serviceData;
+    serviceData.connection_svc_host = spIpAddress;
+    serviceData.connection_svc_port = 5153;
+    serviceData.connection_svc_path = "/connections";
+    serviceData.protocol = "marjoram";
+    return serviceData;
+}
+
+static void oregano_stop(DIALServer *ds, const char *appname, DIAL_run_t run_id,
+                         void *callback_data) {
+    fprintf(stderr, "** KILL GoogleFiberTV **\n");
+    pid_t pid;
+    if ((pid = isAppRunning( spAppOregano, spAppOreganoMatch ))) {
+        kill(pid, SIGTERM);
+    }
+}
+
+void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid, const char **ppIpAddress);
 
 static void printUsage()
 {
@@ -392,16 +437,21 @@
 {
     DIALServer *ds;
     ds = DIAL_start();
-    struct DIALAppCallbacks cb_nf = {netflix_start, netflix_stop, netflix_status};
-    struct DIALAppCallbacks cb_yt = {youtube_start, youtube_stop, youtube_status};
-    struct DIALAppCallbacks cb_ft = {fibertv_start, fibertv_stop, fibertv_status};
+    struct DIALAppCallbacks cb_nf = {netflix_start, netflix_stop, netflix_status, NULL, NULL};
+    struct DIALAppCallbacks cb_yt = {youtube_start, youtube_stop, youtube_status, NULL, NULL};
+    struct DIALAppCallbacks cb_ft = {fibertv_start, fibertv_stop, fibertv_status, NULL, NULL};
+    struct DIALAppCallbacks cb_or = {oregano_start, oregano_stop, oregano_status, oregano_service_data, NULL};
 
     DIAL_register_app(ds, "Netflix", &cb_nf, NULL);
     DIAL_register_app(ds, "YouTube", &cb_yt, NULL);
-    DIAL_register_app(ds, "FiberTV", &cb_ft, NULL);
+    if (strcmp(spUiType, "oregano") == 0) {
+      DIAL_register_app(ds, "GoogleFiberTV", &cb_or, NULL);
+    } else {
+      DIAL_register_app(ds, "FiberTV", &cb_ft, NULL);
+    }
     gDialPort = DIAL_get_port(ds);
     fprintf(stderr, "launcher listening on gDialPort %d\n", gDialPort);
-    run_ssdp(gDialPort, spFriendlyName, spModelName, spUuid);
+    run_ssdp(gDialPort, spFriendlyName, spModelName, spUuid, &spIpAddress);
 
     DIAL_stop(ds);
 }
@@ -419,6 +469,9 @@
         case 2: // UUID
             setValue( pOption, spUuid );
             break;
+        case 3: // UI type
+            setValue( pOption, spUiType );
+            break;
         default:
             // Should not get here
             fprintf( stderr, "Option %d not valid\n", index);
diff --git a/src/server/quick_ssdp.c b/src/server/quick_ssdp.c
index 9cef629..a2da683 100644
--- a/src/server/quick_ssdp.c
+++ b/src/server/quick_ssdp.c
@@ -205,7 +205,7 @@
   }
 }
 
-void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid) {
+void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid, const char **ppIpAddress) {
   struct sockaddr sa;
   socklen_t len = sizeof(sa);
 
@@ -230,6 +230,7 @@
 
   dial_port = port;
   get_local_address();
+  *ppIpAddress = ip_addr;
   ctx = mg_start(&request_handler, NULL, SSDP_PORT);
 
   if (mg_get_listen_addr(ctx, &sa, &len)) {