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

Commit 24c5bd1e authored by Pomai Ahlo's avatar Pomai Ahlo
Browse files

Update Bluetooth Client to Support AIDL

Tag: #feature
Bug: 241969533
Test: atest BluetoothInstrumentationTests
Change-Id: I9eec18393f0e60555a712a5029f5c6a4a7d6f5e8
parent 13cbbee9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@ android_app {
    ],
    static_libs: [
        "android.hardware.radio-V1.0-java",
        "android.hardware.radio.sap-V1-java",
        "androidx.core_core",
        "androidx.legacy_legacy-support-v4",
        "androidx.lifecycle_lifecycle-livedata",
+49 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.bluetooth.sap;

import android.hardware.radio.sap.ISap;

/**
 * ISapRilReceiver is used to send messages
 */
public interface ISapRilReceiver extends ISap {
    /**
     * Set mSapProxy to null
     */
    void resetSapProxy();

    /**
     * Notify SapServer that this class is ready for shutdown.
     */
    void notifyShutdown();

    /**
     * Notify SapServer that the RIL socket is connected
     */
    void sendRilConnectMessage();

    /**
     * Get mSapProxyLock
     */
    Object getSapProxyLock();

    /**
     * Verifies mSapProxy is not null
     */
    boolean isProxyValid();
}
+24 −29
Original line number Diff line number Diff line
package com.android.bluetooth.sap;

import android.hardware.radio.V1_0.ISap;
import android.hardware.radio.V1_0.SapApduType;
import android.hardware.radio.V1_0.SapTransferProtocol;
import android.hardware.radio.sap.SapApduType;
import android.hardware.radio.sap.SapTransferProtocol;
import android.os.RemoteException;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;

import com.google.protobuf.micro.CodedOutputStreamMicro;
import com.google.protobuf.micro.InvalidProtocolBufferMicroException;

import org.android.btsap.SapApi;
@@ -26,7 +24,6 @@ import org.android.btsap.SapApi.RIL_SIM_SAP_TRANSFER_CARD_READER_STATUS_RSP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@@ -35,7 +32,6 @@ import java.util.concurrent.atomic.AtomicInteger;
 * SapMessage is used for incoming and outgoing messages.
 *
 * For incoming messages
 *
 */
public class SapMessage {

@@ -194,6 +190,7 @@ public class SapMessage {

    /**
     * Create a SapMessage
     *
     * @param msgType the SAP message type
     */
    public SapMessage(int msgType) {
@@ -374,6 +371,7 @@ public class SapMessage {

    /**
     * Construct a SapMessage based on the incoming rfcomm request.
     *
     * @param requestType The type of the request
     * @param is          the input stream to read the data from
     * @return the resulting message, or null if an error occurs
@@ -443,6 +441,7 @@ public class SapMessage {

    /**
     * Blocking read of an entire array of data.
     *
     * @param is     the input stream to read from
     * @param buffer the buffer to read into - the length of the buffer will
     *               determine how many bytes will be read.
@@ -463,6 +462,7 @@ public class SapMessage {

    /**
     * Skip a number of bytes in an InputStream.
     *
     * @param is    the input stream
     * @param count the number of bytes to skip
     * @throws IOException In case of reaching EOF or a stream error
@@ -477,10 +477,10 @@ public class SapMessage {
     * Read the parameters from the stream and update the relevant members.
     * This function will ensure that all parameters are read from the stream, even
     * if an error is detected.
     *
     * @param count the number of parameters to read
     * @param is    the input stream
     * @return True if all parameters were successfully parsed, False if an error were detected.
     * @throws IOException
     */
    private boolean parseParameters(int count, InputStream is) throws IOException {
        int paramId;
@@ -621,6 +621,7 @@ public class SapMessage {

    /**
     * Writes a single value parameter of 1 or 2 bytes in length.
     *
     * @param os     The BufferedOutputStream to write to.
     * @param id     The Parameter ID
     * @param value  The parameter value
@@ -656,6 +657,7 @@ public class SapMessage {

    /**
     * Writes a byte[] parameter of any length.
     *
     * @param os    The BufferedOutputStream to write to.
     * @param id    The Parameter ID
     * @param value The byte array to write, the length will be extracted from the array.
@@ -729,18 +731,11 @@ public class SapMessage {
     * RILD Interface message conversion functions.
     ***************************************************************************/

    private ArrayList<Byte> primitiveArrayToContainerArrayList(byte[] arr) {
        ArrayList<Byte> arrayList = new ArrayList<>(arr.length);
        for (byte b : arr) {
            arrayList.add(b);
        }
        return arrayList;
    }

    /**
     * Send the message by calling corresponding ISap api.
     */
    public void send(ISap sapProxy) throws RemoteException, RuntimeException {
    public void send(ISapRilReceiver sapProxy) throws RemoteException, RuntimeException {
        int rilSerial = sNextSerial.getAndIncrement();

        Log.e(TAG, "callISapReq: called for mMsgType " + mMsgType + " rilSerial " + rilSerial);
@@ -763,13 +758,13 @@ public class SapMessage {
            }
            case ID_TRANSFER_APDU_REQ: {
                int type;
                ArrayList<Byte> command;
                byte[] command;
                if (mApdu != null) {
                    type = SapApduType.APDU;
                    command = primitiveArrayToContainerArrayList(mApdu);
                    command = mApdu;
                } else if (mApdu7816 != null) {
                    type = SapApduType.APDU7816;
                    command = primitiveArrayToContainerArrayList(mApdu7816);
                    command = mApdu7816;
                } else {
                    Log.e(TAG, "Missing Apdu parameter in TRANSFER_APDU_REQ");
                    throw new IllegalArgumentException();
+199 −44
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.bluetooth.sap;

import android.hardware.radio.V1_0.ISap;
import android.hardware.radio.V1_0.ISapCallback;
import android.hardware.radio.sap.ISap;
import android.hardware.radio.sap.ISapCallback;
import android.os.Handler;
import android.os.HwBinder;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import com.android.modules.utils.build.SdkLevel;

import java.util.concurrent.atomic.AtomicLong;

public class SapRilReceiver {
/**
 * SapRiilReceiver is the AIDL implementation of ISapRilReceiver
 */
public class SapRilReceiver implements ISapRilReceiver {
    private static final String TAG = "SapRilReceiver";
    public static final boolean DEBUG = true;
    public static final boolean VERBOSE = true;

    private static final String SERVICE_NAME_RIL_BT = "slot1";
    // match with constant in ril.cpp - as in RIL.java
    private static final int SOCKET_OPEN_RETRY_MILLIS = 4 * 1000;
    // todo: add support for slot2 and slot3
    private static final String HAL_INSTANCE_NAME = ISap.DESCRIPTOR + "/slot1";

    SapCallback mSapCallback;
    volatile ISap mSapProxy = null;
    Object mSapProxyLock = new Object();
    final Object mSapProxyLock = new Object();
    final AtomicLong mSapProxyCookie = new AtomicLong(0);
    final SapProxyDeathRecipient mSapProxyDeathRecipient;

@@ -35,15 +52,129 @@ public class SapRilReceiver {
    public static final int RIL_MAX_COMMAND_BYTES = (8 * 1024);
    public byte[] buffer = new byte[RIL_MAX_COMMAND_BYTES];

    final class SapProxyDeathRecipient implements HwBinder.DeathRecipient {
    /**
     * TRANSFER_APDU_REQ from SAP 1.1 spec 5.1.6
     *
     * @param serial  Id to match req-resp. Resp must include same serial.
     * @param type    APDU command type
     * @param command CommandAPDU/CommandAPDU7816 parameter depending on type
     */
    @Override
    public void apduReq(int serial, int type, byte[] command) throws android.os.RemoteException {
        mSapProxy.apduReq(serial, type, command);
    }

    /**
     * CONNECT_REQ from SAP 1.1 spec 5.1.1
     *
     * @param serial          Id to match req-resp. Resp must include same serial.
     * @param maxMsgSizeBytes MaxMsgSize to be used for SIM Access Profile connection
     */
    @Override
    public void connectReq(int serial, int maxMsgSizeBytes) throws android.os.RemoteException {
        mSapProxy.connectReq(serial, maxMsgSizeBytes);
    }

    /**
     * DISCONNECT_REQ from SAP 1.1 spec 5.1.3
     *
     * @param serial Id to match req-resp. Resp must include same serial.
     */
    @Override
    public void disconnectReq(int serial) throws android.os.RemoteException {
        mSapProxy.disconnectReq(serial);
    }

    /**
     * POWER_SIM_OFF_REQ and POWER_SIM_ON_REQ from SAP 1.1 spec 5.1.10 + 5.1.12
     *
     * @param serial  Id to match req-resp. Resp must include same serial.
     * @param powerOn true for on, false for off
     */
    @Override
    public void powerReq(int serial, boolean powerOn) throws android.os.RemoteException {
        mSapProxy.powerReq(serial, powerOn);
    }

    /**
     * RESET_SIM_REQ from SAP 1.1 spec 5.1.14
     *
     * @param serial Id to match req-resp. Resp must include same serial.
     */
    @Override
    public void resetSimReq(int serial) throws android.os.RemoteException {
        mSapProxy.resetSimReq(serial);
    }

    /**
     * Set callback that has response and unsolicited indication functions
     *
     * @param sapCallback Object containing response and unosolicited indication callbacks
     */
    @Override
    public void setCallback(android.hardware.radio.sap.ISapCallback sapCallback)
            throws android.os.RemoteException {
        Log.e(TAG, "setCallback should never be called");
    }

    /**
     * SET_TRANSPORT_PROTOCOL_REQ from SAP 1.1 spec 5.1.20
     *
     * @param serial           Id to match req-resp. Resp must include same serial.
     * @param transferProtocol Transport Protocol
     */
    @Override
        public void serviceDied(long cookie) {
    public void setTransferProtocolReq(int serial, int transferProtocol)
            throws android.os.RemoteException {
        mSapProxy.setTransferProtocolReq(serial, transferProtocol);
    }

    /**
     * TRANSFER_ATR_REQ from SAP 1.1 spec 5.1.8
     *
     * @param serial Id to match req-resp. Resp must include same serial.
     */
    @Override
    public void transferAtrReq(int serial) throws android.os.RemoteException {
        mSapProxy.transferAtrReq(serial);
    }

    /**
     * TRANSFER_CARD_READER_STATUS_REQ from SAP 1.1 spec 5.1.17
     *
     * @param serial Id to match req-resp. Resp must include same serial.
     */
    @Override
    public void transferCardReaderStatusReq(int serial) throws android.os.RemoteException {
        mSapProxy.transferCardReaderStatusReq(serial);
    }

    @Override
    public int getInterfaceVersion() {
        Log.e(TAG, "getInterfaceVersion should never be called");
        return 0;
    }

    @Override
    public String getInterfaceHash() {
        Log.e(TAG, "getInterfaceHash should never be called");
        return "";
    }

    @Override
    public android.os.IBinder asBinder() {
        Log.e(TAG, "asBinder should never be called");
        return null;
    }

    final class SapProxyDeathRecipient implements IBinder.DeathRecipient {
        @Override
        public void binderDied() {
            // Deal with service going away
            Log.d(TAG, "serviceDied");
            // todo: temp hack to send delayed message so that rild is back up by then
            // mSapHandler.sendMessage(mSapHandler.obtainMessage(EVENT_SAP_PROXY_DEAD, cookie));
            mSapServerMsgHandler.sendMessageDelayed(
                    mSapServerMsgHandler.obtainMessage(SapServer.SAP_PROXY_DEAD, cookie),
                    mSapServerMsgHandler.obtainMessage(SapServer.SAP_PROXY_DEAD, (long) 0),
                    SapServer.ISAP_GET_SERVICE_DELAY_MILLIS);
        }
    }
@@ -100,25 +231,25 @@ public class SapRilReceiver {
        }

        @Override
        public void apduResponse(int token, int resultCode, ArrayList<Byte> apduRsp) {
        public void apduResponse(int token, int resultCode, byte[] apduRsp) {
            Log.d(TAG, "apduResponse: token " + token);
            SapService.notifyUpdateWakeLock(mSapServiceHandler);
            SapMessage sapMessage = new SapMessage(SapMessage.ID_TRANSFER_APDU_RESP);
            sapMessage.setResultCode(resultCode);
            if (resultCode == SapMessage.RESULT_OK) {
                sapMessage.setApduResp(arrayListToPrimitiveArray(apduRsp));
                sapMessage.setApduResp(apduRsp);
            }
            removeOngoingReqAndSendMessage(token, sapMessage);
        }

        @Override
        public void transferAtrResponse(int token, int resultCode, ArrayList<Byte> atr) {
        public void transferAtrResponse(int token, int resultCode, byte[] atr) {
            Log.d(TAG, "transferAtrResponse: token " + token + " resultCode " + resultCode);
            SapService.notifyUpdateWakeLock(mSapServiceHandler);
            SapMessage sapMessage = new SapMessage(SapMessage.ID_TRANSFER_ATR_RESP);
            sapMessage.setResultCode(resultCode);
            if (resultCode == SapMessage.RESULT_OK) {
                sapMessage.setAtr(arrayListToPrimitiveArray(atr));
                sapMessage.setAtr(atr);
            }
            removeOngoingReqAndSendMessage(token, sapMessage);
        }
@@ -195,20 +326,39 @@ public class SapRilReceiver {
            sapMessage.setResultCode(resultCode);
            removeOngoingReqAndSendMessage(token, sapMessage);
        }

        @Override
        public String getInterfaceHash() {
            return ISapCallback.HASH;
        }

    public static byte[] arrayListToPrimitiveArray(List<Byte> bytes) {
        byte[] ret = new byte[bytes.size()];
        for (int i = 0; i < ret.length; i++) {
            ret[i] = bytes.get(i);
        @Override
        public int getInterfaceVersion() {
            return ISapCallback.VERSION;
        }
        return ret;
    }

    @Override
    public Object getSapProxyLock() {
        return mSapProxyLock;
    }

    @Override
    public boolean isProxyValid() {
        // Only call when synchronized with getSapProxyLock
        return mSapProxy != null;
    }

    /**
     * Check if AIDL is supported
     */
    public static boolean isAidlSupported() {
        return SdkLevel.isAtLeastU() && ServiceManager.isDeclared(HAL_INSTANCE_NAME);
    }

    /**
     * Obtain a valid sapProxy
     */
    public ISap getSapProxy() {
        synchronized (mSapProxyLock) {
            if (mSapProxy != null) {
@@ -216,10 +366,11 @@ public class SapRilReceiver {
            }

            try {
                mSapProxy = ISap.getService(SERVICE_NAME_RIL_BT);
                IBinder service = ServiceManager.waitForDeclaredService(HAL_INSTANCE_NAME);
                mSapProxy = ISap.Stub.asInterface(service);
                if (mSapProxy != null) {
                    mSapProxy.linkToDeath(mSapProxyDeathRecipient,
                            mSapProxyCookie.incrementAndGet());
                    service.linkToDeath(mSapProxyDeathRecipient,
                            /* flags= */ 0);
                    mSapProxy.setCallback(mSapCallback);
                } else {
                    Log.e(TAG, "getSapProxy: mSapProxy == null");
@@ -240,16 +391,17 @@ public class SapRilReceiver {
        }
    }

    @Override
    public void resetSapProxy() {
        synchronized (mSapProxyLock) {
            if (DEBUG) Log.d(TAG, "resetSapProxy :" + mSapProxy);
            try {
                if (mSapProxy != null) {
                    mSapProxy.unlinkToDeath(mSapProxyDeathRecipient);
            if (mSapProxy == null) {
                return;
            }
            } catch (RemoteException | RuntimeException e) {
                Log.e(TAG, "resetSapProxy: exception: " + e);
            if (mSapProxy.asBinder() == null) {
                Log.e(TAG, "asdf asBinder is null");
            }
            mSapProxy.asBinder().unlinkToDeath(mSapProxyDeathRecipient, /* flags= */ 0);
            mSapProxy = null;
        }
    }
@@ -264,25 +416,26 @@ public class SapRilReceiver {
        }
    }

    /**
     * Notify SapServer that this class is ready for shutdown.
     */
    void notifyShutdown() {
    @Override
    public void notifyShutdown() {
        if (DEBUG) {
            Log.i(TAG, "notifyShutdown()");
        }
        // If we are already shutdown, don't bother sending a notification.
        synchronized (mSapProxyLock) {
            // If we are already shutdown, don't bother sending a notification.
            if (mSapProxy != null) {
                sendShutdownMessage();
            }
            resetSapProxy();

            // todo: rild should be back up since the message was sent with a delay. this is
            // a hack.
            getSapProxy();
        }
    }

    /**
     * Notify SapServer that the RIL socket is connected
     */
    void sendRilConnectMessage() {
    @Override
    public void sendRilConnectMessage() {
        if (mSapServerMsgHandler != null) {
            mSapServerMsgHandler.sendEmptyMessage(SapServer.SAP_MSG_RIL_CONNECT);
        }
@@ -290,6 +443,7 @@ public class SapRilReceiver {

    /**
     * Send reply (solicited) message from the RIL to the Sap Server Handler Thread
     *
     * @param sapMsg The message to send
     */
    private void sendClientMessage(SapMessage sapMsg) {
@@ -308,6 +462,7 @@ public class SapRilReceiver {

    /**
     * Send indication (unsolicited) message from RIL to the Sap Server Handler Thread
     *
     * @param sapMsg The message to send
     */
    private void sendRilIndMessage(SapMessage sapMsg) {
+481 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading