Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67d903ab authored by Jun Yin's avatar Jun Yin
Browse files

AsyncMessageInvocation and implementations

AsyncMessageInvocation wraps an invocation to an asynchronous method
using Message to be working with AsyncResultCallback. This CL adds
three implementations for opening logical channel, transmitting APDU
via logical channel, and closing logical channel on the async methods of
CommandsInterface.

Bug: 38206971
Test: unit test on the ApduSender CL of the same topic
Change-Id: I0621a0e8b1f4d5e1eb6409427ec814d0dee09de9
parent ce0d687b
Loading
Loading
Loading
Loading
+62 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony.uicc.euicc.apdu;

/**
 * Parts of an APDU command.
 *
 * @hide
 */
class ApduCommand {
    /** Channel of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
    public final int channel;

    /** Class of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
    public final int cla;

    /** Instruction of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
    public final int ins;

    /** Parameter 1 of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
    public final int p1;

    /** Parameter 2 of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
    public final int p2;

    /** Parameter 3 of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
    public final int p3;

    /** Command data of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
    public final String cmdHex;

    /** The parameters are defined as in GlobalPlatform Card Specification v.2.3. */
    ApduCommand(int channel, int cla, int ins, int p1, int p2, int p3, String cmdHex) {
        this.channel = channel;
        this.cla = cla;
        this.ins = ins;
        this.p1 = p1;
        this.p2 = p2;
        this.p3 = p3;
        this.cmdHex = cmdHex;
    }

    @Override
    public String toString() {
        return "ApduCommand(channel=" + channel + ", cla=" + cla + ", ins=" + ins + ", p1=" + p1
                + ", p2=" + p2 + ", p3=" + p3 + ", cmd=" + cmdHex + ")";
    }
}
+61 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony.uicc.euicc.apdu;

import android.os.AsyncResult;
import android.os.Message;
import android.telephony.Rlog;

import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.euicc.async.AsyncMessageInvocation;

/**
 * Invokes {@link CommandsInterface#iccCloseLogicalChannel(int, Message)}. This takes a channel id
 * (Integer) as the input and return a boolean to indicate if closing the logical channel is
 * succeeded. No exception will be returned to the result callback.
 *
 * @hide
 */
class CloseLogicalChannelInvocation extends AsyncMessageInvocation<Integer, Boolean> {
    private static final String LOG_TAG = "CloseChan";

    private final CommandsInterface mCi;

    CloseLogicalChannelInvocation(CommandsInterface ci) {
        mCi = ci;
    }

    @Override
    protected void sendRequestMessage(Integer channel, Message msg) {
        Rlog.v(LOG_TAG, "Channel: " + channel);
        mCi.iccCloseLogicalChannel(channel, msg);
    }

    @Override
    protected Boolean parseResult(AsyncResult ar) {
        if (ar.exception == null) {
            return true;
        }
        if (ar.exception instanceof CommandException) {
            Rlog.e(LOG_TAG, "CommandException", ar.exception);
        } else {
            Rlog.e(LOG_TAG, "Unknown exception", ar.exception);
        }
        return false;
    }
}
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony.uicc.euicc.apdu;

import android.os.AsyncResult;
import android.os.Message;
import android.telephony.IccOpenLogicalChannelResponse;
import android.telephony.Rlog;

import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.euicc.async.AsyncMessageInvocation;

/**
 * Invokes {@link CommandsInterface#iccOpenLogicalChannel(String, int, Message)}. This takes AID
 * (String) as the input and return the response of opening a logical channel. Error will be
 * included in the {@link IccOpenLogicalChannelResponse} result and no exception will be returned to
 * the result callback.
 *
 * @hide
 */
class OpenLogicalChannelInvocation
        extends AsyncMessageInvocation<String, IccOpenLogicalChannelResponse> {
    private static final String LOG_TAG = "OpenChan";

    private final CommandsInterface mCi;

    OpenLogicalChannelInvocation(CommandsInterface ci) {
        mCi = ci;
    }

    @Override
    protected void sendRequestMessage(String aid, Message msg) {
        mCi.iccOpenLogicalChannel(aid, 0, msg);
    }

    @Override
    protected IccOpenLogicalChannelResponse parseResult(AsyncResult ar) {
        IccOpenLogicalChannelResponse openChannelResp;
        // The code below is copied from PhoneInterfaceManager.java.
        // TODO: move this code into IccOpenLogicalChannelResponse so that it can be shared.
        if (ar.exception == null && ar.result != null) {
            int[] result = (int[]) ar.result;
            int channel = result[0];
            byte[] selectResponse = null;
            if (result.length > 1) {
                selectResponse = new byte[result.length - 1];
                for (int i = 1; i < result.length; ++i) {
                    selectResponse[i - 1] = (byte) result[i];
                }
            }
            openChannelResp = new IccOpenLogicalChannelResponse(
                    channel, IccOpenLogicalChannelResponse.STATUS_NO_ERROR, selectResponse);
        } else {
            if (ar.result == null) {
                Rlog.e(LOG_TAG, "Empty response");
            }
            if (ar.exception != null) {
                Rlog.e(LOG_TAG, "Exception", ar.exception);
            }

            int errorCode = IccOpenLogicalChannelResponse.STATUS_UNKNOWN_ERROR;
            if (ar.exception instanceof CommandException) {
                CommandException.Error error =
                        ((CommandException) (ar.exception)).getCommandError();
                if (error == CommandException.Error.MISSING_RESOURCE) {
                    errorCode = IccOpenLogicalChannelResponse.STATUS_MISSING_RESOURCE;
                } else if (error == CommandException.Error.NO_SUCH_ELEMENT) {
                    errorCode = IccOpenLogicalChannelResponse.STATUS_NO_SUCH_ELEMENT;
                }
            }
            openChannelResp = new IccOpenLogicalChannelResponse(
                    IccOpenLogicalChannelResponse.INVALID_CHANNEL, errorCode, null);
        }

        Rlog.v(LOG_TAG, "Response: " + openChannelResp);
        return openChannelResp;
    }
}
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony.uicc.euicc.apdu;

