Android 蓝牙开发——MAP协议(八)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

SDK路径:frameworks/base/core/java/android/bluetooth/

服务路径:packages/apps/Bluetooth/src/com/android/bluetooth/

        在使用协议类的时候无法找到该类由于安卓源码中关于蓝牙协议的 Client 部分或相关接口都被 @hide 给隐藏掉了这样 android.jar 满足不了安卓源码 framework 层开发人员的需求可以使用反射机制或者引用 framework.jar 代替 android.jar。

位置:out\target\common\obj\JAVA_LIBRARIES\framework_intermediates\classes.jar

一、SDK接口

         BluetoothMapClient.java:蓝牙apk中的MAP协议的代理类应用通过此代理类访问MAP协议的方法。

接口名描述
connect连接指定设备
disconnect断开指定设备
isConnected判断指定设备是否连接连接则返回true否则false
getConnectedDevices获取已连接设备列表
getDevicesMatchingConnectionStates获取指定状态的设备列表
getConnectionState获得指定设备的状态
setPriority设置协议的优先级
getPriority获取协议的优先级
sendMessage使用指定设备发送消息至指定的联系人
getUnreadMessages获得未读消息
isUploadingSupported从SDP记录的MapSupportedFeatures字段返回“上传”特征位值
setMessageStatus设置MSE上的消息状态

重点关注的接口:

    /**
     * 向指定的电话号码发送SMS消息
     * 
     * @param device 蓝牙设备
     * @param contacts 联系人的Uri[]列表
     * @param message 要发送的消息
     * @param sentIntent 发送消息时发出的意图 SMS消息发送成功将发送{@link #ACTION_MESSAGE_SENT_SUCCESSFULLY} 广播
     * @param deliveredIntent 消息传递时发出的意图 SMS消息传递成功将发送{@link #ACTION_MESSAGE_DELIVERED_SUCCESSFULLY} 广播
     * @return 如果消息入队则返回 true错误则返回 false
     */
    public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
            PendingIntent sentIntent, PendingIntent deliveredIntent) 
            
    /**
     * 获取未读消息.  未读消息将发送 {@link #ACTION_MESSAGE_RECEIVED} 广播。
     *
     * @param device 蓝牙设备
     * @return 如果消息入队则返回 true错误则返回 false
     */
    public boolean getUnreadMessages(BluetoothDevice device)

 二、APP调用SDK

1、获取 BluetoothMapClient

BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
btAdapter.getProfileProxy(mService, new MapServiceListener(), BluetoothProfile.MAP_CLIENT);

2、MapServiceListener

private BluetoothMapClient mapClient;

class MapServiceListener implements BluetoothProfile.ServiceListener {
    @Override
    public void onServiceConnected(int profile, BluetoothProfile proxy) {
        synchronized (this) {
            mapClient = (BluetoothMapClient) proxy;
        }
    }

    @Override
    public void onServiceDisconnected(int profile) {
        synchronized (this) {
            mapClient = null;
        }
    }
}

         这里通过回调方法拿到 BluetoothMapClient 类。通过该类可以调用 SDK 中的各种方法。

三、源码解析

1、getProfileProxy()

