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