import android.os.AsyncResult;
import android.os.Message;
import android.telephony.Rlog;

import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.IccIoResult;
import com.android.internal.telephony.uicc.euicc.async.AsyncMessageInvocation;

/**
 * Invokes {@link CommandsInterface#iccTransmitApduLogicalChannel(int, int, int, int, int, int,
 * String, Message)}. This takes an APDU command as the input and return the response. The status of
 * returned response will be 0x6F00 if any error happens. No exception will be returned to the
 * result callback.
 *
 * @hide
 */
public class TransmitApduLogicalChannelInvocation
        extends AsyncMessageInvocation<ApduCommand, IccIoResult> {
    private static final String LOG_TAG = "TransApdu";
    private static final int SW1_ERROR = 0x6F;

    private final CommandsInterface mCi;

    TransmitApduLogicalChannelInvocation(CommandsInterface ci) {
        mCi = ci;
    }

    @Override
    protected void sendRequestMessage(ApduCommand command, Message msg) {
        Rlog.v(LOG_TAG, "Send: " + command);
        mCi.iccTransmitApduLogicalChannel(command.channel, command.cla | command.channel,
                command.ins, command.p1, command.p2, command.p3, command.cmdHex, msg);
    }

    @Override
    protected IccIoResult parseResult(AsyncResult ar) {
        IccIoResult response;
        if (ar.exception == null && ar.result != null) {
            response  = (IccIoResult) ar.result;
        } else {
            if (ar.result == null) {
                Rlog.e(LOG_TAG, "Empty response");
            } else if (ar.exception instanceof CommandException) {
                Rlog.e(LOG_TAG,  "CommandException", ar.exception);
            } else {
                Rlog.e(LOG_TAG,  "CommandException", ar.exception);
            }
            response = new IccIoResult(SW1_ERROR, 0 /* sw2 */, (byte[]) null /* payload */);
        }

        Rlog.v(LOG_TAG, "Response: " + response);
        return response;
    }
}
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony.uicc.euicc.async;

import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;

/**
 * This class wraps an invocation to an asynchronous method using {@link Message} to be working with
 * {@link AsyncResultCallback}. With this class, you can use callbacks instead of managing a state
 * machine to complete a task relying on multiple asynchronous method calls.
 *
 * <p>Subclasses should override the abstract methods to invoke the actual asynchronous method and
 * parse the returned result.
 *
 * @param <Request> Class of the request data.
 * @param <Response> Class of the response data.
 *
 * @hide
 */
public abstract class AsyncMessageInvocation<Request, Response> implements Handler.Callback {
    /**
     * Executes an invocation.
     *
     * @param request The request to be sent with the invocation.
     * @param resultCallback Will be called after result is returned.
     * @param handler The handler that {@code resultCallback} will be executed on.
     */
    public final void invoke(
            Request request, AsyncResultCallback<Response> resultCallback, Handler handler) {
        Handler h = new Handler(handler.getLooper(), this);
        sendRequestMessage(request, h.obtainMessage(0, resultCallback));
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean handleMessage(Message msg) {
        AsyncResult result = (AsyncResult) msg.obj;
        AsyncResultCallback<Response> resultCallback =
                (AsyncResultCallback<Response>) result.userObj;
        try {
            resultCallback.onResult(parseResult(result));
        } catch (Throwable t) {
            resultCallback.onException(t);
        }
        return true;
    }

    /**
     * Calls the asynchronous method with the given {@code msg}. The implementation should convert
     * the given {@code request} to the parameters of the asynchronous method.
     */
    protected abstract void sendRequestMessage(Request request, Message msg);

    /** Parses the asynchronous result returned by the method to a {@link Response}. */
    protected abstract Response parseResult(AsyncResult result) throws Throwable;
}