由于需要实现avrcp的controller的get attr功能,所以看了下android6的代码调用关系。
这里的app层是指用户编写的应用程序而不是谷歌android中生成的一些app。如下是调试app的截屏。该app中出现了几个button用于控制targe。
app的代码简短罗列如下:
public class MyActivity extends Activity {
private static final String TAG = "AndroidAvrcpControllerDemo";
//BluetoothAvrcpController是要追踪的重点之一。
private BluetoothAvrcpController mAvrcpController;
private TextView mStatusView;
private TextView mAttrsView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mStatusView = (TextView)findViewById(R.id.status);
mAttrsView = (TextView)findViewById(R.id.attrs);
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
bluetoothAdapter.getProfileProxy(this, mAvrcpServiceListener, BluetoothProfile.AVRCP_CONTROLLER);
mStatusView.setText("Connecting to the AVRCP_CONTROLLER service");
}
@Override
protected void onDestroy() {
super.onDestroy();
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
//mAvrcpController.removeCallback();
bluetoothAdapter.closeProfileProxy(BluetoothProfile.AVRCP_CONTROLLER, mAvrcpController);
}
private BluetoothProfile.ServiceListener mAvrcpServiceListener = new BluetoothProfile.ServiceListener(){
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.AVRCP_CONTROLLER){
mStatusView.setText("AVRCP_CONTROLLER connected");
Log.d(TAG, "AvrcpControllerService connected");
mAvrcpController = (BluetoothAvrcpController) proxy;
// mAvrcpController.setCallback(new AvrcpControllerCallback());
mStatusView.append("\r\nAvrcp devices: \r\n");
List devices = mAvrcpController.getConnectedDevices();
for (BluetoothDevice device : devices)
mStatusView.append(" - " + device.getName() + " " + device.getAddress()+"\r\n");
}
}
@Override
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
mStatusView.setText("AVRCP_CONTROLLER disconnected");
Log.d(TAG, "AvrcpControllerService disconnected");
//mAvrcpController.removeCallback();
mAvrcpController = null;
}
}
};
private void sendCommand(int keyCode){
if (mAvrcpController == null)
return;
List devices = mAvrcpController.getConnectedDevices();
for (BluetoothDevice device : devices){
Log.d(TAG, "send command to device: "+ keyCode + device.getName() + " " + device.getAddress());
//该方法是要追踪的重点之二
mAvrcpController.sendPassThroughCmd(device, keyCode, BluetoothAvrcp.PASSTHROUGH_STATE_PRESS);
mAvrcpController.sendPassThroughCmd(device, keyCode, BluetoothAvrcp.PASSTHROUGH_STATE_RELEASE);
}
}
public void onPlayButtonClick(View view){
sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_PLAY);
}
frameworks/base/core/java/android/bluetooth/BluetoothAvrcpController.java
public final class BluetoothAvrcpController implements BluetoothProfile {
//服务对象
private IBluetoothAvrcpController mService;
//创建BluetoothAvrcpController代理对象,用于和本地AVRCP服务交互。
/*package*/ BluetoothAvrcpController(Context context, ServiceListener l) {
mContext = context;
mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
try {
mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
doBind();
}
boolean doBind() {//aidl
Intent intent = new Intent(IBluetoothAvrcpController.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
android.os.Process.myUserHandle())) {
Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
return false;
}
return true;
}
//这里是应用调用的sendPassThroughCmd方法,该方法调用的是一个service的sendPassThroughCmd方法。
public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) {
if (DBG) Log.d(TAG, "sendPassThroughCmd");
if (mService != null && isEnabled()) {
try {
mService.sendPassThroughCmd(device, keyCode, keyState);
return;
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e);
return;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
}
该类提供了蓝牙AVRCP控制profile。BluetoothAvrcpController是一个通过IPC方式控制AVRCP 服务的代理对象。
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
mService = IBluetoothAvrcpController.Stub.asInterface(service);
上述或得了服务对象。
frameworks/base/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
interface IBluetoothAvrcpController {
List getConnectedDevices();
List getDevicesMatchingConnectionStates(in int[] states);
int getConnectionState(in BluetoothDevice device);
void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState);
}
这里描述的是service提供服务的接口函数。
package
packages/apps/Bluetooth/src/com/android/bluetooth/avrcp/AvrcpControllerService.java
public class AvrcpControllerService extends ProfileService {
static {
classInitNative();
}
public AvrcpControllerService() {
mMetadata = new Metadata();
initNative();
}
...
getConnectedDevices
public List getConnectedDevices() {
AvrcpControllerService service = getService();
if (service == null) return new ArrayList(0);
return service.getConnectedDevices();
}
该实现最终调用service的getConnectedDevices方法,就是这个文件里的方法。该方法调用native层实现。
上图左侧颜色相同的部分是调用关系
类似向下调用关系,直接上图
在升级到android 6之后,这个测试app在高通平台遇到了如下问题:
其提示我ACCESS_BLUETOOTH_AVRCP_CT_DATA权限没有,但是我的Manifest has definitions as follows:
I'v tried to remove BLUETOOTH permission to verify that it is surely can get the BLUETOOTH permission. So the question is why I can't get ACCESS_BLUETOOTH_AVRCP_CT_DATA permission.
Since ACCESS_BLUETOOTH_AVRCP_CT_DATA is defined in system Bluetooth.apk. My demo apk should have the same sign as system apks.
The sign is in 签名证书(platform.pk8和platform.x509.pem), which is in directory build/target/product/security. FOTA also use this files.
Android.mk
LOCAL_CERTIFICATE := platform