Add support of dislaying Alpha tag for BIP commands

While BIP data call setup is still handled in RIL/Modem,
this patch is adding support of Alpha tag display on UI.
Alpha tag is optionally included in "OPEN Channel", "Close Channel",
"Send Data" or "Receive Data" command.

"Open channel" will be notified via RIL_UNSOL_STK_PROACTIVE_COMMAND
which requires TERMINAL RESPONSE based on user input.
"Close channel", "Send Data" and "Receive Data" commands
are send via RIL_UNSOL_STK_EVENT_NOTIFY just to display
transient notice.

Bug:5165510
Change-Id: I873e55274c860886bc816ce6fb07cb882d339214
diff --git a/telephony/java/com/android/internal/telephony/cat/AppInterface.java b/telephony/java/com/android/internal/telephony/cat/AppInterface.java
index 2eb6ccb..299e140 100644
--- a/telephony/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/telephony/java/com/android/internal/telephony/cat/AppInterface.java
@@ -42,6 +42,7 @@
      * Enumeration for representing "Type of Command" of proactive commands.
      * Those are the only commands which are supported by the Telephony. Any app
      * implementation should support those.
+     * Refer to ETSI TS 102.223 section 9.4
      */
     public static enum CommandType {
         DISPLAY_TEXT(0x21),
@@ -59,7 +60,11 @@
         SET_UP_IDLE_MODE_TEXT(0x28),
         SET_UP_MENU(0x25),
         SET_UP_CALL(0x10),
-        PROVIDE_LOCAL_INFORMATION(0x26);
+        PROVIDE_LOCAL_INFORMATION(0x26),
+        OPEN_CHANNEL(0x40),
+        CLOSE_CHANNEL(0x41),
+        RECEIVE_DATA(0x42),
+        SEND_DATA(0x43);
 
         private int mValue;
 
diff --git a/telephony/java/com/android/internal/telephony/cat/CatCmdMessage.java b/telephony/java/com/android/internal/telephony/cat/CatCmdMessage.java
index 5155bb2..48c2e2b 100644
--- a/telephony/java/com/android/internal/telephony/cat/CatCmdMessage.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatCmdMessage.java
@@ -85,6 +85,13 @@
             mCallSettings.confirmMsg = ((CallSetupParams) cmdParams).confirmMsg;
             mCallSettings.callMsg = ((CallSetupParams) cmdParams).callMsg;
             break;
+        case OPEN_CHANNEL:
+        case CLOSE_CHANNEL:
+        case RECEIVE_DATA:
+        case SEND_DATA:
+            BIPClientParams param = (BIPClientParams) cmdParams;
+            mTextMsg = param.textMsg;
+            break;
         }
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java
index 5a994f3..74af9fa 100644
--- a/telephony/java/com/android/internal/telephony/cat/CatService.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatService.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -32,6 +34,7 @@
 
 
 import java.io.ByteArrayOutputStream;
+import java.util.List;
 import java.util.Locale;
 
 class RilMessage {
@@ -72,6 +75,7 @@
     private CatCmdMessage mMenuCmd = null;
 
     private RilMessageDecoder mMsgDecoder = null;
+    private boolean mStkAppInstalled = false;
 
     // Service constants.
     static final int MSG_ID_SESSION_END              = 1;
@@ -125,7 +129,10 @@
         mCmdIf.registerForNVReady(this, MSG_ID_SIM_READY, null);
         mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);
 
