Adding support for SIM communication from Android over logical channels.

Implement the following new functions defined in ITelephony.aidl.
    - iccOpenLogicalChannel
    - iccCloseLogicalChannel
    - iccTransmitApduLogicalChannel
The new calls are protected by the SIM_COMMUNICATION permission.

Change-Id: I89c08adc6f9738907e3b547c749f3cc61f300710
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 82b5e9f..338c640 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -38,13 +38,16 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.telephony.CallManager;
+import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.DefaultPhoneNotifier;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.uicc.IccIoResult;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.UiccController;
 
 import java.util.List;
 import java.util.ArrayList;
@@ -64,6 +67,13 @@
     private static final int CMD_ANSWER_RINGING_CALL = 4;
     private static final int CMD_END_CALL = 5;  // not used yet
     private static final int CMD_SILENCE_RINGER = 6;
+    private static final int CMD_TRANSMIT_APDU = 7;
+    private static final int EVENT_TRANSMIT_APDU_DONE = 8;
+    private static final int CMD_OPEN_CHANNEL = 9;
+    private static final int EVENT_OPEN_CHANNEL_DONE = 10;
+    private static final int CMD_CLOSE_CHANNEL = 11;
+    private static final int EVENT_CLOSE_CHANNEL_DONE = 12;
+
 
     /** The singleton instance. */
     private static PhoneInterfaceManager sInstance;
@@ -76,6 +86,25 @@
     CallHandlerServiceProxy mCallHandlerService;
 
     /**
+     * A request object to use for transmitting data to an ICC.
+     */
+    private static final class IccAPDUArgument {
+        public int channel, cla, command, p1, p2, p3;
+        public String data;
+
+        public IccAPDUArgument(int channel, int cla, int command,
+                int p1, int p2, int p3, String data) {
+            this.channel = channel;
+            this.cla = cla;
+            this.command = command;
+            this.p1 = p1;
+            this.p2 = p2;
+            this.p3 = p3;
+            this.data = data;
+        }
+    }
+
+    /**
      * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
      * request after sending. The main thread will notify the request when it is complete.
      */
@@ -172,6 +201,95 @@
                     }
                     break;
 
+                case CMD_TRANSMIT_APDU:
+                    request = (MainThreadRequest) msg.obj;
+                    IccAPDUArgument argument = (IccAPDUArgument) request.argument;
+                    onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
+                    UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
+                            argument.channel, argument.cla, argument.command,
+                            argument.p1, argument.p2, argument.p3, argument.data,
+                            onCompleted);
+                    break;
+
+                case EVENT_TRANSMIT_APDU_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null && ar.result != null) {
+                        request.result = ar.result;
+                    } else {
+                        request.result = new IccIoResult(0x6F, 0, (byte[])null);
+                        if (ar.result == null) {
+                            loge("iccTransmitApduLogicalChannel: Empty response");
+                        } else if (ar.exception != null &&
+                            (ar.exception instanceof CommandException)) {
+                            loge("iccTransmitApduLogicalChannel: CommandException: " +
+                                    (CommandException)ar.exception);
+                        } else {
+                            loge("iccTransmitApduLogicalChannel: Unknown exception");
+                        }
+                    }
+                    synchronized (request) {
+                        request.notifyAll();
+                    }
+                    break;
+
+                case CMD_OPEN_CHANNEL:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
+                    UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
+                            (String)request.argument, onCompleted);
+                    break;
+
+                case EVENT_OPEN_CHANNEL_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null && ar.result != null) {
+                        request.result = new Integer(((int[])ar.result)[0]);
+                    } else {
+                        request.result = new Integer(-1);
+                        if (ar.result == null) {
+                            loge("iccOpenLogicalChannel: Empty response");
+                        } else if (ar.exception != null &&
+                            (ar.exception instanceof CommandException)) {
+                            loge("iccOpenLogicalChannel: CommandException: " +
+                                    (CommandException)ar.exception);
+                        } else {
+                            loge("iccOpenLogicalChannel: Unknown exception");
+                        }
+                    }
+                    synchronized (request) {
+                        request.notifyAll();
+                    }
+                    break;
+
+                case CMD_CLOSE_CHANNEL:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
+                            request);
+                    UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
+                            ((Integer)request.argument).intValue(),
+                            onCompleted);
+                    break;
+
+                case EVENT_CLOSE_CHANNEL_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null) {
+                        request.result = new Boolean(true);
+                    } else {
+                        request.result = new Boolean(false);
+                        if (ar.exception instanceof CommandException) {
+                            loge("iccCloseLogicalChannel: CommandException: " +
+                                    (CommandException)ar.exception);
+                        } else {
+                            loge("iccCloseLogicalChannel: Unknown exception");
+                        }
+                    }
+                    synchronized (request) {
+                        request.notifyAll();
+                    }
+                    break;
+
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -694,7 +812,7 @@
                 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
                         CMD_HANDLE_NEIGHBORING_CELL, null);
             } catch (RuntimeException e) {
-                Log.e(LOG_TAG, "getNeighboringCellInfo " + e);
+                loge("getNeighboringCellInfo " + e);
             }
             return cells;
         } else {
@@ -793,6 +911,14 @@
         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
     }
 
+    /**
+     * Make sure the caller has SIM_COMMUNICATION permission.
+     *
+     * @throws SecurityException if the caller does not have the required permission.
+     */
+    private void enforceSimCommunicationPermission() {
+        mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
+    }
 
     private String createTelUrl(String number) {
         if (TextUtils.isEmpty(number)) {
@@ -897,4 +1023,60 @@
     public int getLteOnCdmaMode() {
         return mPhone.getLteOnCdmaMode();
     }
+
+    @Override
+    public int iccOpenLogicalChannel(String AID) {
+        enforceSimCommunicationPermission();
+
+        if (DBG) log("iccOpenLogicalChannel: " + AID);
+        Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
+        if (DBG) log("iccOpenLogicalChannel: " + channel);
+        return channel.intValue();
+    }
+
+    @Override
+    public boolean iccCloseLogicalChannel(int channel) {
+        enforceSimCommunicationPermission();
+
+        if (DBG) log("iccCloseLogicalChannel: " + channel);
+        if (channel < 0) {
+          return false;
+        }
+        Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL,
+            new Integer(channel));
+        if (DBG) log("iccCloseLogicalChannel: " + success);
+        return success;
+    }
+
+    @Override
+    public String iccTransmitApduLogicalChannel(int channel, int cla,
+            int command, int p1, int p2, int p3, String data) {
+        enforceSimCommunicationPermission();
+
+        if (DBG) {
+            log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
+                    " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
+                    " data=" + data);
+        }
+
+        if (channel < 0) {
+            return "";
+        }
+
+        IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
+                new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
+        if (DBG) log("iccTransmitApduLogicalChannel: " + response);
+
+        // If the payload is null, there was an error. Indicate that by returning
+        // an empty string.
+        if (response.payload == null) {
+          return "";
+        }
+
+        // Append the returned status code to the end of the response payload.
+        String s = Integer.toHexString(
+                (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
+        s = IccUtils.bytesToHexString(response.payload) + s;
+        return s;
+    }
 }