Add features for automotive usecases.

1. Add clock to metadata - If the CAR does not have a clock (no network
or bad clock) then Radio RDS could be used as a proxy.
2. Add support for emergency announcement - If the CAR wants to make an
emergency announcement.
    2.1 Add support for callbacks.

Bug: b/24807501
Bug: b/22701655

Change-Id: Ie5ada889bb6dfc4a2c1b02fc1db2d48d6899bcde
(cherry picked from commit 5768372922fbdfa47afef5bb414c69d606e7c1d2)
diff --git a/modules/radio/radio_hw.c b/modules/radio/radio_hw.c
index 9c0f22c..bd9e324 100644
--- a/modules/radio/radio_hw.c
+++ b/modules/radio/radio_hw.c
@@ -58,6 +58,7 @@
                 .rds = RADIO_RDS_US,
                 .ta = false,
                 .af = false,
+                .ea = true,
             }
         },
         {
@@ -74,6 +75,11 @@
     }
 };
 
+static const radio_metadata_clock_t hw_clock = {
+    .utc_seconds_since_epoch = 1234567890,
+    .timezone_offset_in_minutes = (-8 * 60),
+};
+
 struct stub_radio_tuner {
     struct radio_tuner interface;
     struct stub_radio_device *dev;
@@ -103,6 +109,7 @@
     CMD_TUNE,
     CMD_CANCEL,
     CMD_METADATA,
+    CMD_ANNOUNCEMENTS,
 } thread_cmd_type_t;
 
 struct thread_command {
@@ -215,6 +222,9 @@
         ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ICON, BITMAP_FILE_PATH);
         if (ret != 0)
             goto exit;
+        ret = radio_metadata_add_clock(metadata, RADIO_METADATA_KEY_CLOCK, &hw_clock);
+        if (ret != 0)
+            goto exit;
     } else {
         ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ART, BITMAP_FILE_PATH);
         if (ret != 0)
@@ -251,6 +261,10 @@
 
     pthread_mutex_lock(&tuner->lock);
 
+    // Fields which are used to toggle the state of traffic announcements and
+    // ea announcements at random. They are access protected by tuner->lock.
+    bool ea_state = false;
+
     while (true) {
         struct thread_command *cmd = NULL;
         struct listnode *item;
@@ -278,7 +292,8 @@
             cmd = node_to_item(item, struct thread_command, node);
 
             if (got_cancel && (cmd->type == CMD_STEP || cmd->type == CMD_SCAN ||
-                    cmd->type == CMD_TUNE || cmd->type == CMD_METADATA)) {
+                    cmd->type == CMD_TUNE || cmd->type == CMD_METADATA ||
+                    cmd->type == CMD_ANNOUNCEMENTS)) {
                  list_remove(item);
                  free(cmd);
                  continue;
@@ -309,9 +324,11 @@
                           __func__, tuner->config.type,
                           tuner->config.lower_limit, tuner->config.upper_limit);
                     if (tuner->config.type == RADIO_BAND_FM) {
-                        ALOGV("  - stereo %d\n  - rds %d\n  - ta %d\n  - af %d",
+                        ALOGV("  - stereo %d\n  - rds %d\n  - ta %d\n  - af %d\n"
+                              "  - ea %d\n",
                               tuner->config.fm.stereo, tuner->config.fm.rds,
-                              tuner->config.fm.ta, tuner->config.fm.af);
+                              tuner->config.fm.ta, tuner->config.fm.af,
+                              tuner->config.fm.ea);
                     } else {
                         ALOGV("  - stereo %d", tuner->config.am.stereo);
                     }
@@ -377,7 +394,7 @@
 
                     if (tuner->program.tuned) {
                         prepare_metadata(tuner, &tuner->program.metadata, true);
-                        send_command_l(tuner, CMD_METADATA, 5000, NULL);
+                        send_command_l(tuner, CMD_ANNOUNCEMENTS, 1000, NULL);
                     } else {
                         if (tuner->program.metadata != NULL)
                             radio_metadata_deallocate(tuner->program.metadata);
@@ -408,6 +425,28 @@
                     got_cancel = true;
                 } break;
 
+                // Fire emergency announcements if they are enabled in the config. Stub
+                // implementation simply fires an announcement for 5 second
+                // duration with a gap of 5 seconds.
+                case CMD_ANNOUNCEMENTS: {
+                    ALOGV("In annoucements. %d %d %d\n",
+                          ea_state, tuner->config.type, tuner->config.fm.ea);
+                    if (tuner->config.type == RADIO_BAND_FM ||
+                        tuner->config.type == RADIO_BAND_FM_HD) {
+                        if (ea_state) {
+                            ea_state = false;
+                            event.type = RADIO_EVENT_EA;
+                        } else if (tuner->config.fm.ea) {
+                            ea_state = true;
+                            event.type = RADIO_EVENT_EA;
+                        }
+                        event.on = ea_state;
+
+                        if (tuner->config.fm.ea) {
+                            send_command_l(tuner, CMD_ANNOUNCEMENTS, 5000, NULL);
+                        }
+                    }
+                } break;
                 }
                 if (event.type != RADIO_EVENT_HW_FAILURE && tuner->callback != NULL) {
                     pthread_mutex_unlock(&tuner->lock);
@@ -438,7 +477,7 @@
                     free(cmd);
                 }
             }
-            send_command_l(tuner, CMD_METADATA, 100, NULL);
+            send_command_l(tuner, CMD_METADATA, 1000, NULL);
         }
     }
 
@@ -472,7 +511,7 @@
 }
 
 static int tuner_get_configuration(const struct radio_tuner *tuner,
-                         radio_hal_band_config_t *config)
+                                   radio_hal_band_config_t *config)
 {
     struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
     int status = 0;