Android添加蓝牙音响功能

\system\media\audio\include\system

audio.h

添加AUDIO_SOURCE_BLUETOOTH_A2DP

typedef enum {
    AUDIO_SOURCE_DEFAULT             = 0,
    AUDIO_SOURCE_MIC                 = 1,
    AUDIO_SOURCE_VOICE_UPLINK        = 2,
    AUDIO_SOURCE_VOICE_DOWNLINK      = 3,
    AUDIO_SOURCE_VOICE_CALL          = 4,
    AUDIO_SOURCE_CAMCORDER           = 5,
    AUDIO_SOURCE_VOICE_RECOGNITION   = 6,
    AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
    AUDIO_SOURCE_REMOTE_SUBMIX       = 8, /* Source for the mix to be presented remotely.      */
                                          /* An example of remote presentation is Wifi Display */
                                          /*  where a dongle attached to a TV can be used to   */
                                          /*  play the mix captured by this audio source.      */
	AUDIO_SOURCE_HDMIRX              = 9,
	AUDIO_SOURCE_BLUETOOTH_A2DP      = 10,

    AUDIO_SOURCE_CNT,
    AUDIO_SOURCE_MAX                 = AUDIO_SOURCE_CNT - 1,
    AUDIO_SOURCE_FM_TUNER            = 1998,
    AUDIO_SOURCE_HOTWORD             = 1999, /* A low-priority, preemptible audio source for
                                                for background software hotword detection.
                                                Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION.
                                                Used only internally to the framework. Not exposed
                                                at the audio HAL. */
} audio_source_t;

\system\bt

Android.mk

ifeq ($(BLUETOOTH_A2DP_SINK),true)
  bdroid_CFLAGS += -DA2DP_SINK
endif

audio_a2dp_hw\Android.mk

ifeq ($(BLUETOOTH_A2DP_SINK),true)
LOCAL_CFLAGS += -DA2DP_SINK
endif

include\bt_target.h

#ifndef BTA_AV_SINK_INCLUDED
#define BTA_AV_SINK_INCLUDED TRUE
#endif

\hardware\libhardware_legacy\audio

Android.mk

ifeq ($(BLUETOOTH_A2DP_SINK),true)
LOCAL_CFLAGS += -DBLUETOOTH_A2DP_SINK
endif

AudioPolicyManagerBase.cpp

audio_devices_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource)
{
    uint32_t device = AUDIO_DEVICE_NONE;

    switch (inputSource) {
    case AUDIO_SOURCE_VOICE_UPLINK:
      if (mAvailableInputDevices & AUDIO_DEVICE_IN_VOICE_CALL) {
          device = AUDIO_DEVICE_IN_VOICE_CALL;
          break;
      }
      // FALL THROUGH

    case AUDIO_SOURCE_DEFAULT:
    case AUDIO_SOURCE_MIC:
    if (mAvailableInputDevices & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
        device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
        break;
    }
    // FALL THROUGH

    case AUDIO_SOURCE_VOICE_RECOGNITION:
    case AUDIO_SOURCE_HOTWORD:
    case AUDIO_SOURCE_VOICE_COMMUNICATION:
        if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO &&
            mAvailableInputDevices & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
            device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
        } else if (mAvailableInputDevices & AUDIO_DEVICE_IN_WIRED_HEADSET) {
            device = AUDIO_DEVICE_IN_WIRED_HEADSET;
        } else if (mAvailableInputDevices & AUDIO_DEVICE_IN_USB_DEVICE) {
            device = AUDIO_DEVICE_IN_USB_DEVICE;
        } else if (mAvailableInputDevices & AUDIO_DEVICE_IN_BUILTIN_MIC) {
            device = AUDIO_DEVICE_IN_BUILTIN_MIC;
        }
        break;
    case AUDIO_SOURCE_CAMCORDER:
        if (mAvailableInputDevices & AUDIO_DEVICE_IN_BACK_MIC) {
            device = AUDIO_DEVICE_IN_BACK_MIC;
        } else if (mAvailableInputDevices & AUDIO_DEVICE_IN_BUILTIN_MIC) {
            device = AUDIO_DEVICE_IN_BUILTIN_MIC;
        }
        break;
    case AUDIO_SOURCE_VOICE_DOWNLINK:
    case AUDIO_SOURCE_VOICE_CALL:
        if (mAvailableInputDevices & AUDIO_DEVICE_IN_VOICE_CALL) {
            device = AUDIO_DEVICE_IN_VOICE_CALL;
        }
        break;
    case AUDIO_SOURCE_REMOTE_SUBMIX:
        if (mAvailableInputDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
            device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
        }
        break;
    case AUDIO_SOURCE_HDMIRX:
        if (mAvailableInputDevices & AUDIO_DEVICE_IN_AUX_DIGITAL) {
            device = AUDIO_DEVICE_IN_AUX_DIGITAL;
        }
        break;
    //add begin
	case AUDIO_SOURCE_BLUETOOTH_A2DP:
		if (mAvailableInputDevices & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
			device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;      
		}
		break;
	//add end
    default:
        ALOGW("getDeviceForInputSource() invalid input source %d", inputSource);
        break;
    }
    ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
    return device;
}