-        CatLog.d(this, "Is running");
+        // Check if STK application is availalbe
+        mStkAppInstalled = isStkAppInstalled();
+
+        CatLog.d(this, "Running CAT service. STK app installed:" + mStkAppInstalled);
     }
 
     public void dispose() {
@@ -154,7 +161,7 @@
             if (rilMsg.mResCode == ResultCode.OK) {
                 cmdParams = (CommandParams) rilMsg.mData;
                 if (cmdParams != null) {
-                    handleProactiveCommand(cmdParams);
+                    handleCommand(cmdParams, false);
                 }
             }
             break;
@@ -170,7 +177,7 @@
             }
             if (cmdParams != null) {
                 if (rilMsg.mResCode == ResultCode.OK) {
-                    handleProactiveCommand(cmdParams);
+                    handleCommand(cmdParams, true);
                 } else {
                     // for proactive commands that couldn't be decoded
                     // successfully respond with the code generated by the
@@ -183,7 +190,7 @@
         case MSG_ID_REFRESH:
             cmdParams = (CommandParams) rilMsg.mData;
             if (cmdParams != null) {
-                handleProactiveCommand(cmdParams);
+                handleCommand(cmdParams, false);
             }
             break;
         case MSG_ID_SESSION_END:
@@ -197,11 +204,13 @@
     }
 
     /**
-     * Handles RIL_UNSOL_STK_PROACTIVE_COMMAND unsolicited command from RIL.
+     * Handles RIL_UNSOL_STK_EVENT_NOTIFY or RIL_UNSOL_STK_PROACTIVE_COMMAND command
+     * from RIL.
      * Sends valid proactive command data to the application using intents.
-     *
+     * RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE will be send back if the command is
+     * from RIL_UNSOL_STK_PROACTIVE_COMMAND.
      */
-    private void handleProactiveCommand(CommandParams cmdParams) {
+    private void handleCommand(CommandParams cmdParams, boolean isProactiveCmd) {
         CatLog.d(this, cmdParams.getCommandType().name());
 
         CharSequence message;
@@ -235,15 +244,16 @@
                     case CommandParamsFactory.DTTZ_SETTING:
                         resp = new DTTZResponseData(null);
                         sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
-                        return;
+                        break;
                     case CommandParamsFactory.LANGUAGE_SETTING:
                         resp = new LanguageResponseData(Locale.getDefault().getLanguage());
                         sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
-                        return;
+                        break;
                     default:
                         sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
-                        return;
                 }
+                // No need to start STK app here.
+                return;
             case LAUNCH_BROWSER:
                 if ((((LaunchBrowserParams) cmdParams).confirmMsg.text != null)
                         && (((LaunchBrowserParams) cmdParams).confirmMsg.text.equals(STK_DEFAULT))) {
@@ -274,6 +284,42 @@
                     ((CallSetupParams) cmdParams).confirmMsg.text = message.toString();
                 }
                 break;
+            case OPEN_CHANNEL:
+            case CLOSE_CHANNEL:
+            case RECEIVE_DATA:
+            case SEND_DATA:
+                BIPClientParams cmd = (BIPClientParams) cmdParams;
+                if (cmd.bHasAlphaId && (cmd.textMsg.text == null)) {
+                    CatLog.d(this, "cmd " + cmdParams.getCommandType() + " with null alpha id");
+                    // If alpha length is zero, we just respond with OK.
+                    if (isProactiveCmd) {
+                        sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+                    }
+                    return;
+                }
+                // Respond with permanent failure to avoid retry if STK app is not present.
+                if (!mStkAppInstalled) {
+                    CatLog.d(this, "No STK application found.");
+                    if (isProactiveCmd) {
+                        sendTerminalResponse(cmdParams.cmdDet,
+                                             ResultCode.BEYOND_TERMINAL_CAPABILITY,
+                                             false, 0, null);
+                        return;
+                    }
+                }
+                /*
+                 * CLOSE_CHANNEL, RECEIVE_DATA and SEND_DATA can be delivered by
+                 * either PROACTIVE_COMMAND or EVENT_NOTIFY.
+                 * If PROACTIVE_COMMAND is used for those commands, send terminal
+                 * response here.
+                 */
+                if (isProactiveCmd &&
+                    ((cmdParams.getCommandType() == CommandType.CLOSE_CHANNEL) ||
+                     (cmdParams.getCommandType() == CommandType.RECEIVE_DATA) ||
+                     (cmdParams.getCommandType() == CommandType.SEND_DATA))) {
+                    sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+                }
+                break;
             default:
                 CatLog.d(this, "Unsupported command");
                 return;
@@ -684,6 +730,7 @@
         case NO_RESPONSE_FROM_USER:
         case UICC_SESSION_TERM_BY_USER:
         case BACKWARD_MOVE_BY_USER:
+        case USER_NOT_ACCEPT:
             resp = null;
             break;
         default:
@@ -692,4 +739,14 @@
         sendTerminalResponse(cmdDet, resMsg.resCode, false, 0, resp);
         mCurrntCmd = null;
     }
+
+    private boolean isStkAppInstalled() {
+        Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> broadcastReceivers =
+                            pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA);
+        int numReceiver = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+
+        return (numReceiver > 0);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cat/CommandParams.java b/telephony/java/com/android/internal/telephony/cat/CommandParams.java
index 22a5c8c..959c9e2 100644
--- a/telephony/java/com/android/internal/telephony/cat/CommandParams.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandParams.java
@@ -166,4 +166,29 @@
     }
 }
 
