Add a "gsm signal-profile" command

The previous "gsm signal" command was only letting the user set the RSSI
and BER. This new "signal-profile" commands uses a set of five profiles
corresponding to the signal strength bars displayed on the phone (from 0
to 4, matching one of None, Poor, Moderate, Good, Great).

Signed-off-by: Mathieu Pasquet <mathieu.pasquet@alterway.fr>
Change-Id: If4c0a17af0986e58d11db57140fa9f931bdae8aa
diff --git a/android-qemu1-glue/qemu-cellular-agent-impl.c b/android-qemu1-glue/qemu-cellular-agent-impl.c
index c86af98..e916fe7 100644
--- a/android-qemu1-glue/qemu-cellular-agent-impl.c
+++ b/android-qemu1-glue/qemu-cellular-agent-impl.c
@@ -30,6 +30,18 @@
     }
 }
 
+static void cellular_setSignalStrengthProfile(int zeroTo4)
+{
+    // (See do_gsm_signal_profile() in android-qemu1-glue/console.c)
+
+    if (android_modem) {
+        if (zeroTo4 < 0) zeroTo4 =  0;
+        if (zeroTo4 > 4) zeroTo4 =  4;
+
+        amodem_set_signal_strength_profile(android_modem, zeroTo4);
+    }
+}
+
 static void cellular_setVoiceStatus(enum CellularStatus voiceStatus)
 {
     // (See do_gsm_voice() in android-qemu1-glue/console.c)
@@ -103,6 +115,7 @@
 
 static const QAndroidCellularAgent sQAndroidCellularAgent = {
     .setSignalStrength = cellular_setSignalStrength,
+    .setSignalStrengthProfile = cellular_setSignalStrengthProfile,
     .setVoiceStatus = cellular_setVoiceStatus,
     .setDataStatus = cellular_setDataStatus,
     .setStandard = cellular_setStandard};
diff --git a/android/console.c b/android/console.c
index d856581..bc95054 100644
--- a/android/console.c
+++ b/android/console.c
@@ -1484,61 +1484,85 @@
 static int
 do_gsm_signal( ControlClient  client, char*  args )
 {
-      enum { SIGNAL_RSSI = 0, SIGNAL_BER, NUM_SIGNAL_PARAMS };
-      char*   p = args;
-      int     top_param = -1;
-      int     params[ NUM_SIGNAL_PARAMS ];
+    enum { SIGNAL_RSSI = 0, SIGNAL_BER, NUM_SIGNAL_PARAMS };
+    char*   p = args;
+    int     top_param = -1;
+    int     params[ NUM_SIGNAL_PARAMS ];
 
-      static  int  last_ber = 99;
+    static  int  last_ber = 99;
 
-      if (!p)
-          p = "";
+    if (!p)
+        p = "";
 
-      /* tokenize */
-      while (*p) {
-          char*   end;
-          int  val = strtol( p, &end, 10 );
+    /* tokenize */
+    while (*p) {
+        char*   end;
+        int  val = strtol( p, &end, 10 );
 
-          if (end == p) {
-              control_write( client, "KO: argument '%s' is not a number\n", p );
-              return -1;
-          }
+        if (end == p) {
+            control_write( client, "KO: argument '%s' is not a number\n", p );
+            return -1;
+        }
 
-          params[++top_param] = val;
-          if (top_param + 1 == NUM_SIGNAL_PARAMS)
-              break;
+        params[++top_param] = val;
+        if (top_param + 1 == NUM_SIGNAL_PARAMS)
+            break;
 
-          p = end;
-          while (*p && (p[0] == ' ' || p[0] == '\t'))
-              p += 1;
-      }
+        p = end;
+        while (*p && (p[0] == ' ' || p[0] == '\t'))
+            p += 1;
+    }
 
-      /* sanity check */
-      if (top_param < SIGNAL_RSSI) {
-          control_write( client, "KO: not enough arguments: see 'help gsm signal' for details\r\n" );
-          return -1;
-      }
+    /* sanity check */
+    if (top_param < SIGNAL_RSSI) {
+        control_write( client, "KO: not enough arguments: see 'help gsm signal' for details\r\n" );
+        return -1;
+    }
 
-      int rssi = params[SIGNAL_RSSI];
-      if ((rssi < 0 || rssi > 31) && rssi != 99) {
-          control_write( client, "KO: invalid RSSI - must be 0..31 or 99\r\n");
-          return -1;
-      }
+    int rssi = params[SIGNAL_RSSI];
+    if ((rssi < 0 || rssi > 31) && rssi != 99) {
+        control_write( client, "KO: invalid RSSI - must be 0..31 or 99\r\n");
+        return -1;
+    }
 
-      /* check ber is 0..7 or 99 */
-      if (top_param >= SIGNAL_BER) {
-          int ber = params[SIGNAL_BER];
-          if ((ber < 0 || ber > 7) && ber != 99) {
-              control_write( client, "KO: invalid BER - must be 0..7 or 99\r\n");
-              return -1;
-          }
-          last_ber = ber;
-      }
+    /* check ber is 0..7 or 99 */
+    if (top_param >= SIGNAL_BER) {
+        int ber = params[SIGNAL_BER];
+        if ((ber < 0 || ber > 7) && ber != 99) {
+            control_write( client, "KO: invalid BER - must be 0..7 or 99\r\n");
+            return -1;
+        }
+        last_ber = ber;
+    }
 
-      amodem_set_signal_strength( android_modem, rssi, last_ber );
+    amodem_set_signal_strength( android_modem, rssi, last_ber );
 
-      return 0;
-  }
+    return 0;
+}
+
+static int
+do_gsm_signal_profile( ControlClient  client, char*  args )
+{
+    char* end;
+    char*   p = args;
+    if (!p)
+        p = "";
+    int  val = strtol( p, &end, 10 );
+
+    if (end == p) {
+        control_write( client, "KO: argument '%s' is not a number\r\n", p );
+        return -1;
+    }
+
+    if (val < 0 || val > 4) {
+        control_write( client, "KO: invalid signal strength - must be 0..4\r\n");
+        return -1;
+    }
+
+    amodem_set_signal_strength_profile( android_modem, val );
+
+    return 0;
+}
 
 
 #if 0
@@ -1620,6 +1644,11 @@
     "ber range is 0..7 percent and 99 for unknown\r\n",
     NULL, do_gsm_signal, NULL },
 
+    { "signal-profile", "set the signal strength profile",
+    "'gsm signal-profile <strength>' changes the reported strength on next (15s) update.\r\n"
+    "strength range is 0..4\r\n",
+    NULL, do_gsm_signal_profile, NULL },
+
     { NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
diff --git a/android/emulation/control/cellular_agent.h b/android/emulation/control/cellular_agent.h
index f631872..734a8e9 100644
--- a/android/emulation/control/cellular_agent.h
+++ b/android/emulation/control/cellular_agent.h
@@ -22,11 +22,18 @@
 enum CellularStandard { Cellular_Std_GSM,  Cellular_Std_HSCSD, Cellular_Std_GPRS, Cellular_Std_EDGE,
                         Cellular_Std_UMTS, Cellular_Std_HSDPA, Cellular_Std_full };
 
+enum CellularSignal { Cellular_Signal_None, Cellular_Signal_Poor, Cellular_Signal_Moderate,
+                      Cellular_Signal_Good, Cellular_Signal_Great };
+
 typedef struct QAndroidCellularAgent {
     // Sets the cellular signal strength
     // Input: 0(none) .. 31(very strong)
     void (*setSignalStrength)(int zeroTo31);
 
+    // Sets the cellular signal strength
+    // Input: enum CellularSignal, above
+    void (*setSignalStrengthProfile)(enum CellularSignal);
+
     // Sets the status of the voice connectivity
     // Input: enum CellularStatus, above
     void (*setVoiceStatus)(enum CellularStatus);
diff --git a/android/telephony/modem.c b/android/telephony/modem.c
index 1690778..1602cd1 100644
--- a/android/telephony/modem.c
+++ b/android/telephony/modem.c
@@ -210,6 +210,46 @@
 
 #define  A_MODEM_SELF_SIZE   3
 
+typedef struct _signal {
+    int gsm_rssi;
+    int gsm_ber;
+    int cdma_dbm;
+    int cdma_ecio;
+    int evdo_dbm;
+    int evdo_ecio;
+    int evdo_snr;
+    int lte_rssi;
+    int lte_rsrp;
+    int lte_rsrq;
+    int lte_rssnr;
+    int lte_cqi;
+    int lte_timing;
+} signal_t;
+
+typedef enum {
+    NONE = 0,
+    POOR = 1,
+    MODERATE = 2,
+    GOOD = 3,
+    GREAT = 4,
+} signal_strength;
+
+/*
+ * Values derived from the ranges used in the SignalStrength
+ * class in the frameworks/base telephony framework.
+ */
+static const signal_t NET_PROFILES[5] = {
+    /* NONE */
+    {0, 7, 105, 160, 110, 160, 0, 105, 140, 3, -200, 0, 500},
+    /* POOR (one bar) */
+    {5, 5, 100, 150, 100, 150, 2, 100, 110,  5, 0, 2, 300},
+    /* MODERATE (2 bars) */
+    {12, 4, 90, 120, 80, 120, 4, 90, 100, 10, 30, 7, 200},
+    /* GOOD (3 bars) */
+    {20, 2, 80, 100, 70, 100, 6, 70, 90, 15, 100, 12, 100},
+    /* GREAT (4 bars) */
+    {30, 0, 70, 80, 60, 80, 7, 60, 80, 20, 200, 15, 50},
+};
 
 typedef struct AModemRec_
 {
@@ -222,8 +262,12 @@
     int           cell_id;
     int           base_port;
 
-    int           rssi;
-    int           ber;
+    /* Signal strength variables */
+    int             use_signal_profile;
+    signal_strength quality;
+    int             rssi;
+    int             ber;
+
 
     /* SMS */
     int           wait_sms;
@@ -455,7 +499,9 @@
     modem->radio_state = A_RADIO_STATE_OFF;
     modem->wait_sms    = 0;
 
-    modem->rssi= 7;    // Two signal strength bars
+    modem->use_signal_profile = 1;
+    modem->quality = MODERATE;    // Two signal strength bars
+    modem->rssi = 7;   // Two signal strength bars
     modem->ber = 99;   // Means 'unknown'
 
     modem->oper_name_index     = amodem_nvram_get_int(modem, NV_OPER_NAME_INDEX, 2);
@@ -920,6 +966,16 @@
 {
     modem->rssi = rssi;
     modem->ber = ber;
+    modem->use_signal_profile = 0;
+}
+
+void
+amodem_set_signal_strength_profile( AModem modem, int quality )
+{
+    if (quality >= NONE && quality <= GREAT) {
+        modem->quality = quality;
+        modem->use_signal_profile = 1;
+    }
 }
 
 static void
@@ -2190,14 +2246,25 @@
       android_snapshot_update_time_request = 0;
     }
 
-    // rssi = 0 (<-113dBm) 1 (<-111) 2-30 (<-109--53) 31 (>=-51) 99 (?!)
-    // ber (bit error rate) - always 99 (unknown), apparently.
-    // TODO: return 99 if modem->radio_state==A_RADIO_STATE_OFF, once radio_state is in snapshot.
-    int rssi = modem->rssi;
-    int ber = modem->ber;
-    rssi = (0 > rssi && rssi > 31) ? 99 : rssi ;
-    ber = (0 > ber && ber > 7 ) ? 99 : ber;
-    amodem_add_line( modem, "+CSQ: %i,%i,85,130,90,6,4,25,9,50,68,12\r\n", rssi, ber );
+    if (modem->use_signal_profile) {
+        signal_t current_signal = NET_PROFILES[modem->quality];
+        amodem_add_line(modem, "+CSQ: %i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i\r\n",
+                        current_signal.gsm_rssi, current_signal.gsm_ber,
+                        current_signal.cdma_dbm, current_signal.cdma_ecio,
+                        current_signal.evdo_dbm, current_signal.evdo_ecio, current_signal.evdo_snr,
+                        current_signal.lte_rssi, current_signal.lte_rsrp, current_signal.lte_rsrq,
+                        current_signal.lte_rssnr, current_signal.lte_cqi, current_signal.lte_timing);
+    }
+    else {
+        // rssi = 0 (<-113dBm) 1 (<-111) 2-30 (<-109--53) 31 (>=-51) 99 (?!)
+        // ber (bit error rate) - always 99 (unknown), apparently.
+        // TODO: return 99 if modem->radio_state==A_RADIO_STATE_OFF, once radio_state is in snapshot.
+        int rssi = modem->rssi;
+        int ber = modem->ber;
+        rssi = (0 > rssi && rssi > 31) ? 99 : rssi;
+        ber = (0 > ber && ber > 7 ) ? 99 : ber;
+        amodem_add_line(modem, "+CSQ: %i,%i,85,130,90,6,4,25,9,50,68,12\r\n", rssi, ber);
+    }
     return amodem_end_line( modem );
 }
 
diff --git a/android/telephony/modem.h b/android/telephony/modem.h
index 5b896bf..ef41075 100644
--- a/android/telephony/modem.h
+++ b/android/telephony/modem.h
@@ -50,6 +50,9 @@
 /* Set the received signal strength indicator and bit error rate */
 extern void         amodem_set_signal_strength( AModem modem, int rssi, int ber );
 
+/* Set the received signal strength profile */
+extern void         amodem_set_signal_strength_profile( AModem modem, int quality );
+
 /** SIM CARD STATUS
  **/
 extern ASimCard    amodem_get_sim( AModem  modem );