\packages\apps\Bluetooth\src\com\android\bluetooth\a2dp

A2dpSinkStateMachine.java

import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder.AudioSource;


final class A2dpSinkStateMachine extends StateMachine {
	private AudioRecord recorder;
	private AudioTrack player;
	private int recorder_buf_size;
	private int player_buf_size;
	private boolean mThreadExitFlag = false;
	private boolean isPlaying = false;

通过AudioRecord和AudioTrack播放蓝牙音频

    private A2dpSinkStateMachine(A2dpSinkService svc, Context context) {
        super("A2dpSinkStateMachine");
        recorder_buf_size = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
		player_buf_size = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
    }

	private void cleanAudioTrack()
	{
		audioPause();
		mThreadExitFlag = true;
		if (recorder != null) {
			recorder.release();
			recorder = null;
		}
		if (player != null) {
			player.release();
			player = null;
		}
	}

    private void initAudioTrack()    
	{        
		if (recorder == null) 
		{            
			recorder = new AudioRecord(AudioSource.BLUETOOTH_A2DP, 44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT,  recorder_buf_size);        
		}        
		if (player == null) 
		{            
			player = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, player_buf_size, AudioTrack.MODE_STREAM);        
		}    
	}

    private void audioPlay()    
	{        
		initAudioTrack();        
		if (isPlaying == false) 
		{            
			isPlaying = true;            
			mThreadExitFlag = false;            
			new RecordThread().start();        
		}    
	}

	private void audioPause()	
	{		
		if (isPlaying == true) 
		{		   
			isPlaying = false;			
			mThreadExitFlag = true; 		  
			recorder.stop();			 
			player.stop(); 	  
		} 	
	}
    private class Disconnected extends State {
        @Override
        public void enter() {
            log("Enter Disconnected: " + getCurrentMessage().what);
			if (isA2dpSinkEnabled()) {
				cleanAudioTrack();
			}
        }
        private void processAudioStateEvent(int state, BluetoothDevice device) {
            if (!mCurrentDevice.equals(device)) {
                loge("Audio State Device:" + device + "is different from ConnectedDevice:" +
                                                           mCurrentDevice);
                return;
            }
            switch (state) {
                case AUDIO_STATE_STARTED:
					if (isA2dpSinkEnabled()) {
						audioPlay();
					}
                    broadcastAudioState(device, BluetoothA2dpSink.STATE_PLAYING,
                                        BluetoothA2dpSink.STATE_NOT_PLAYING);
                    break;
                case AUDIO_STATE_REMOTE_SUSPEND:
                case AUDIO_STATE_STOPPED:
					if (isA2dpSinkEnabled()) {
						audioPause();
					}
                    broadcastAudioState(device, BluetoothA2dpSink.STATE_NOT_PLAYING,
                                        BluetoothA2dpSink.STATE_PLAYING);
                    break;
                default:
                  loge("Audio State Device: " + device + " bad state: " + state);
                  break;
            }
        }
    }

	private static boolean isA2dpSinkEnabled() 
	{	 
		ParcelUuid[] uuids = BluetoothAdapter.getDefaultAdapter().getUuids();	   
		return BluetoothUuid.isUuidPresent(uuids,BluetoothUuid.AudioSink); 
	} 
	class RecordThread  extends Thread{        
		@Override        
		public void run() {            
			byte[] buffer = new byte[recorder_buf_size];            
			recorder.startRecording();            
			player.play();            
			while(true) {                
				if (mThreadExitFlag == true) {                    
					break;                
				}                
				try {                    
					int res = recorder.read(buffer, 0, recorder_buf_size);                    
					if (res>0) {                        
						byte[] tmpBuf = new byte[res];                        
						System.arraycopy(buffer, 0, tmpBuf, 0, res);                        
						player.write(tmpBuf, 0, tmpBuf.length);                    
					}                
				} catch (Exception e) {                    
				e.printStackTrace();                    
				break;                
				}            
			}        
		}    
	}

AndroidManifest.xml

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

关于在哪取蓝牙过来的音频数据,怎么能让蓝牙进入音频数据过来的模式,这部分android 已经做好了。我们只需要配置一下 packages/apps/Bluetooth/res/values/config.xml 里

<resources>
    <bool name="profile_supported_rtkbt">true</bool>
    <bool name="profile_supported_a2dp">true</bool>
    <bool name="profile_supported_a2dp_sink">true</bool>

\frameworks\base

api\sytem-current.txt

api\current.txt

  public class AudioManager {
  	field public static final int DEVICE_IN_BLUETOOTH_A2DP = -2147352576; // 0x80020000

  public final class MediaRecorder.AudioSource {
    field public static final int BLUETOOTH_A2DP = 10; // 0xa

\media\java\android\media

AudioAttributes.java

        @SystemApi
        public Builder setCapturePreset(int preset) {
            switch (preset) {
                case MediaRecorder.AudioSource.DEFAULT:
                case MediaRecorder.AudioSource.MIC:
                case MediaRecorder.AudioSource.CAMCORDER:
                case MediaRecorder.AudioSource.VOICE_RECOGNITION:
                case MediaRecorder.AudioSource.VOICE_COMMUNICATION:
				case MediaRecorder.AudioSource.BLUETOOTH_A2DP:
                    mSource = preset;
                    break;
                default:
                    Log.e(TAG, "Invalid capture preset " + preset + " for AudioAttributes");
            }
            return this;
        

AudioManager.java

    public boolean isBluetoothA2dpOn() {
    /*
        if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP,"")
            == AudioSystem.DEVICE_STATE_UNAVAILABLE) {
            return false;
        } else {
            return true;
        }
    */
		if (AudioSystem.isA2dpSinkEnabled()) 
		{
			if (AudioSystem.getDeviceConnectionState(DEVICE_IN_BLUETOOTH_A2DP,"")
				== AudioSystem.DEVICE_STATE_UNAVAILABLE) {
				return false;
			} else {
				return true;
			}         
		} else { 
				if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP,"")
				== AudioSystem.DEVICE_STATE_UNAVAILABLE) {
				return false;
			} else {
				return true;
			}
    	}
    }

    public static final int DEVICE_OUT_BLUETOOTH_A2DP = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
	public static final int DEVICE_IN_BLUETOOTH_A2DP = AudioSystem.DEVICE_IN_BLUETOOTH_A2DP;

AudioSystem.java

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothUuid;
import android.os.ParcelUuid;
import android.os.SystemProperties;

	public static	boolean isA2dpSinkEnabled()
	{
        String property = SystemProperties.get("device.bluetooth.ctrl");
        if(property.equals("true")){
		    ParcelUuid[] uuids = BluetoothAdapter.getDefaultAdapter().getUuids();
		    for (ParcelUuid uuid: uuids) {
			    Log.e(TAG, "UUID: " + uuid.getUuid().toString());
		    }
		    return BluetoothUuid.isUuidPresent(uuids,BluetoothUuid.AudioSink);
        }else{
            return false;
        }
	}

MediaRecorder.java

	public static final int BLUETOOTH_A2DP = 10;

    public static final int getAudioSourceMax() {
        //return AudioSource.REMOTE_SUBMIX;
        if (AudioSystem.isA2dpSinkEnabled()) {
			return AudioSource.BLUETOOTH_A2DP;
		} else {
			return AudioSource.REMOTE_SUBMIX;
		}
    }

services/core/java/com/android/server/audio/AudioService.java

    public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
        // address is not used for now, but may be used when multiple a2dp devices are supported
        synchronized (mA2dpAvrcpLock) {

            mAvrcpAbsVolSupported = support;
/*
            sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
                    AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
                    mStreamStates[AudioSystem.STREAM_MUSIC], 0);
            sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
                    AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
                    mStreamStates[AudioSystem.STREAM_RING], 0);
*/
			if (AudioSystem.isA2dpSinkEnabled()) {
				sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
					AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, 0,
					mStreamStates[AudioSystem.STREAM_MUSIC], 0);
				sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
					AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, 0,
					mStreamStates[AudioSystem.STREAM_RING], 0);
			} else {
				sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
					AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
					mStreamStates[AudioSystem.STREAM_MUSIC], 0);
				sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
					AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
					mStreamStates[AudioSystem.STREAM_RING], 0);
			}
        }
    }

\frameworks\av\services\audiopolicy

enginedefault\src\Engine.cpp

audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) const
{
    const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
    const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
    const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
    audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;

    uint32_t device = AUDIO_DEVICE_NONE;

    switch (inputSource) {
    case AUDIO_SOURCE_VOICE_UPLINK:
      if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) {
          device = AUDIO_DEVICE_IN_VOICE_CALL;
          break;
      }
      break;

    case AUDIO_SOURCE_DEFAULT:
        device = AUDIO_DEVICE_IN_BUILTIN_MIC;
        break;

    //begin
    case AUDIO_SOURCE_BLUETOOTH_A2DP:
        if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
            device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
        }
        break;

audio_policy.conf

修改audio_policy.conf配置

  a2dp {
    outputs {
      a2dp {
        sampling_rates 44100
        channel_masks AUDIO_CHANNEL_OUT_STEREO
        formats AUDIO_FORMAT_PCM_16_BIT
        devices AUDIO_DEVICE_OUT_ALL_A2DP
      }
    }
    inputs {
      a2dp {
        sampling_rates 44100
        channel_masks AUDIO_CHANNEL_IN_STEREO
        formats AUDIO_FORMAT_PCM_16_BIT
        devices AUDIO_DEVICE_IN_BLUETOOTH_A2DP
      }
    }
  }
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
是一款具有众多功能和精美设计的 Android 最佳音乐播放器。这款内置顶级质量均衡器的 mp3 播放器将您的音乐聆听体验提升到一个新的水平。这款带eq和高音量应用程序的 mp3 播放器可让您以最佳的声音和最小的使用量播放音乐,而无需消耗电池。是 Android 系统默认音乐播放器的完美替代品。 是离线本地 mp3 播放器应用程序。它不支持在线音乐下载或音乐流。但它可让您在一个地方轻松管理所有离线音乐,浏览快速搜索并支持以所有音乐格式播放。其时尚,强大和快速的音乐播放器,占用的内存少,并提供完美的音乐体验。它是Android上最有用的eq音乐播放器。 精彩功能: 时装设计,30多个最佳音乐主题可替换的背景图片。从图库中选择您自己的图片。华丽的免费背景皮肤。自定义大量的颜色。 强大的节拍均衡器,通过10个惊人的预设,5个频段,低音增强器,音乐虚拟器和3D混响效果调整等来增强您的音乐体验。 内置Mp3剪切器,铃声制作器轻松剪切音频歌曲的最佳部分并将其另存为铃声/警报/通知/音乐文件等。 支持所有音乐文件格式-MP3,MP4,WAV,M4A,FLAC ,3GP,OGC等。 按专辑,艺术家,播放列表,流派,文件夹等浏览和播放音乐。 播放列表自动/手动备份-切勿松动您的播放列表。 支持歌词的音乐播放器(嵌入歌词)。 耳机/蓝牙支持。 音乐持续时间过滤器。 智能睡眠定时器。 智能摇来更改歌曲。 同时支持纵向/横向模式。 背景图片-选择您自己的图片。 内置免费的JAudio Tag编辑器支持。 窗口小部件支持(4×4,4×2,4×1,4×1) 播放带有重新排序的队列-轻松添加曲目并向上/向下拖动以进行排序。 借助“建议的仪表板”,您可以在一处智能地跟踪所有听力。 智能自动播放列表-最近播放/播放次数最多/历史记录完全支持播放列表并随时随地构建自己的播放列表。 标记“收藏歌曲”选项。 从多个播放器设计中选择。 强大的搜索功能-可以快速搜索歌曲,艺术家,专辑等。 漂亮的锁屏控件具有全屏专辑封面支持(启用/禁用)。 Last.fm集成自动获取艺术家信息,例如艺术家作品或传记。 Last.fm Scrobber支持。 强大的Mp3切割器和铃声切割器。 文件夹支持-按文件夹播放歌曲。 派对随机播放音乐-随机播放所有曲目。 天才拖动以对播放列表和播放队列进行排序。 立即播放屏幕滑动以更改歌曲 轻松自定义最佳主题-浅色/深色/黑色/彩色/图片主题。 下载丢失的专辑封面和艺术家图像。 可穿戴的支持。 完美的健身音乐应用程序。 以随机播放,重复播放,循环播放和顺序播放歌曲。 易于导航和简约设计。 节拍指南找到您所有的歌曲和音乐。 最好的免费音乐离线应用和媒体播放器。 轻松分享歌曲。 通知控制-从通知控制跟踪。 超过35种语言支持。
Android高级编程》首先简要介绍Android软件栈,接着陈述为手机创建稳定可靠、赏心悦目的应用程序的基本原理。通过学习,您可以打下牢固的理论根基,了解使用当前Android 1.0 SDK编写定制移动程序所需的知识,还能灵活快捷地运用未来的增强功能构建最前沿的解决方案。   Android提供一个开放的开发环境,为针对移动设备编写创新应用程序带来了激动人心的新机遇。作为使用AndroidSDK构建这些应用程序的实用指南书籍,《Android高级编程》从始至终穿插了一系列示例项目,每个项目都引入Android的新功能和新技术,以助您达到最圆满的学习效果。书中介绍Android的所有基本功能,并通过简明扼要的示例引导您使用高级功能。 更多关于安卓软件系统详情-安卓风 目录: 第1章 Android简介 1.1 一些背景信息 1.1.1 不远的过去 1.1.2 未来的前景 1.2 对Android的误解 1.3 开放的移动开发平台 1.4 自带的Android应用程序 1.5 Android SDK功能 1.5.1 对包括摄像头、GPS和加速计在内的硬件的访问 1.5.2 自带的Google地图、地理编码和基于位置的服务 1.5.3 后台服务 1.5.4 SQLite数据存储和检索数据库 1.5.5 共享数据和应用程序间通信 1.5.6 使用Google Talk的P2P服务 1.5.7 扩展的数据支持和2D/3D图形 1.5.8 优化的内存和进程管理 1.6 开放手机联盟简介 1.7 运行Android的环境 1.8 从事Android开发的原因 1.8.1 推动Android普及的因素 1.8.2 Android的独到之处 1.8.3 改变移动开发格局 1.9 开发框架简介 1.9.1 开发包中的资源 1.9.2 理解Android软件栈 1.9.3 Dalvik虚拟机 1.9.4 Android应用程序架构 1.9.5 Android库 1.9.6 高级Android库 1.10 小结 第2章 开始入手 2.1 Android开发 2.1.1 开始前的准备工作 2.1.2 创建第一个Android活动 2.1.3 Android应用程序的类型 2.2 面向移动设备的开发 2.2.1 关于硬件设计的考虑事项 2.2.2 考虑用户环境 2.2.3 Android开发 2.3 To-Do List示例 2.4 Android开发工具 2.4.1 Android模拟器 2.4.2 Dalvik调试监控服务(DDMS) 2.4.3 Android调试桥(ADB) 2.5 小结 第3章 创建应用程序和活动 3.1 Android应用程序的组成部分 3.2 程序清单简介 3.3 使用清单编辑器 3.4 Android应用程序生命周期 3.5 理解应用程序的优先级和进程状态 3.6 分离资源 3.6.1 创建资源 3.6.2 使用资源 3.6.3 To-DoList资源示例 3.6.4 为不同的语言和硬件创建资源 3.6.5 运行时配置更改 3.7 深入探讨Android活动 3.7.1 创建一个活动 3.7.2 活动生命周期 3.7.3 Android活动类 3.8 小结 第4章 创建用户界面 4.1 Android UI基本设计 4.2 View简介 4.2.1 使用View创建Activity(活动)用户界面 4.2.2 Android Widget工具箱 4.3 布局简介 4.4 创建新的View 4.4.1 修改现有的View 4.4.2 创建复合控件 4.4.3 创建定制的Widget和控件 4.4.4 使用定制的控件 4.5 创建和使用菜单 4.5.1 Android菜单系统简介 4.5.2 定义活动的菜单 4.5.3 动态更新菜单项 4.5.4 处理菜单选择 4.5.5 子菜单和上下文菜单 4.5.6 To-Do List示例续 4.6 小结 第5章 Intent、广播接收器、 Adapter和Internet 5.1 Intent简介 5.1.1 使用Intent来启动活动 5.1.2 使用Intent Filter来为隐式Intent提供服务 5.1.3 使用Intent Filter作为插件和扩展 5.1.4 使用Intent来广播事件 5.2 Adapter简介 5.2.1 Android提供的部分Adapter简介 5.2.2 使用Adapter绑定数据 5.3 使用Internet资源 5.3.1 连接到Internet资源 5.3.2 利用Internet资源 5.4 Dialog简介 5.4.1 Dialog类简介 5.4.2 使用活动作为对话框 5.5 创建一个地震查看器 5.6 小结
ble audio补丁原理是利用hidraw节点捕捉协议栈发送的语音数据,目前Android Blueroid将ble语音数据和按键信息通过hid发送出去,通过建立hidraw节点,可以从中捕捉到语音数据。目前通过ble hal实现从hidraw中读取遥控器语音数据,在Android框架层上就通过配置文件将ble hal导入到音频框架中,并通过绑定Android原生已有的耳麦设备来完成audio音频策略选择,通过apk检测ble连接状态,通知audio服务耳麦设备的状态就可以使得录音通路切换至ble hal,实现从ble获取录音数据功能。 打补丁前最好使用干净的环境,不要有别家方案ble补丁,否则可能会有不兼容问题。 补丁如若不能使用首先检查节点是否存在和其权限,正常节点权限如下: ls -l /dev/hidraw* crw-rw---- 1 system audio 241, 0 2018-12-18 13:42 /dev/hidraw0 audio用户组有读写权限。 2、如果selinux模式为Enforcing,可以通过logcat搜索avc关键字。有如下类似提示则为异常,提示进程没有权限,检查sepolicy是否设置正常: avc: denied { read } for name="/" dev="tmpfs" ino=6145 scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=0 //Android 5.0和6.0版本,audio hal被mediaserver进程加载 avc: denied { read } for name="/" dev="tmpfs" ino=8125 scontext=u:r:audioserver:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=0 //Android 7.0版本,audio hal被audioserver进程加载 avc: denied { read } for name="hidraw" dev="sysfs" ino=16332 scontext=u:r:hal_audio_default:s0 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0 //Android 8.0和9.0版本,audio hal被android[email protected]进程加载 3、检查audio的配置,打上patch后,首先确认小机上文件是否有修改到,目前文件可能位于/vendor/etc或/system/etc目录下,其中/vendor/etc下的配置文件是优先解析的。确保文件无误后,通过dumpsys media.audio_policy查看ble hal是否正常加载。 以下是相关说明: AudioPolicyManager: 0xf20c5200 Command Thread: 0xf20af140 Tones Thread: 0xf20af020 ... - Available input devices: Device 1: - id: 3 - type: AUDIO_DEVICE_IN_BUILTIN_MIC - Profiles: Profile 0: - format: AUDIO_FORMAT_PCM_16_BIT - sampling rates:8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 - channel masks:0x000c, 0x0010 Device 2: - id: 20 - type: AUDIO_DEVICE_IN_WIRED_HEADSET //对应的数值是0x80000010 - name: RemoteDM1204 - Profiles: Available input devices指示当前可用设备,目前ble hal是和AUDIO_DEVICE_IN_WIRED_HEADSET设备绑定,如果需要录音走ble hal,AUDIO_DEVICE_IN_WIRED_HEADSET设备必须出现在可用设备中,如果没有,就可能是补丁中hidaudio.apk的问题。 HW Modules dump: ... - H
CruiseYoung提供的带有详细书签的电子书籍目录 http://blog.csdn.net/fksec/article/details/7888251 该资料是《Android高级编程》的源代码 对应的书籍资料见: Android高级编程 基本信息 原书名: Professional Android Application Development 原出版社: Wrox 作者: (英)Reto Meier 译者: 王鹏杰 霍建同 出版社:清华大学出版社 ISBN:9787302228448 上架时间:2010-7-1 出版日期:2010 年6月 开本:16开 页码:398 版次:1-1 内容简介   android提供一个开放的开发环境,为针对移动设备编写创新应用程序带来了激动人心的新机遇。作为使用androidsdk构建这些应用程序的实用指南书籍,《android高级编程》从始至终穿插了一系列示例项目,每个项目都引入android的新功能和新技术,以助您达到最圆满的学习效果。书中介绍android的所有基本功能,并通过简明扼要的示例引导您使用高级功能。    《android高级编程》首先简要介绍android软件栈,接着陈述为手机创建稳定可靠、赏心悦目的应用程序的基本原理。通过学习,您可以打下牢固的理论根基,了解使用当前android 1.0 sdk编写定制移动程序所需的知识,还能灵活快捷地运用未来的增强功能构建最前沿的解决方案。    主要内容    ◆android移动开发的最佳实践    ◆简要介绍活动、intent、清单和资源    ◆如何使用布局和定制view创建用户界面    ◆存储和共享应用程序数据的技术    ◆如何创建基于地图的应用程序,如何使用gps和地理编码位置等基于位置的服务    ◆如何创建和使用后台服务及notification    ◆使用加速计、指南针和摄像头硬件    ◆与电话和网络硬件相关的所有内容,如电话api、sms和网络管理等    ◆高级开发主题,包括安全、ipc以及一些高级图形和用户界面技术    读者对象    本书面向希望在android手机平台上创建应用程序的所有人员。不管是经验丰富的移动开发人员,还是初出茅庐的新手,都能从本书提供的宝贵信息中获益。 作译者   Reto Meier出生于澳大利西南的珀斯市,现居伦敦。 Reto是一位经验丰富的软件开发人员,拥有逾10年的GUI程序架构、设计和开发经验。他在涉足IT行业前曾从事过海洋石油和天然气开发以及金融工作。 Reto始终不渝地追求掌握新技术,从2007年Android发布之初Reto就迷恋上了此项技术。他利用业余时间研究包括WPF在内的多种开发平台以及Google的各种开发工具。 详情请访问Reto的个人网站RadioactiveYak,网址是http://blog.radioactiveyak.com。 目录 封面 -14 封底 -13 扉页 -12 版权 -11 作者简介 -10 前言 -9 目录 -4 第1章 Android简介 1 1.1 一些背景信息 2 1.1.1 不远的过去 2 1.1.2 未来的前景 2 1.2 对Android的误解 3 1.3 开放的移动开发平台 3 1.4 自带的Android应用程序 4 1.5 Android SDK功能 5 1.5.1 对包括摄像头、GPS和加速计在内的硬件的访问 5 1.5.2 自带的Google地图、地理编码和基于位置的服务 6 1.5.3 后台服务 6 1.5.4 SQLite 数据存储和检索数据库 6 1.5.5 共享数据和应用程序间通信 7 1.5.6 使用Google Talk的P2P服务 7 1.5.7 扩展的数据支持和2D/3D图形 7 1.5.8 优化的内存和进程管理 8 1.6 开放手机联盟简介 8 1.7 运行Android的环境 8 1.8 从事Android开发的原因 9 1.8.1 推动Android普及的因素 9 1.8.2 Android的独到之处 10 1.8.3 改变移动开发格局 10 1.9 开发框架简介 11 1.9.1 开发包中的资源 11 1.9.2 理解Android软件栈 12 1.9.3 Dalvik虚拟机 13 1.9.4 Android应用程序架构 14 1.9.5 Android库 14 1.9.6 高级Android库 15 1.10 小结 16 第2章 开始入手 17 2.1 Android开发 18 2.1.1

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值