1388fa40468c01b8959ae4bac688241b6d362820fJun Yin/*
2388fa40468c01b8959ae4bac688241b6d362820fJun Yin * Copyright (C) 2018 The Android Open Source Project
3388fa40468c01b8959ae4bac688241b6d362820fJun Yin *
4388fa40468c01b8959ae4bac688241b6d362820fJun Yin * Licensed under the Apache License, Version 2.0 (the "License");
5388fa40468c01b8959ae4bac688241b6d362820fJun Yin * you may not use this file except in compliance with the License.
6388fa40468c01b8959ae4bac688241b6d362820fJun Yin * You may obtain a copy of the License at
7388fa40468c01b8959ae4bac688241b6d362820fJun Yin *
8388fa40468c01b8959ae4bac688241b6d362820fJun Yin *      http://www.apache.org/licenses/LICENSE-2.0
9388fa40468c01b8959ae4bac688241b6d362820fJun Yin *
10388fa40468c01b8959ae4bac688241b6d362820fJun Yin * Unless required by applicable law or agreed to in writing, software
11388fa40468c01b8959ae4bac688241b6d362820fJun Yin * distributed under the License is distributed on an "AS IS" BASIS,
12388fa40468c01b8959ae4bac688241b6d362820fJun Yin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13388fa40468c01b8959ae4bac688241b6d362820fJun Yin * See the License for the specific language governing permissions and
14388fa40468c01b8959ae4bac688241b6d362820fJun Yin * limitations under the License.
15388fa40468c01b8959ae4bac688241b6d362820fJun Yin */
16388fa40468c01b8959ae4bac688241b6d362820fJun Yin
17388fa40468c01b8959ae4bac688241b6d362820fJun Yinpackage com.android.internal.telephony.uicc.euicc.apdu;
18388fa40468c01b8959ae4bac688241b6d362820fJun Yin
19388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport android.annotation.Nullable;
20388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport android.os.Handler;
21388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport android.telephony.IccOpenLogicalChannelResponse;
22388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport android.telephony.Rlog;
23388fa40468c01b8959ae4bac688241b6d362820fJun Yin
24388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport com.android.internal.telephony.CommandsInterface;
25388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport com.android.internal.telephony.uicc.IccIoResult;
26388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
27388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport com.android.internal.telephony.uicc.euicc.async.AsyncResultHelper;
28388fa40468c01b8959ae4bac688241b6d362820fJun Yin
29388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport java.io.ByteArrayOutputStream;
30388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport java.io.IOException;
31388fa40468c01b8959ae4bac688241b6d362820fJun Yinimport java.util.List;
32388fa40468c01b8959ae4bac688241b6d362820fJun Yin
33388fa40468c01b8959ae4bac688241b6d362820fJun Yin/**
34388fa40468c01b8959ae4bac688241b6d362820fJun Yin * This class sends a list of APDU commands to an AID on a UICC. A logical channel will be opened
35388fa40468c01b8959ae4bac688241b6d362820fJun Yin * before sending and closed after all APDU commands are sent. The complete response of the last
36388fa40468c01b8959ae4bac688241b6d362820fJun Yin * APDU command will be returned. If any APDU command returns an error status (other than
37388fa40468c01b8959ae4bac688241b6d362820fJun Yin * {@link #STATUS_NO_ERROR}) or causing an exception, an {@link ApduException} will be returned
38388fa40468c01b8959ae4bac688241b6d362820fJun Yin * immediately without sending the rest of commands. This class is thread-safe.
39388fa40468c01b8959ae4bac688241b6d362820fJun Yin *
40388fa40468c01b8959ae4bac688241b6d362820fJun Yin * @hide
41388fa40468c01b8959ae4bac688241b6d362820fJun Yin */
42388fa40468c01b8959ae4bac688241b6d362820fJun Yinpublic class ApduSender {
43388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private static final String LOG_TAG = "ApduSender";
44388fa40468c01b8959ae4bac688241b6d362820fJun Yin
45388fa40468c01b8959ae4bac688241b6d362820fJun Yin    // Parameter and response used by the command to get extra responses of an APDU command.
46388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private static final int INS_GET_MORE_RESPONSE = 0xC0;
47388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private static final int SW1_MORE_RESPONSE = 0x61;
48388fa40468c01b8959ae4bac688241b6d362820fJun Yin
49388fa40468c01b8959ae4bac688241b6d362820fJun Yin    // Status code of APDU response
50388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private static final int STATUS_NO_ERROR = 0x9000;
51388fa40468c01b8959ae4bac688241b6d362820fJun Yin
52388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private static void logv(String msg) {
53388fa40468c01b8959ae4bac688241b6d362820fJun Yin        Rlog.v(LOG_TAG, msg);
54388fa40468c01b8959ae4bac688241b6d362820fJun Yin    }
55388fa40468c01b8959ae4bac688241b6d362820fJun Yin
56388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private final String mAid;
57388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private final boolean mSupportExtendedApdu;
58388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private final OpenLogicalChannelInvocation mOpenChannel;
59388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private final CloseLogicalChannelInvocation mCloseChannel;
60388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private final TransmitApduLogicalChannelInvocation mTransmitApdu;
61388fa40468c01b8959ae4bac688241b6d362820fJun Yin
62388fa40468c01b8959ae4bac688241b6d362820fJun Yin    // Lock for accessing mChannelOpened. We only allow to open a single logical channel at any
63388fa40468c01b8959ae4bac688241b6d362820fJun Yin    // time for an AID.
64388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private final Object mChannelLock = new Object();
65388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private boolean mChannelOpened;
66388fa40468c01b8959ae4bac688241b6d362820fJun Yin
67388fa40468c01b8959ae4bac688241b6d362820fJun Yin    /**
68388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param aid The AID that will be used to open a logical channel to.
69388fa40468c01b8959ae4bac688241b6d362820fJun Yin     */
70388fa40468c01b8959ae4bac688241b6d362820fJun Yin    public ApduSender(CommandsInterface ci, String aid, boolean supportExtendedApdu) {
71388fa40468c01b8959ae4bac688241b6d362820fJun Yin        mAid = aid;
72388fa40468c01b8959ae4bac688241b6d362820fJun Yin        mSupportExtendedApdu = supportExtendedApdu;
73388fa40468c01b8959ae4bac688241b6d362820fJun Yin        mOpenChannel = new OpenLogicalChannelInvocation(ci);
74388fa40468c01b8959ae4bac688241b6d362820fJun Yin        mCloseChannel = new CloseLogicalChannelInvocation(ci);
75388fa40468c01b8959ae4bac688241b6d362820fJun Yin        mTransmitApdu = new TransmitApduLogicalChannelInvocation(ci);
76388fa40468c01b8959ae4bac688241b6d362820fJun Yin    }
77388fa40468c01b8959ae4bac688241b6d362820fJun Yin
78388fa40468c01b8959ae4bac688241b6d362820fJun Yin    /**
79388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * Sends APDU commands.
80388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *
81388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param requestProvider Will be called after a logical channel is opened successfully. This is
82388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *     in charge of building a request with all APDU commands to be sent. This won't be called
83388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *     if any error happens when opening a logical channel.
84388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param resultCallback Will be called after an error or the last APDU command has been
85388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *     executed. The result will be the full response of the last APDU command. Error will be
86388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *     returned as an {@link ApduException} exception.
87388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param handler The handler that {@code requestProvider} and {@code resultCallback} will be
88388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *     executed on.
89388fa40468c01b8959ae4bac688241b6d362820fJun Yin     */
90388fa40468c01b8959ae4bac688241b6d362820fJun Yin    public void send(
91388fa40468c01b8959ae4bac688241b6d362820fJun Yin            RequestProvider requestProvider,
92388fa40468c01b8959ae4bac688241b6d362820fJun Yin            AsyncResultCallback<byte[]> resultCallback,
93388fa40468c01b8959ae4bac688241b6d362820fJun Yin            Handler handler) {
94388fa40468c01b8959ae4bac688241b6d362820fJun Yin        synchronized (mChannelLock) {
95388fa40468c01b8959ae4bac688241b6d362820fJun Yin            if (mChannelOpened) {
96388fa40468c01b8959ae4bac688241b6d362820fJun Yin                AsyncResultHelper.throwException(
97388fa40468c01b8959ae4bac688241b6d362820fJun Yin                        new ApduException("Logical channel has already been opened."),
98388fa40468c01b8959ae4bac688241b6d362820fJun Yin                        resultCallback, handler);
99388fa40468c01b8959ae4bac688241b6d362820fJun Yin                return;
100388fa40468c01b8959ae4bac688241b6d362820fJun Yin            }
101388fa40468c01b8959ae4bac688241b6d362820fJun Yin            mChannelOpened = true;
102388fa40468c01b8959ae4bac688241b6d362820fJun Yin        }
103388fa40468c01b8959ae4bac688241b6d362820fJun Yin
104388fa40468c01b8959ae4bac688241b6d362820fJun Yin        mOpenChannel.invoke(mAid, new AsyncResultCallback<IccOpenLogicalChannelResponse>() {
105388fa40468c01b8959ae4bac688241b6d362820fJun Yin            @Override
106388fa40468c01b8959ae4bac688241b6d362820fJun Yin            public void onResult(IccOpenLogicalChannelResponse openChannelResponse) {
107388fa40468c01b8959ae4bac688241b6d362820fJun Yin                int channel = openChannelResponse.getChannel();
108388fa40468c01b8959ae4bac688241b6d362820fJun Yin                int status = openChannelResponse.getStatus();
109388fa40468c01b8959ae4bac688241b6d362820fJun Yin                if (channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL
110388fa40468c01b8959ae4bac688241b6d362820fJun Yin                        || status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR) {
111388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    synchronized (mChannelLock) {
112388fa40468c01b8959ae4bac688241b6d362820fJun Yin                        mChannelOpened = false;
113388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    }
114388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    resultCallback.onException(
115388fa40468c01b8959ae4bac688241b6d362820fJun Yin                            new ApduException("Failed to open logical channel opened for AID: "
116388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                    + mAid + ", with status: " + status));
117388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    return;
118388fa40468c01b8959ae4bac688241b6d362820fJun Yin                }
119388fa40468c01b8959ae4bac688241b6d362820fJun Yin
120388fa40468c01b8959ae4bac688241b6d362820fJun Yin                RequestBuilder builder = new RequestBuilder(channel, mSupportExtendedApdu);
121388fa40468c01b8959ae4bac688241b6d362820fJun Yin                Throwable requestException = null;
122388fa40468c01b8959ae4bac688241b6d362820fJun Yin                try {
123388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    requestProvider.buildRequest(openChannelResponse.getSelectResponse(), builder);
124388fa40468c01b8959ae4bac688241b6d362820fJun Yin                } catch (Throwable e) {
125388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    requestException = e;
126388fa40468c01b8959ae4bac688241b6d362820fJun Yin                }
127388fa40468c01b8959ae4bac688241b6d362820fJun Yin                if (builder.getCommands().isEmpty() || requestException != null) {
128388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    // Just close the channel if we don't have commands to send or an error
129388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    // was encountered.
130388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    closeAndReturn(channel, null /* response */, requestException, resultCallback,
131388fa40468c01b8959ae4bac688241b6d362820fJun Yin                            handler);
132388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    return;
133388fa40468c01b8959ae4bac688241b6d362820fJun Yin                }
134388fa40468c01b8959ae4bac688241b6d362820fJun Yin                sendCommand(builder.getCommands(), 0 /* index */, resultCallback, handler);
135388fa40468c01b8959ae4bac688241b6d362820fJun Yin            }
136388fa40468c01b8959ae4bac688241b6d362820fJun Yin        }, handler);
137388fa40468c01b8959ae4bac688241b6d362820fJun Yin    }
138388fa40468c01b8959ae4bac688241b6d362820fJun Yin
139388fa40468c01b8959ae4bac688241b6d362820fJun Yin    /**
140388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * Sends the current command and then continue to send the next one. If this is the last
141388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * command or any error happens, {@code resultCallback} will be called.
142388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *
143388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param commands All commands to be sent.
144388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param index The current command index.
145388fa40468c01b8959ae4bac688241b6d362820fJun Yin     */
146388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private void sendCommand(
147388fa40468c01b8959ae4bac688241b6d362820fJun Yin            List<ApduCommand> commands,
148388fa40468c01b8959ae4bac688241b6d362820fJun Yin            int index,
149388fa40468c01b8959ae4bac688241b6d362820fJun Yin            AsyncResultCallback<byte[]> resultCallback,
150388fa40468c01b8959ae4bac688241b6d362820fJun Yin            Handler handler) {
151388fa40468c01b8959ae4bac688241b6d362820fJun Yin        ApduCommand command = commands.get(index);
152388fa40468c01b8959ae4bac688241b6d362820fJun Yin        mTransmitApdu.invoke(command, new AsyncResultCallback<IccIoResult>() {
153388fa40468c01b8959ae4bac688241b6d362820fJun Yin            @Override
154388fa40468c01b8959ae4bac688241b6d362820fJun Yin            public void onResult(IccIoResult response) {
155388fa40468c01b8959ae4bac688241b6d362820fJun Yin                // A long response may need to be fetched by multiple following-up APDU
156388fa40468c01b8959ae4bac688241b6d362820fJun Yin                // commands. Makes sure that we get the complete response.
157388fa40468c01b8959ae4bac688241b6d362820fJun Yin                getCompleteResponse(command.channel, response, null /* responseBuilder */,
158388fa40468c01b8959ae4bac688241b6d362820fJun Yin                        new AsyncResultCallback<IccIoResult>() {
159388fa40468c01b8959ae4bac688241b6d362820fJun Yin                            @Override
160388fa40468c01b8959ae4bac688241b6d362820fJun Yin                            public void onResult(IccIoResult fullResponse) {
161388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                logv("Full APDU response: " + fullResponse);
162388fa40468c01b8959ae4bac688241b6d362820fJun Yin
163388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                int status = (fullResponse.sw1 << 8) | fullResponse.sw2;
164388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                if (status != STATUS_NO_ERROR) {
165388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                    closeAndReturn(command.channel, null /* response */,
166388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                            new ApduException(status), resultCallback, handler);
167388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                    return;
168388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                }
169388fa40468c01b8959ae4bac688241b6d362820fJun Yin
170388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                // Last command
171388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                if (index == commands.size() - 1) {
172388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                    closeAndReturn(command.channel, fullResponse.payload,
173388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                            null /* exception */, resultCallback, handler);
174388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                    return;
175388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                }
176388fa40468c01b8959ae4bac688241b6d362820fJun Yin
177388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                // Sends the next command
178388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                sendCommand(commands, index + 1, resultCallback, handler);
179388fa40468c01b8959ae4bac688241b6d362820fJun Yin                            }
180388fa40468c01b8959ae4bac688241b6d362820fJun Yin                        }, handler);
181388fa40468c01b8959ae4bac688241b6d362820fJun Yin            }
182388fa40468c01b8959ae4bac688241b6d362820fJun Yin        }, handler);
183388fa40468c01b8959ae4bac688241b6d362820fJun Yin    }
184388fa40468c01b8959ae4bac688241b6d362820fJun Yin
185388fa40468c01b8959ae4bac688241b6d362820fJun Yin    /**
186388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * Gets the full response.
187388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *
188388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param lastResponse Will be checked to see if we need to fetch more.
189388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param responseBuilder For continuously building the full response. It should not contain the
190388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *     last response. If it's null, a new builder will be created.
191388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param resultCallback Error will be included in the result and no exception will be returned.
192388fa40468c01b8959ae4bac688241b6d362820fJun Yin     */
193388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private void getCompleteResponse(
194388fa40468c01b8959ae4bac688241b6d362820fJun Yin            int channel,
195388fa40468c01b8959ae4bac688241b6d362820fJun Yin            IccIoResult lastResponse,
196388fa40468c01b8959ae4bac688241b6d362820fJun Yin            @Nullable ByteArrayOutputStream responseBuilder,
197388fa40468c01b8959ae4bac688241b6d362820fJun Yin            AsyncResultCallback<IccIoResult> resultCallback,
198388fa40468c01b8959ae4bac688241b6d362820fJun Yin            Handler handler) {
199388fa40468c01b8959ae4bac688241b6d362820fJun Yin        ByteArrayOutputStream resultBuilder =
200388fa40468c01b8959ae4bac688241b6d362820fJun Yin                responseBuilder == null ? new ByteArrayOutputStream() : responseBuilder;
201d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun        if (lastResponse.payload != null) {
202d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun            try {
203d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun                resultBuilder.write(lastResponse.payload);
204d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun            } catch (IOException e) {
205d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun                // Should never reach here.
206d687148b8a2627ba49296e28d38580697940c5e7Holly Jiuyu Sun            }
207388fa40468c01b8959ae4bac688241b6d362820fJun Yin        }
208388fa40468c01b8959ae4bac688241b6d362820fJun Yin        if (lastResponse.sw1 != SW1_MORE_RESPONSE) {
209388fa40468c01b8959ae4bac688241b6d362820fJun Yin            lastResponse.payload = resultBuilder.toByteArray();
210388fa40468c01b8959ae4bac688241b6d362820fJun Yin            resultCallback.onResult(lastResponse);
211388fa40468c01b8959ae4bac688241b6d362820fJun Yin            return;
212388fa40468c01b8959ae4bac688241b6d362820fJun Yin        }
213388fa40468c01b8959ae4bac688241b6d362820fJun Yin
214388fa40468c01b8959ae4bac688241b6d362820fJun Yin        mTransmitApdu.invoke(
215388fa40468c01b8959ae4bac688241b6d362820fJun Yin                new ApduCommand(channel, 0 /* cls  */, INS_GET_MORE_RESPONSE, 0 /* p1 */,
216388fa40468c01b8959ae4bac688241b6d362820fJun Yin                        0 /* p2 */, lastResponse.sw2, "" /* cmdHex */),
217388fa40468c01b8959ae4bac688241b6d362820fJun Yin                new AsyncResultCallback<IccIoResult>() {
218388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    @Override
219388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    public void onResult(IccIoResult response) {
220388fa40468c01b8959ae4bac688241b6d362820fJun Yin                        getCompleteResponse(
221388fa40468c01b8959ae4bac688241b6d362820fJun Yin                                channel, response, resultBuilder, resultCallback, handler);
222388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    }
223388fa40468c01b8959ae4bac688241b6d362820fJun Yin                }, handler);
224388fa40468c01b8959ae4bac688241b6d362820fJun Yin    }
225388fa40468c01b8959ae4bac688241b6d362820fJun Yin
226388fa40468c01b8959ae4bac688241b6d362820fJun Yin    /**
227388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * Closes the opened logical channel.
228388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *
229388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param response If {@code exception} is null, this will be returned to {@code resultCallback}
230388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *     after the channel has been closed.
231388fa40468c01b8959ae4bac688241b6d362820fJun Yin     * @param exception If not null, this will be returned to {@code resultCallback} after the
232388fa40468c01b8959ae4bac688241b6d362820fJun Yin     *     channel has been closed.
233388fa40468c01b8959ae4bac688241b6d362820fJun Yin     */
234388fa40468c01b8959ae4bac688241b6d362820fJun Yin    private void closeAndReturn(
235388fa40468c01b8959ae4bac688241b6d362820fJun Yin            int channel,
236388fa40468c01b8959ae4bac688241b6d362820fJun Yin            @Nullable byte[] response,
237388fa40468c01b8959ae4bac688241b6d362820fJun Yin            @Nullable Throwable exception,
238388fa40468c01b8959ae4bac688241b6d362820fJun Yin            AsyncResultCallback<byte[]> resultCallback,
239388fa40468c01b8959ae4bac688241b6d362820fJun Yin            Handler handler) {
240388fa40468c01b8959ae4bac688241b6d362820fJun Yin        mCloseChannel.invoke(channel, new AsyncResultCallback<Boolean>() {
241388fa40468c01b8959ae4bac688241b6d362820fJun Yin            @Override
242388fa40468c01b8959ae4bac688241b6d362820fJun Yin            public void onResult(Boolean aBoolean) {
243388fa40468c01b8959ae4bac688241b6d362820fJun Yin                synchronized (mChannelLock) {
244388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    mChannelOpened = false;
245388fa40468c01b8959ae4bac688241b6d362820fJun Yin                }
246388fa40468c01b8959ae4bac688241b6d362820fJun Yin
247388fa40468c01b8959ae4bac688241b6d362820fJun Yin                if (exception == null) {
248388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    resultCallback.onResult(response);
249388fa40468c01b8959ae4bac688241b6d362820fJun Yin                } else {
250388fa40468c01b8959ae4bac688241b6d362820fJun Yin                    resultCallback.onException(exception);
251388fa40468c01b8959ae4bac688241b6d362820fJun Yin                }
252388fa40468c01b8959ae4bac688241b6d362820fJun Yin            }
253388fa40468c01b8959ae4bac688241b6d362820fJun Yin        }, handler);
254388fa40468c01b8959ae4bac688241b6d362820fJun Yin    }
255388fa40468c01b8959ae4bac688241b6d362820fJun Yin}
256