public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, int profile) {
    if (context == null || listener == null) {
        return false;
    }

    if (profile == BluetoothProfile.HEADSET) {
        BluetoothHeadset headset = new BluetoothHeadset(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.A2DP) {
        BluetoothA2dp a2dp = new BluetoothA2dp(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.A2DP_SINK) {
        BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
        BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.HID_HOST) {
        BluetoothHidHost iDev = new BluetoothHidHost(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.PAN) {
        BluetoothPan pan = new BluetoothPan(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.PBAP) {
        BluetoothPbap pbap = new BluetoothPbap(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.HEALTH) {
        Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
        return false;
    } else if (profile == BluetoothProfile.MAP) {
        BluetoothMap map = new BluetoothMap(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.HEADSET_CLIENT) {
        BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.SAP) {
        BluetoothSap sap = new BluetoothSap(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.PBAP_CLIENT) {
        BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.MAP_CLIENT) {
        BluetoothMapClient mapClient = new BluetoothMapClient(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.HID_DEVICE) {
        BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this);
        return true;
    } else if (profile == BluetoothProfile.HEARING_AID) {
        if (isHearingAidProfileSupported()) {        
            BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this);
            return true;
        }
        return false;
    } else if (profile == BluetoothProfile.LE_AUDIO) {
        BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this);
        return true;
    } else {
        return false;
    }
}

2、BluetoothMapClient

private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT, "BluetoothMapClient", IBluetoothMapClient.class.getName()) {
    @Override
    public IBluetoothMapClient getServiceInterface(IBinder service) {
        return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service));
    }
};

/* package */ BluetoothMapClient(Context context, ServiceListener listener, BluetoothAdapter adapter) {
    if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object");
    mAdapter = adapter;
    mAttributionSource = adapter.getAttributionSource();
    mProfileConnector.connect(context, listener);
}

3、BluetoothProfileConnector.connect()

void connect(Context context, BluetoothProfile.ServiceListener listener) {
    mContext = context;
    mServiceListener = listener;
    // 从BluetoothAdapter类中获取到蓝牙管理服务对象
    IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();

    // Preserve legacy compatibility where apps were depending on
    // registerStateChangeCallback() performing a permissions check which
    // has been relaxed in modern platform versions
    if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException("Need BLUETOOTH permission");
    }

    if (mgr != null) {
        try {
            // 注册Map状态回调接口
            mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
        } catch (RemoteException re) {
            logError("Failed to register state change callback. " + re);
        }
    }
    // 绑定蓝牙Map Service
    doBind();
}
private boolean doBind() {
    synchronized (mConnection) {
        if (mService == null) {
            logDebug("Binding service...");
            mCloseGuard.open("doUnbind");
            try {
                // 绑定 MapService实现与 Bluetooth 进程通信
                Intent intent = new Intent(mServiceName);
                ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
                intent.setComponent(comp);
                if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, UserHandle.CURRENT_OR_SELF)) {
                    logError("Could not bind to Bluetooth Service with " + intent);
                        return false;
                }
            } catch (SecurityException se) {
                logError("Failed to bind service. " + se);
                return false;
            }
        }
    }
    return true;
}

         这里就绑定了 MapClientService 服务(packages/apps/Bluetooth/src/com/android/bluetooth/mapclient/。接下来分析 如何调用 BluetoothMapClient 提供的 API 方法以 connect() 为例。

4、BluetoothMapClient.connect()

@Override
public boolean connect(BluetoothDevice device, AttributionSource source) {
    Attributable.setAttributionSource(device, source);
    MapClientService service = getService(source);
    if (service == null) {
        return false;
    }
    return service.connect(device);
}

public boolean connect(BluetoothDevice device) {
    if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
    final IBluetoothMapClient service = getService();
    if (service != null) {
        try {
            return service.connect(device, mAttributionSource);
        } catch (RemoteException e) {
            Log.e(TAG, e.toString());
        }
    } else {
        Log.w(TAG, "Proxy not attached to service");
        if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
    }
    return false;
}
private IBluetoothMapClient getService() {
    return mProfileConnector.getService();
}

        这里的 service 就是上面 BluetoothProfileConnector 中绑定的 MapClientService。

MapClientService.connect()

public synchronized boolean connect(BluetoothDevice device) {
    enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
    if (device == null) {
        throw new IllegalArgumentException("Null device");
    }
    if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
        Log.w(TAG, "Connection not allowed: <" + device.getAddress() + "> is CONNECTION_POLICY_FORBIDDEN");
        return false;
    }
    MceStateMachine mapStateMachine = mMapInstanceMap.get(device);
    if (mapStateMachine == null) {
        // 一个映射状态机实例还不存在如果可以的话创建一个新的。
        if (mMapInstanceMap.size() < MAXIMUM_CONNECTED_DEVICES) {
            addDeviceToMapAndConnect(device);
            return true;
        } else {
            // 允许的连接数已达到最大值
            // 看看当前的一些连接是否可以清理腾出空间。
            removeUncleanAccounts();
            if (mMapInstanceMap.size() < MAXIMUM_CONNECTED_DEVICES) {
                addDeviceToMapAndConnect(device);
                return true;
            } else {
                Log.e(TAG, "Maxed out on the number of allowed MAP connections. " + "Connect request rejected on " + device);
                return false;
            }
        }
    }

    // 状态机已经存在于map中
    int state = getConnectionState(device);
    if (state == BluetoothProfile.STATE_CONNECTED || state == BluetoothProfile.STATE_CONNECTING) {
        Log.w(TAG, "Received connect request while already connecting/connected.");
        return true;
    }

    // 状态机存在但不在连接状态从map上删除添加一个新的
    mMapInstanceMap.remove(device);
    addDeviceToMapAndConnect(device);
    return true;
}
private synchronized void addDeviceToMapAndConnect(BluetoothDevice device) {
    // 当创建一个新的状态机时它的状态被设置为连接——这将触发连接。
    MceStateMachine mapStateMachine = new MceStateMachine(this, device);
    mMapInstanceMap.put(device, mapStateMachine);
}

        最后的连接工作在 MceStateMachine 中完成对于各种状态的变化也在 MceStateMachine 里通过各种广播通知出来。

四、获取未读消息

1、MapClientService.getUnreadMessages()

        我们直接看 MapClientService 中的 getUnreadMessages() 方法。

public synchronized boolean getUnreadMessages(BluetoothDevice device) {
    MceStateMachine mapStateMachine = mMapInstanceMap.get(device);
    if (mapStateMachine == null) {
        return false;
    }
    return mapStateMachine.getUnreadMessages();
}

2、MceStateMachine.getUnreadMessages()

synchronized boolean getUnreadMessages() {
    if (this.getCurrentState() == mConnected) {
        sendMessage(MSG_GET_MESSAGE_LISTING, FOLDER_INBOX);
        return true;
    }
    return false;
}
@Override
public boolean processMessage(Message message) {
    switch (message.what) {
        case MSG_GET_MESSAGE_LISTING:
            // 获取最新50条未读邮件
            MessagesFilter filter = new MessagesFilter();
            filter.setMessageType(MapUtils.fetchMessageType());
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DATE, -7);
            filter.setPeriod(calendar.getTime(), null);
            mMasClient.makeRequest(new RequestGetMessagesListing( (String) message.obj, 0, filter, 0, 50, 0));
            break;

    }
    return HANDLED;
}

3、MasClient.makeRequest()

public boolean makeRequest(Request request) {
    boolean status = mHandler.sendMessage(mHandler.obtainMessage(REQUEST, request));
    if (!status) {
        return false;
    }
    return true;
}

private static class MasClientHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        MasClient inst = mInst.get();
        switch (msg.what) {
            case REQUEST:
                if (inst.mConnected) {
                    inst.executeRequest((Request) msg.obj);
                }
                break;
        }
    }
}

private void executeRequest(Request request) {
    try {
        request.execute(mSession);
        mCallback.sendMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, request);
    } catch (IOException e) {
        // 断开到清理
        disconnect();
    }
}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: android