+/*
+ * BIP (Bearer Independent Protocol) is the mechanism for SIM card applications
+ * to access data connection through the mobile device.
+ *
+ * SIM utilizes proactive commands (OPEN CHANNEL, CLOSE CHANNEL, SEND DATA and
+ * RECEIVE DATA to control/read/write data for BIP. Refer to ETSI TS 102 223 for
+ * the details of proactive commands procedures and their structures.
+ */
+class BIPClientParams extends CommandParams {
+    TextMessage textMsg;
+    boolean bHasAlphaId;
 
+    BIPClientParams(CommandDetails cmdDet, TextMessage textMsg, boolean has_alpha_id) {
+        super(cmdDet);
+        this.textMsg = textMsg;
+        this.bHasAlphaId = has_alpha_id;
+    }
+
+    boolean setIcon(Bitmap icon) {
+        if (icon != null && textMsg != null) {
+            textMsg.icon = icon;
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index e7fca5a..89c1329 100644
--- a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -165,6 +165,12 @@
              case PROVIDE_LOCAL_INFORMATION:
                 cmdPending = processProvideLocalInfo(cmdDet, ctlvs);
                 break;
+             case OPEN_CHANNEL:
+             case CLOSE_CHANNEL:
+             case RECEIVE_DATA:
+             case SEND_DATA:
+                 cmdPending = processBIPClient(cmdDet, ctlvs);
+                 break;
             default:
                 // unsupported proactive commands
                 mCmdParams = new CommandParams(cmdDet);
@@ -893,4 +899,43 @@
         }
         return false;
     }
+
+    private boolean processBIPClient(CommandDetails cmdDet,
+                                     List<ComprehensionTlv> ctlvs) throws ResultException {
+        AppInterface.CommandType commandType =
+                                    AppInterface.CommandType.fromInt(cmdDet.typeOfCommand);
+        if (commandType != null) {
+            CatLog.d(this, "process "+ commandType.name());
+        }
+
+        TextMessage textMsg = new TextMessage();
+        IconId iconId = null;
+        ComprehensionTlv ctlv = null;
+        boolean has_alpha_id = false;
+
+        // parse alpha identifier
+        ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs);
+        if (ctlv != null) {
+            textMsg.text = ValueParser.retrieveAlphaId(ctlv);
+            CatLog.d(this, "alpha TLV text=" + textMsg.text);
+            has_alpha_id = true;
+        }
+
+        // parse icon identifier
+        ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
+        if (ctlv != null) {
+            iconId = ValueParser.retrieveIconId(ctlv);
+            textMsg.iconSelfExplanatory = iconId.selfExplanatory;
+        }
+
+        textMsg.responseNeeded = false;
+        mCmdParams = new BIPClientParams(cmdDet, textMsg, has_alpha_id);
+
+        if (iconId != null) {
+            mIconLoadState = LOAD_SINGLE_ICON;
+            mIconLoader.loadIcon(iconId.recordNumber, this.obtainMessage(MSG_ID_LOAD_ICON_DONE));
+            return true;
+        }
+        return false;
+    }
 }