diff options
48 files changed, 2740 insertions, 90 deletions
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index 50841a6..a68a9cb 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -88,8 +88,8 @@ public: * user: Pointer to context for use by the callback receiver. * info: Pointer to optional parameter according to event type: * - EVENT_MORE_DATA: pointer to AudioRecord::Buffer struct. The callback must not read - * more bytes than indicated by 'size' field and update 'size' if fewer bytes are - * consumed. + * more bytes than indicated by 'size' field and update 'size' if + * fewer bytes are consumed. * - EVENT_OVERRUN: unused. * - EVENT_MARKER: pointer to const uint32_t containing the marker position in frames. * - EVENT_NEW_POS: pointer to const uint32_t containing the new position in frames. diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index ba63065..ad5d6ed 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -113,7 +113,7 @@ public: // audio_stream->get_buffer_size()/audio_stream_out_frame_size() static status_t getFrameCount(audio_io_handle_t output, size_t* frameCount); - // returns the audio output stream latency in ms. Corresponds to + // returns the audio output latency in ms. Corresponds to // audio_stream_out->get_latency() static status_t getLatency(audio_io_handle_t output, uint32_t* latency); @@ -126,7 +126,7 @@ public: static status_t setVoiceVolume(float volume); // return the number of audio frames written by AudioFlinger to audio HAL and - // audio dsp to DAC since the specified output I/O handle has exited standby. + // audio dsp to DAC since the specified output has exited standby. // returned status (from utils/Errors.h) can be: // - NO_ERROR: successful operation, halFrames and dspFrames point to valid data // - INVALID_OPERATION: Not supported on current hardware platform diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h index c51e2f0..f927a80 100644 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -94,6 +94,8 @@ public: sp<IMemory>& buffers, // return value 0 means it follows cblk status_t *status) = 0; + // FIXME Surprisingly, sampleRate/format/frameCount/latency don't work for input handles + /* query the audio hardware state. This state never changes, * and therefore can be cached. */ diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index 442c861..aa91485 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -299,6 +299,8 @@ private: status_t setupRawAudioFormat( OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels); + status_t setPriority(int32_t priority); + status_t setMinBufferSize(OMX_U32 portIndex, size_t size); status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg); diff --git a/include/media/stagefright/ProcessInfo.h b/include/media/stagefright/ProcessInfo.h new file mode 100644 index 0000000..ec0cdff --- /dev/null +++ b/include/media/stagefright/ProcessInfo.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef PROCESS_INFO_H_ + +#define PROCESS_INFO_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/ProcessInfoInterface.h> + +namespace android { + +struct ProcessInfo : public ProcessInfoInterface { + ProcessInfo(); + + virtual bool getPriority(int pid, int* priority); + +protected: + virtual ~ProcessInfo(); + +private: + DISALLOW_EVIL_CONSTRUCTORS(ProcessInfo); +}; + +} // namespace android + +#endif // PROCESS_INFO_H_ diff --git a/media/libmediaplayerservice/ProcessInfoInterface.h b/include/media/stagefright/ProcessInfoInterface.h index 222f92d..222f92d 100644 --- a/media/libmediaplayerservice/ProcessInfoInterface.h +++ b/include/media/stagefright/ProcessInfoInterface.h diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index 7143f1a..5644428 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -53,8 +53,8 @@ namespace android { struct AudioTrackSharedStreaming { // similar to NBAIO MonoPipe // in continuously incrementing frame units, take modulo buffer size, which must be a power of 2 - volatile int32_t mFront; // read by server - volatile int32_t mRear; // write by client + volatile int32_t mFront; // read by consumer (output: server, input: client) + volatile int32_t mRear; // written by producer (output: client, input: server) volatile int32_t mFlush; // incremented by client to indicate a request to flush; // server notices and discards all data between mFront and mRear volatile uint32_t mUnderrunFrames; // server increments for each unavailable but desired frame diff --git a/include/radio/IRadio.h b/include/radio/IRadio.h new file mode 100644 index 0000000..1877f8f --- /dev/null +++ b/include/radio/IRadio.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_HARDWARE_IRADIO_H +#define ANDROID_HARDWARE_IRADIO_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <system/radio.h> + +namespace android { + +class IRadio : public IInterface +{ +public: + + DECLARE_META_INTERFACE(Radio); + + virtual void detach() = 0; + + virtual status_t setConfiguration(const struct radio_band_config *config) = 0; + + virtual status_t getConfiguration(struct radio_band_config *config) = 0; + + virtual status_t setMute(bool mute) = 0; + + virtual status_t getMute(bool *mute) = 0; + + virtual status_t step(radio_direction_t direction, bool skipSubChannel) = 0; + + virtual status_t scan(radio_direction_t direction, bool skipSubChannel) = 0; + + virtual status_t tune(unsigned int channel, unsigned int subChannel) = 0; + + virtual status_t cancel() = 0; + + virtual status_t getProgramInformation(struct radio_program_info *info) = 0; + + virtual status_t hasControl(bool *hasControl) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnRadio: public BnInterface<IRadio> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_IRADIO_H diff --git a/include/radio/IRadioClient.h b/include/radio/IRadioClient.h new file mode 100644 index 0000000..9062ad6 --- /dev/null +++ b/include/radio/IRadioClient.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_HARDWARE_IRADIO_CLIENT_H +#define ANDROID_HARDWARE_IRADIO_CLIENT_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> + +namespace android { + +class IRadioClient : public IInterface +{ +public: + + DECLARE_META_INTERFACE(RadioClient); + + virtual void onEvent(const sp<IMemory>& eventMemory) = 0; + +}; + +// ---------------------------------------------------------------------------- + +class BnRadioClient : public BnInterface<IRadioClient> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_IRADIO_CLIENT_H diff --git a/include/radio/IRadioService.h b/include/radio/IRadioService.h new file mode 100644 index 0000000..a946dd5 --- /dev/null +++ b/include/radio/IRadioService.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_HARDWARE_IRADIO_SERVICE_H +#define ANDROID_HARDWARE_IRADIO_SERVICE_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <system/radio.h> + +namespace android { + +class IRadio; +class IRadioClient; + +class IRadioService : public IInterface +{ +public: + + DECLARE_META_INTERFACE(RadioService); + + virtual status_t listModules(struct radio_properties *properties, + uint32_t *numModules) = 0; + + virtual status_t attach(const radio_handle_t handle, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool withAudio, + sp<IRadio>& radio) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnRadioService: public BnInterface<IRadioService> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_IRADIO_SERVICE_H diff --git a/include/radio/Radio.h b/include/radio/Radio.h new file mode 100644 index 0000000..302bf16 --- /dev/null +++ b/include/radio/Radio.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_HARDWARE_RADIO_H +#define ANDROID_HARDWARE_RADIO_H + +#include <binder/IBinder.h> +#include <utils/threads.h> +#include <radio/RadioCallback.h> +#include <radio/IRadio.h> +#include <radio/IRadioService.h> +#include <radio/IRadioClient.h> +#include <system/radio.h> + +namespace android { + +class MemoryDealer; + +class Radio : public BnRadioClient, + public IBinder::DeathRecipient +{ +public: + + virtual ~Radio(); + + static status_t listModules(struct radio_properties *properties, + uint32_t *numModules); + static sp<Radio> attach(radio_handle_t handle, + const struct radio_band_config *config, + bool withAudio, + const sp<RadioCallback>& callback); + + + void detach(); + + status_t setConfiguration(const struct radio_band_config *config); + + status_t getConfiguration(struct radio_band_config *config); + + status_t setMute(bool mute); + + status_t getMute(bool *mute); + + status_t step(radio_direction_t direction, bool skipSubChannel); + + status_t scan(radio_direction_t direction, bool skipSubChannel); + + status_t tune(unsigned int channel, unsigned int subChannel); + + status_t cancel(); + + status_t getProgramInformation(struct radio_program_info *info); + + status_t hasControl(bool *hasControl); + + // BpRadioClient + virtual void onEvent(const sp<IMemory>& eventMemory); + + //IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + +private: + Radio(radio_handle_t handle, + const sp<RadioCallback>&); + static const sp<IRadioService>& getRadioService(); + + Mutex mLock; + sp<IRadio> mIRadio; + const radio_handle_t mHandle; + sp<RadioCallback> mCallback; +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_RADIO_H diff --git a/include/radio/RadioCallback.h b/include/radio/RadioCallback.h new file mode 100644 index 0000000..4a7f1a6 --- /dev/null +++ b/include/radio/RadioCallback.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_HARDWARE_RADIO_CALLBACK_H +#define ANDROID_HARDWARE_RADIO_CALLBACK_H + +#include <utils/RefBase.h> +#include <system/radio.h> + +namespace android { + +class RadioCallback : public RefBase +{ +public: + + RadioCallback() {} + virtual ~RadioCallback() {} + + virtual void onEvent(struct radio_event *event) = 0; + +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_RADIO_CALLBACK_H diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 08241e2..6d5f1af 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -423,7 +423,6 @@ status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *request goto end; } // check for obtainBuffer interrupted by client - // check for obtainBuffer interrupted by client if (flags & CBLK_INTERRUPT) { ALOGV("waitStreamEndDone() interrupted by client"); status = -EINTR; diff --git a/media/libmediaplayerservice/DrmSessionManager.cpp b/media/libmediaplayerservice/DrmSessionManager.cpp index 6e17eb1..641f881 100644 --- a/media/libmediaplayerservice/DrmSessionManager.cpp +++ b/media/libmediaplayerservice/DrmSessionManager.cpp @@ -21,10 +21,10 @@ #include "DrmSessionManager.h" #include "DrmSessionClientInterface.h" -#include "ProcessInfoInterface.h" #include <binder/IPCThreadState.h> #include <binder/IProcessInfoService.h> #include <binder/IServiceManager.h> +#include <media/stagefright/ProcessInfo.h> #include <unistd.h> #include <utils/String8.h> @@ -38,37 +38,6 @@ static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) { return sessionIdStr; } -struct ProcessInfo : public ProcessInfoInterface { - ProcessInfo() {} - - virtual bool getPriority(int pid, int* priority) { - sp<IBinder> binder = defaultServiceManager()->getService(String16("processinfo")); - sp<IProcessInfoService> service = interface_cast<IProcessInfoService>(binder); - - size_t length = 1; - int32_t states; - status_t err = service->getProcessStatesFromPids(length, &pid, &states); - if (err != OK) { - ALOGE("getProcessStatesFromPids failed"); - return false; - } - ALOGV("pid %d states %d", pid, states); - if (states < 0) { - return false; - } - - // Use process state as the priority. Lower the value, higher the priority. - *priority = states; - return true; - } - -protected: - virtual ~ProcessInfo() {} - -private: - DISALLOW_EVIL_CONSTRUCTORS(ProcessInfo); -}; - bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) { if (sessionId1.size() != sessionId2.size()) { return false; diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp index 27b482b..d3e760b 100644 --- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp +++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp @@ -23,8 +23,8 @@ #include "Drm.h" #include "DrmSessionClientInterface.h" #include "DrmSessionManager.h" -#include "ProcessInfoInterface.h" #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/ProcessInfoInterface.h> namespace android { diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index c75d4df..31e10ce 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1676,6 +1676,11 @@ status_t ACodec::configureCodec( err = setMinBufferSize(kPortIndexInput, 8192); // XXX } + int32_t priority; + if (msg->findInt32("priority", &priority)) { + err = setPriority(priority); + } + mBaseOutputFormat = outputFormat; CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK); @@ -1686,6 +1691,22 @@ status_t ACodec::configureCodec( return err; } +status_t ACodec::setPriority(int32_t priority) { + if (priority < 0) { + return BAD_VALUE; + } + OMX_PARAM_U32TYPE config; + InitOMXParams(&config); + config.nU32 = (OMX_U32)priority; + status_t temp = mOMX->setConfig( + mNode, (OMX_INDEXTYPE)OMX_IndexConfigPriority, + &config, sizeof(config)); + if (temp != OK) { + ALOGI("codec does not support config priority (err %d)", temp); + } + return OK; +} + status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 38f2e34..177293d 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -47,6 +47,7 @@ LOCAL_SRC_FILES:= \ OMXClient.cpp \ OMXCodec.cpp \ OggExtractor.cpp \ + ProcessInfo.cpp \ SampleIterator.cpp \ SampleTable.cpp \ SkipCutBuffer.cpp \ diff --git a/media/libstagefright/ProcessInfo.cpp b/media/libstagefright/ProcessInfo.cpp new file mode 100644 index 0000000..b4172b3 --- /dev/null +++ b/media/libstagefright/ProcessInfo.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ProcessInfo" +#include <utils/Log.h> + +#include <media/stagefright/ProcessInfo.h> + +#include <binder/IProcessInfoService.h> +#include <binder/IServiceManager.h> + +namespace android { + +ProcessInfo::ProcessInfo() {} + +bool ProcessInfo::getPriority(int pid, int* priority) { + sp<IBinder> binder = defaultServiceManager()->getService(String16("processinfo")); + sp<IProcessInfoService> service = interface_cast<IProcessInfoService>(binder); + + size_t length = 1; + int32_t states; + status_t err = service->getProcessStatesFromPids(length, &pid, &states); + if (err != OK) { + ALOGE("getProcessStatesFromPids failed"); + return false; + } + ALOGV("pid %d states %d", pid, states); + if (states < 0) { + return false; + } + + // Use process state as the priority. Lower the value, higher the priority. + *priority = states; + return true; +} + +ProcessInfo::~ProcessInfo() {} + +} // namespace android diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index 530383b..e8abf48 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -26,6 +26,7 @@ #include <media/hardware/MetadataBufferType.h> #include <ui/GraphicBuffer.h> +#include <gui/BufferItem.h> #include <gui/ISurfaceComposer.h> #include <gui/IGraphicBufferAlloc.h> #include <OMX_Component.h> @@ -290,7 +291,7 @@ status_t SurfaceMediaSource::read( // TODO: mCurrentSlot can be made a bufferstate since there // can be more than one "current" slots. - BufferQueue::BufferItem item; + BufferItem item; // If the recording has started and the queue is empty, then just // wait here till the frames come in from the client side while (mStarted) { diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp index 5ec3438..8ef2dca 100644 --- a/media/libstagefright/avc_utils.cpp +++ b/media/libstagefright/avc_utils.cpp @@ -26,6 +26,7 @@ #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> +#include <utils/misc.h> namespace android { @@ -186,17 +187,31 @@ void FindAVCDimensions( if (aspect_ratio_idc == 255 /* extendedSAR */) { sar_width = br.getBits(16); sar_height = br.getBits(16); - } else if (aspect_ratio_idc > 0 && aspect_ratio_idc < 14) { - static const int32_t kFixedSARWidth[] = { - 1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160 + } else { + static const struct { unsigned width, height; } kFixedSARs[] = { + { 0, 0 }, // Invalid + { 1, 1 }, + { 12, 11 }, + { 10, 11 }, + { 16, 11 }, + { 40, 33 }, + { 24, 11 }, + { 20, 11 }, + { 32, 11 }, + { 80, 33 }, + { 18, 11 }, + { 15, 11 }, + { 64, 33 }, + { 160, 99 }, + { 4, 3 }, + { 3, 2 }, + { 2, 1 }, }; - static const int32_t kFixedSARHeight[] = { - 1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99 - }; - - sar_width = kFixedSARWidth[aspect_ratio_idc - 1]; - sar_height = kFixedSARHeight[aspect_ratio_idc - 1]; + if (aspect_ratio_idc > 0 && aspect_ratio_idc < NELEM(kFixedSARs)) { + sar_width = kFixedSARs[aspect_ratio_idc].width; + sar_height = kFixedSARs[aspect_ratio_idc].height; + } } } diff --git a/media/libstagefright/filters/GraphicBufferListener.cpp b/media/libstagefright/filters/GraphicBufferListener.cpp index fa38192..66374ba 100644 --- a/media/libstagefright/filters/GraphicBufferListener.cpp +++ b/media/libstagefright/filters/GraphicBufferListener.cpp @@ -21,6 +21,8 @@ #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MediaErrors.h> +#include <gui/BufferItem.h> + #include "GraphicBufferListener.h" namespace android { @@ -83,8 +85,8 @@ void GraphicBufferListener::onSidebandStreamChanged() { // nothing to do } -BufferQueue::BufferItem GraphicBufferListener::getBufferItem() { - BufferQueue::BufferItem item; +BufferItem GraphicBufferListener::getBufferItem() { + BufferItem item; { Mutex::Autolock autoLock(mMutex); @@ -124,8 +126,7 @@ BufferQueue::BufferItem GraphicBufferListener::getBufferItem() { return item; } -sp<GraphicBuffer> GraphicBufferListener::getBuffer( - BufferQueue::BufferItem item) { +sp<GraphicBuffer> GraphicBufferListener::getBuffer(BufferItem item) { sp<GraphicBuffer> buf; if (item.mBuf < 0 || item.mBuf >= BufferQueue::NUM_BUFFER_SLOTS) { ALOGE("getBuffer() received invalid BufferItem: mBuf==%d", item.mBuf); @@ -138,8 +139,7 @@ sp<GraphicBuffer> GraphicBufferListener::getBuffer( return buf; } -status_t GraphicBufferListener::releaseBuffer( - BufferQueue::BufferItem item) { +status_t GraphicBufferListener::releaseBuffer(BufferItem item) { if (item.mBuf < 0 || item.mBuf >= BufferQueue::NUM_BUFFER_SLOTS) { ALOGE("getBuffer() received invalid BufferItem: mBuf==%d", item.mBuf); return ERROR_OUT_OF_RANGE; diff --git a/media/libstagefright/filters/GraphicBufferListener.h b/media/libstagefright/filters/GraphicBufferListener.h index b3e0ee3..586bf65 100644 --- a/media/libstagefright/filters/GraphicBufferListener.h +++ b/media/libstagefright/filters/GraphicBufferListener.h @@ -41,9 +41,9 @@ public: return mProducer; } - BufferQueue::BufferItem getBufferItem(); - sp<GraphicBuffer> getBuffer(BufferQueue::BufferItem item); - status_t releaseBuffer(BufferQueue::BufferItem item); + BufferItem getBufferItem(); + sp<GraphicBuffer> getBuffer(BufferItem item); + status_t releaseBuffer(BufferItem item); enum { kWhatFrameAvailable = 'frav', diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp index d2f662d..0a09575 100644 --- a/media/libstagefright/filters/MediaFilter.cpp +++ b/media/libstagefright/filters/MediaFilter.cpp @@ -31,6 +31,8 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaFilter.h> +#include <gui/BufferItem.h> + #include "ColorConvert.h" #include "GraphicBufferListener.h" #include "IntrinsicBlurFilter.h" @@ -749,7 +751,7 @@ void MediaFilter::onCreateInputSurface() { } void MediaFilter::onInputFrameAvailable() { - BufferQueue::BufferItem item = mGraphicBufferListener->getBufferItem(); + BufferItem item = mGraphicBufferListener->getBufferItem(); sp<GraphicBuffer> buf = mGraphicBufferListener->getBuffer(item); // get pointer to graphic buffer diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index d81da3f..477cfc6 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -28,6 +28,7 @@ #include <media/hardware/MetadataBufferType.h> #include <ui/GraphicBuffer.h> +#include <gui/BufferItem.h> #include <inttypes.h> #include "FrameDropper.h" @@ -360,7 +361,7 @@ void GraphicBufferSource::suspend(bool suspend) { mSuspended = true; while (mNumFramesAvailable > 0) { - BufferQueue::BufferItem item; + BufferItem item; status_t err = mConsumer->acquireBuffer(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { @@ -410,7 +411,7 @@ bool GraphicBufferSource::fillCodecBuffer_l() { ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%zu", mNumFramesAvailable); - BufferQueue::BufferItem item; + BufferItem item; status_t err = mConsumer->acquireBuffer(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // shouldn't happen @@ -503,7 +504,7 @@ bool GraphicBufferSource::repeatLatestBuffer_l() { return false; } - BufferQueue::BufferItem item; + BufferItem item; item.mBuf = mLatestBufferId; item.mFrameNumber = mLatestBufferFrameNum; item.mTimestamp = mRepeatLastFrameTimestamp; @@ -534,7 +535,7 @@ bool GraphicBufferSource::repeatLatestBuffer_l() { } void GraphicBufferSource::setLatestBuffer_l( - const BufferQueue::BufferItem &item, bool dropped) { + const BufferItem &item, bool dropped) { ALOGV("setLatestBuffer_l"); if (mLatestBufferId >= 0) { @@ -590,7 +591,7 @@ status_t GraphicBufferSource::signalEndOfInputStream() { return OK; } -int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) { +int64_t GraphicBufferSource::getTimestamp(const BufferItem &item) { int64_t timeUs = item.mTimestamp / 1000; if (mTimePerCaptureUs > 0ll) { @@ -651,7 +652,7 @@ int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) { } status_t GraphicBufferSource::submitBuffer_l( - const BufferQueue::BufferItem &item, int cbi) { + const BufferItem &item, int cbi) { ALOGV("submitBuffer_l cbi=%d", cbi); int64_t timeUs = getTimestamp(item); @@ -777,7 +778,7 @@ void GraphicBufferSource::onFrameAvailable(const BufferItem& /*item*/) { ALOGV("onFrameAvailable: suspended, ignoring frame"); } - BufferQueue::BufferItem item; + BufferItem item; status_t err = mConsumer->acquireBuffer(&item, 0); if (err == OK) { // If this is the first time we're seeing this buffer, add it to our diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index ce3881e..1067472 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -192,15 +192,15 @@ private: // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer // reference into the codec buffer, and submits the data to the codec. - status_t submitBuffer_l(const BufferQueue::BufferItem &item, int cbi); + status_t submitBuffer_l(const BufferItem &item, int cbi); // Submits an empty buffer, with the EOS flag set. Returns without // doing anything if we don't have a codec buffer available. void submitEndOfInputStream_l(); - void setLatestBuffer_l(const BufferQueue::BufferItem &item, bool dropped); + void setLatestBuffer_l(const BufferItem &item, bool dropped); bool repeatLatestBuffer_l(); - int64_t getTimestamp(const BufferQueue::BufferItem &item); + int64_t getTimestamp(const BufferItem &item); // Lock, covers all member variables. mutable Mutex mMutex; diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index f1b84ad..0ad0bf3 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -26,7 +26,8 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ liblog \ libbinder \ - libsoundtriggerservice + libsoundtriggerservice \ + libradioservice LOCAL_STATIC_LIBRARIES := \ libregistermsext @@ -38,7 +39,8 @@ LOCAL_C_INCLUDES := \ frameworks/av/services/audiopolicy \ frameworks/av/services/camera/libcameraservice \ $(call include-path-for, audio-utils) \ - frameworks/av/services/soundtrigger + frameworks/av/services/soundtrigger \ + frameworks/av/services/radio LOCAL_MODULE:= mediaserver LOCAL_32_BIT_ONLY := true diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp index 263dd32..99572f8 100644 --- a/media/mediaserver/main_mediaserver.cpp +++ b/media/mediaserver/main_mediaserver.cpp @@ -35,6 +35,7 @@ #include "MediaPlayerService.h" #include "service/AudioPolicyService.h" #include "SoundTriggerHwService.h" +#include "RadioService.h" using namespace android; @@ -130,6 +131,7 @@ int main(int argc __unused, char** argv) CameraService::instantiate(); AudioPolicyService::instantiate(); SoundTriggerHwService::instantiate(); + RadioService::instantiate(); registerExtensions(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); diff --git a/radio/Android.mk b/radio/Android.mk new file mode 100644 index 0000000..ecbb8fd --- /dev/null +++ b/radio/Android.mk @@ -0,0 +1,39 @@ +# Copyright 2014 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + Radio.cpp \ + IRadio.cpp \ + IRadioClient.cpp \ + IRadioService.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libbinder \ + libhardware \ + libradio_metadata + +#LOCAL_C_INCLUDES += \ + system/media/camera/include \ + system/media/private/camera/include + +LOCAL_MODULE:= libradio + +include $(BUILD_SHARED_LIBRARY) diff --git a/radio/IRadio.cpp b/radio/IRadio.cpp new file mode 100644 index 0000000..242df77 --- /dev/null +++ b/radio/IRadio.cpp @@ -0,0 +1,344 @@ +/* +** +** Copyright 2015, 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. +*/ + +#define LOG_TAG "IRadio" +#include <utils/Log.h> +#include <utils/Errors.h> +#include <binder/IMemory.h> +#include <radio/IRadio.h> +#include <radio/IRadioService.h> +#include <radio/IRadioClient.h> +#include <system/radio.h> +#include <system/radio_metadata.h> + +namespace android { + +enum { + DETACH = IBinder::FIRST_CALL_TRANSACTION, + SET_CONFIGURATION, + GET_CONFIGURATION, + SET_MUTE, + GET_MUTE, + SCAN, + STEP, + TUNE, + CANCEL, + GET_PROGRAM_INFORMATION, + HAS_CONTROL +}; + +class BpRadio: public BpInterface<IRadio> +{ +public: + BpRadio(const sp<IBinder>& impl) + : BpInterface<IRadio>(impl) + { + } + + void detach() + { + ALOGV("detach"); + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + remote()->transact(DETACH, data, &reply); + } + + virtual status_t setConfiguration(const struct radio_band_config *config) + { + Parcel data, reply; + if (config == NULL) { + return BAD_VALUE; + } + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + data.write(config, sizeof(struct radio_band_config)); + status_t status = remote()->transact(SET_CONFIGURATION, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t getConfiguration(struct radio_band_config *config) + { + Parcel data, reply; + if (config == NULL) { + return BAD_VALUE; + } + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_CONFIGURATION, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + if (status == NO_ERROR) { + reply.read(config, sizeof(struct radio_band_config)); + } + } + return status; + } + + virtual status_t setMute(bool mute) + { + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + data.writeInt32(mute ? 1 : 0); + status_t status = remote()->transact(SET_MUTE, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t getMute(bool *mute) + { + Parcel data, reply; + if (mute == NULL) { + return BAD_VALUE; + } + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_MUTE, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + if (status == NO_ERROR) { + int muteread = reply.readInt32(); + *mute = muteread != 0; + } + } + return status; + } + + virtual status_t scan(radio_direction_t direction, bool skipSubChannel) + { + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + data.writeInt32(direction); + data.writeInt32(skipSubChannel ? 1 : 0); + status_t status = remote()->transact(SCAN, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t step(radio_direction_t direction, bool skipSubChannel) + { + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + data.writeInt32(direction); + data.writeInt32(skipSubChannel ? 1 : 0); + status_t status = remote()->transact(STEP, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t tune(unsigned int channel, unsigned int subChannel) + { + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + data.writeInt32(channel); + data.writeInt32(subChannel); + status_t status = remote()->transact(TUNE, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t cancel() + { + Parcel data, reply; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + status_t status = remote()->transact(CANCEL, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t getProgramInformation(struct radio_program_info *info) + { + Parcel data, reply; + if (info == NULL) { + return BAD_VALUE; + } + radio_metadata_t *metadata = info->metadata; + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_PROGRAM_INFORMATION, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + if (status == NO_ERROR) { + reply.read(info, sizeof(struct radio_program_info)); + info->metadata = metadata; + if (metadata == NULL) { + return status; + } + size_t size = (size_t)reply.readInt32(); + if (size == 0) { + return status; + } + metadata = + (radio_metadata_t *)calloc(size / sizeof(unsigned int), sizeof(unsigned int)); + if (metadata == NULL) { + return NO_MEMORY; + } + reply.read(metadata, size); + status = radio_metadata_add_metadata(&info->metadata, metadata); + free(metadata); + } + } + return status; + } + + virtual status_t hasControl(bool *hasControl) + { + Parcel data, reply; + if (hasControl == NULL) { + return BAD_VALUE; + } + data.writeInterfaceToken(IRadio::getInterfaceDescriptor()); + status_t status = remote()->transact(HAS_CONTROL, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + if (status == NO_ERROR) { + *hasControl = reply.readInt32() != 0; + } + } + return status; + } +}; + +IMPLEMENT_META_INTERFACE(Radio, "android.hardware.IRadio"); + +// ---------------------------------------------------------------------- + +status_t BnRadio::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DETACH: { + ALOGV("DETACH"); + CHECK_INTERFACE(IRadio, data, reply); + detach(); + return NO_ERROR; + } break; + case SET_CONFIGURATION: { + CHECK_INTERFACE(IRadio, data, reply); + struct radio_band_config config; + data.read(&config, sizeof(struct radio_band_config)); + status_t status = setConfiguration(&config); + reply->writeInt32(status); + return NO_ERROR; + } + case GET_CONFIGURATION: { + CHECK_INTERFACE(IRadio, data, reply); + struct radio_band_config config; + status_t status = getConfiguration(&config); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->write(&config, sizeof(struct radio_band_config)); + } + return NO_ERROR; + } + case SET_MUTE: { + CHECK_INTERFACE(IRadio, data, reply); + bool mute = data.readInt32() != 0; + status_t status = setMute(mute); + reply->writeInt32(status); + return NO_ERROR; + } + case GET_MUTE: { + CHECK_INTERFACE(IRadio, data, reply); + bool mute; + status_t status = getMute(&mute); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->writeInt32(mute ? 1 : 0); + } + return NO_ERROR; + } + case SCAN: { + CHECK_INTERFACE(IRadio, data, reply); + radio_direction_t direction = (radio_direction_t)data.readInt32(); + bool skipSubChannel = data.readInt32() == 1; + status_t status = scan(direction, skipSubChannel); + reply->writeInt32(status); + return NO_ERROR; + } + case STEP: { + CHECK_INTERFACE(IRadio, data, reply); + radio_direction_t direction = (radio_direction_t)data.readInt32(); + bool skipSubChannel = data.readInt32() == 1; + status_t status = step(direction, skipSubChannel); + reply->writeInt32(status); + return NO_ERROR; + } + case TUNE: { + CHECK_INTERFACE(IRadio, data, reply); + unsigned int channel = (unsigned int)data.readInt32(); + unsigned int subChannel = (unsigned int)data.readInt32(); + status_t status = tune(channel, subChannel); + reply->writeInt32(status); + return NO_ERROR; + } + case CANCEL: { + CHECK_INTERFACE(IRadio, data, reply); + status_t status = cancel(); + reply->writeInt32(status); + return NO_ERROR; + } + case GET_PROGRAM_INFORMATION: { + CHECK_INTERFACE(IRadio, data, reply); + struct radio_program_info info; + + status_t status = radio_metadata_allocate(&info.metadata, 0, 0); + if (status != NO_ERROR) { + return status; + } + status = getProgramInformation(&info); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->write(&info, sizeof(struct radio_program_info)); + int count = radio_metadata_get_count(info.metadata); + if (count > 0) { + size_t size = radio_metadata_get_size(info.metadata); + reply->writeInt32(size); + reply->write(info.metadata, size); + } else { + reply->writeInt32(0); + } + } + radio_metadata_deallocate(info.metadata); + return NO_ERROR; + } + case HAS_CONTROL: { + CHECK_INTERFACE(IRadio, data, reply); + bool control; + status_t status = hasControl(&control); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->writeInt32(control ? 1 : 0); + } + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/radio/IRadioClient.cpp b/radio/IRadioClient.cpp new file mode 100644 index 0000000..033ca49 --- /dev/null +++ b/radio/IRadioClient.cpp @@ -0,0 +1,75 @@ +/* +** +** Copyright 2015, 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. +*/ + +#include <stdint.h> +#include <sys/types.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <radio/IRadioClient.h> + +namespace android { + +enum { + ON_EVENT = IBinder::FIRST_CALL_TRANSACTION, +}; + +class BpRadioClient: public BpInterface<IRadioClient> +{ + +public: + BpRadioClient(const sp<IBinder>& impl) + : BpInterface<IRadioClient>(impl) + { + } + + virtual void onEvent(const sp<IMemory>& eventMemory) + { + Parcel data, reply; + data.writeInterfaceToken(IRadioClient::getInterfaceDescriptor()); + data.writeStrongBinder(IInterface::asBinder(eventMemory)); + remote()->transact(ON_EVENT, + data, + &reply); + } +}; + +IMPLEMENT_META_INTERFACE(RadioClient, + "android.hardware.IRadioClient"); + +// ---------------------------------------------------------------------- + +status_t BnRadioClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ON_EVENT: { + CHECK_INTERFACE(IRadioClient, data, reply); + sp<IMemory> eventMemory = interface_cast<IMemory>( + data.readStrongBinder()); + onEvent(eventMemory); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/radio/IRadioService.cpp b/radio/IRadioService.cpp new file mode 100644 index 0000000..8c2b3ef --- /dev/null +++ b/radio/IRadioService.cpp @@ -0,0 +1,181 @@ +/* +** +** Copyright 2015, 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. +*/ + +#define LOG_TAG "BpRadioService" +// +#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Errors.h> + +#include <stdint.h> +#include <sys/types.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <radio/IRadioService.h> +#include <radio/IRadio.h> +#include <radio/IRadioClient.h> + +namespace android { + +enum { + LIST_MODULES = IBinder::FIRST_CALL_TRANSACTION, + ATTACH, +}; + +#define MAX_ITEMS_PER_LIST 1024 + +class BpRadioService: public BpInterface<IRadioService> +{ +public: + BpRadioService(const sp<IBinder>& impl) + : BpInterface<IRadioService>(impl) + { + } + + virtual status_t listModules(struct radio_properties *properties, + uint32_t *numModules) + { + if (numModules == NULL || (*numModules != 0 && properties == NULL)) { + return BAD_VALUE; + } + Parcel data, reply; + data.writeInterfaceToken(IRadioService::getInterfaceDescriptor()); + unsigned int numModulesReq = (properties == NULL) ? 0 : *numModules; + data.writeInt32(numModulesReq); + status_t status = remote()->transact(LIST_MODULES, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + *numModules = (unsigned int)reply.readInt32(); + } + ALOGV("listModules() status %d got *numModules %d", status, *numModules); + if (status == NO_ERROR) { + if (numModulesReq > *numModules) { + numModulesReq = *numModules; + } + if (numModulesReq > 0) { + reply.read(properties, numModulesReq * sizeof(struct radio_properties)); + } + } + return status; + } + + virtual status_t attach(radio_handle_t handle, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool withAudio, + sp<IRadio>& radio) + { + Parcel data, reply; + data.writeInterfaceToken(IRadioService::getInterfaceDescriptor()); + data.writeInt32(handle); + data.writeStrongBinder(IInterface::asBinder(client)); + ALOGV("attach() config %p withAudio %d region %d type %d", config, withAudio, config->region, config->band.type); + if (config == NULL) { + data.writeInt32(0); + } else { + data.writeInt32(1); + data.write(config, sizeof(struct radio_band_config)); + } + data.writeInt32(withAudio ? 1 : 0); + status_t status = remote()->transact(ATTACH, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = reply.readInt32(); + if (reply.readInt32() != 0) { + radio = interface_cast<IRadio>(reply.readStrongBinder()); + } + return status; + } +}; + +IMPLEMENT_META_INTERFACE(RadioService, "android.hardware.IRadioService"); + +// ---------------------------------------------------------------------- + +status_t BnRadioService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case LIST_MODULES: { + CHECK_INTERFACE(IRadioService, data, reply); + unsigned int numModulesReq = data.readInt32(); + if (numModulesReq > MAX_ITEMS_PER_LIST) { + numModulesReq = MAX_ITEMS_PER_LIST; + } + unsigned int numModules = numModulesReq; + struct radio_properties *properties = + (struct radio_properties *)calloc(numModulesReq, + sizeof(struct radio_properties)); + if (properties == NULL) { + reply->writeInt32(NO_MEMORY); + reply->writeInt32(0); + return NO_ERROR; + } + + status_t status = listModules(properties, &numModules); + reply->writeInt32(status); + reply->writeInt32(numModules); + ALOGV("LIST_MODULES status %d got numModules %d", status, numModules); + + if (status == NO_ERROR) { + if (numModulesReq > numModules) { + numModulesReq = numModules; + } + reply->write(properties, + numModulesReq * sizeof(struct radio_properties)); + } + free(properties); + return NO_ERROR; + } break; + + case ATTACH: { + CHECK_INTERFACE(IRadioService, data, reply); + radio_handle_t handle = data.readInt32(); + sp<IRadioClient> client = + interface_cast<IRadioClient>(data.readStrongBinder()); + struct radio_band_config config; + struct radio_band_config *configPtr = NULL; + if (data.readInt32() != 0) { + data.read(&config, sizeof(struct radio_band_config)); + configPtr = &config; + } + bool withAudio = data.readInt32() != 0; + ALOGV("ATTACH configPtr %p withAudio %d", configPtr, withAudio); + sp<IRadio> radio; + status_t status = attach(handle, client, configPtr, withAudio, radio); + reply->writeInt32(status); + if (radio != 0) { + reply->writeInt32(1); + reply->writeStrongBinder(IInterface::asBinder(radio)); + } else { + reply->writeInt32(0); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/radio/Radio.cpp b/radio/Radio.cpp new file mode 100644 index 0000000..e3554c2 --- /dev/null +++ b/radio/Radio.cpp @@ -0,0 +1,283 @@ +/* +** +** Copyright (C) 2015, 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. +*/ + +#define LOG_TAG "Radio" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/threads.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/IMemory.h> + +#include <radio/Radio.h> +#include <radio/IRadio.h> +#include <radio/IRadioService.h> +#include <radio/IRadioClient.h> +#include <radio/RadioCallback.h> + +namespace android { + +namespace { + sp<IRadioService> gRadioService; + const int kRadioServicePollDelay = 500000; // 0.5s + const char* kRadioServiceName = "media.radio"; + Mutex gLock; + + class DeathNotifier : public IBinder::DeathRecipient + { + public: + DeathNotifier() { + } + + virtual void binderDied(const wp<IBinder>& who __unused) { + ALOGV("binderDied"); + Mutex::Autolock _l(gLock); + gRadioService.clear(); + ALOGW("Radio service died!"); + } + }; + + sp<DeathNotifier> gDeathNotifier; +}; // namespace anonymous + +const sp<IRadioService>& Radio::getRadioService() +{ + Mutex::Autolock _l(gLock); + if (gRadioService.get() == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16(kRadioServiceName)); + if (binder != 0) { + break; + } + ALOGW("RadioService not published, waiting..."); + usleep(kRadioServicePollDelay); + } while(true); + if (gDeathNotifier == NULL) { + gDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(gDeathNotifier); + gRadioService = interface_cast<IRadioService>(binder); + } + ALOGE_IF(gRadioService == 0, "no RadioService!?"); + return gRadioService; +} + +// Static methods +status_t Radio::listModules(struct radio_properties *properties, + uint32_t *numModules) +{ + ALOGV("listModules()"); + const sp<IRadioService>& service = getRadioService(); + if (service == 0) { + return NO_INIT; + } + return service->listModules(properties, numModules); +} + +sp<Radio> Radio::attach(radio_handle_t handle, + const struct radio_band_config *config, + bool withAudio, + const sp<RadioCallback>& callback) +{ + ALOGV("attach()"); + sp<Radio> radio; + const sp<IRadioService>& service = getRadioService(); + if (service == 0) { + return radio; + } + radio = new Radio(handle, callback); + status_t status = service->attach(handle, radio, config, withAudio, radio->mIRadio); + + if (status == NO_ERROR && radio->mIRadio != 0) { + IInterface::asBinder(radio->mIRadio)->linkToDeath(radio); + } else { + ALOGW("Error %d connecting to radio service", status); + radio.clear(); + } + return radio; +} + + + +// Radio +Radio::Radio(radio_handle_t handle, const sp<RadioCallback>& callback) + : mHandle(handle), mCallback(callback) +{ +} + +Radio::~Radio() +{ + if (mIRadio != 0) { + mIRadio->detach(); + } +} + + +void Radio::detach() { + ALOGV("detach()"); + Mutex::Autolock _l(mLock); + mCallback.clear(); + if (mIRadio != 0) { + mIRadio->detach(); + IInterface::asBinder(mIRadio)->unlinkToDeath(this); + mIRadio = 0; + } +} + +status_t Radio::setConfiguration(const struct radio_band_config *config) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->setConfiguration(config); +} + +status_t Radio::getConfiguration(struct radio_band_config *config) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->getConfiguration(config); +} + +status_t Radio::setMute(bool mute) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->setMute(mute); +} + +status_t Radio::getMute(bool *mute) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->getMute(mute); +} + +status_t Radio::scan(radio_direction_t direction, bool skipSubchannel) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->scan(direction, skipSubchannel); +} + +status_t Radio::step(radio_direction_t direction, bool skipSubchannel) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->step(direction, skipSubchannel); +} + +status_t Radio::tune(unsigned int channel, unsigned int subChannel) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->tune(channel, subChannel); +} + +status_t Radio::cancel() +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->cancel(); +} + +status_t Radio::getProgramInformation(struct radio_program_info *info) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->getProgramInformation(info); +} + +status_t Radio::hasControl(bool *hasControl) +{ + Mutex::Autolock _l(mLock); + if (mIRadio == 0) { + return NO_INIT; + } + return mIRadio->hasControl(hasControl); +} + + +// BpRadioClient +void Radio::onEvent(const sp<IMemory>& eventMemory) +{ + Mutex::Autolock _l(mLock); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + + struct radio_event *event = (struct radio_event *)eventMemory->pointer(); + // restore local metadata pointer from offset + switch (event->type) { + case RADIO_EVENT_TUNED: + case RADIO_EVENT_AF_SWITCH: + if (event->info.metadata != NULL) { + event->info.metadata = + (radio_metadata_t *)((char *)event + (size_t)event->info.metadata); + } + break; + case RADIO_EVENT_METADATA: + if (event->metadata != NULL) { + event->metadata = + (radio_metadata_t *)((char *)event + (size_t)event->metadata); + } + break; + default: + break; + } + + if (mCallback != 0) { + mCallback->onEvent(event); + } +} + + +//IBinder::DeathRecipient +void Radio::binderDied(const wp<IBinder>& who __unused) { + Mutex::Autolock _l(mLock); + ALOGW("Radio server binder Died "); + mIRadio = 0; + struct radio_event event; + memset(&event, 0, sizeof(struct radio_event)); + event.type = RADIO_EVENT_SERVER_DIED; + event.status = DEAD_OBJECT; + if (mCallback != 0) { + mCallback->onEvent(&event); + } +} + +}; // namespace android diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index e070f90..f1cf0aa 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -415,6 +415,7 @@ void FastMixer::onWork() memset(mMixerBuffer, 0, mMixerBufferSize); mMixerBufferState = ZEROED; } + // prepare the buffer used to write to sink void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer; if (mFormat.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format memcpy_by_audio_format(buffer, mFormat.mFormat, mMixerBuffer, mMixerBufferFormat, diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 9350e48..d600ea9 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -427,7 +427,7 @@ protected: bool mStandby; // Whether thread is currently in standby. audio_devices_t mOutDevice; // output device audio_devices_t mInDevice; // input device - audio_source_t mAudioSource; // (see audio.h, audio_source_t) + audio_source_t mAudioSource; const audio_io_handle_t mId; Vector< sp<EffectChain> > mEffectChains; diff --git a/services/audiopolicy/managerdefault/Devices.h b/services/audiopolicy/managerdefault/Devices.h index 65e1416..af2fbda 100644 --- a/services/audiopolicy/managerdefault/Devices.h +++ b/services/audiopolicy/managerdefault/Devices.h @@ -41,7 +41,6 @@ public: audio_devices_t mDeviceType; String8 mAddress; - audio_port_handle_t mId; static String8 emptyNameStr; }; diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp index ea5dcdd..a5a2fec 100644 --- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp @@ -27,6 +27,7 @@ #include <utils/Log.h> #include <utils/Trace.h> +#include <gui/BufferItem.h> #include <gui/Surface.h> #include <media/hardware/MetadataBufferType.h> @@ -679,7 +680,7 @@ status_t StreamingProcessor::processRecordingFrame() { sp<Camera2Client> client = mClient.promote(); if (client == 0) { // Discard frames during shutdown - BufferItemConsumer::BufferItem imgBuffer; + BufferItem imgBuffer; res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0); if (res != OK) { if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { @@ -697,7 +698,7 @@ status_t StreamingProcessor::processRecordingFrame() { with Camera2Client code calling into StreamingProcessor */ SharedParameters::Lock l(client->getParameters()); Mutex::Autolock m(mMutex); - BufferItemConsumer::BufferItem imgBuffer; + BufferItem imgBuffer; res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0); if (res != OK) { if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { @@ -823,8 +824,7 @@ void StreamingProcessor::releaseRecordingFrame(const sp<IMemory>& mem) { size_t itemIndex; for (itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) { - const BufferItemConsumer::BufferItem item = - mRecordingBuffers[itemIndex]; + const BufferItem item = mRecordingBuffers[itemIndex]; if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT && item.mGraphicBuffer->handle == imgHandle) { break; @@ -868,8 +868,7 @@ void StreamingProcessor::releaseAllRecordingFramesLocked() { size_t releasedCount = 0; for (size_t itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) { - const BufferItemConsumer::BufferItem item = - mRecordingBuffers[itemIndex]; + const BufferItem item = mRecordingBuffers[itemIndex]; if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT) { res = mRecordingConsumer->releaseBuffer(mRecordingBuffers[itemIndex]); if (res != OK) { diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h index 1d679a4..2474062 100644 --- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h +++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h @@ -124,7 +124,7 @@ class StreamingProcessor: static const size_t kDefaultRecordingHeapCount = 8; size_t mRecordingHeapCount; - Vector<BufferItemConsumer::BufferItem> mRecordingBuffers; + Vector<BufferItem> mRecordingBuffers; size_t mRecordingHeapHead, mRecordingHeapFree; virtual bool threadLoop(); diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp index db7e10d..68aca2d 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp @@ -440,7 +440,7 @@ status_t ZslProcessor::processNewZslBuffer() { zslConsumer = mZslConsumer; } ALOGVV("Trying to get next buffer"); - BufferItemConsumer::BufferItem item; + BufferItem item; res = zslConsumer->acquireBuffer(&item, 0); if (res != OK) { if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.h b/services/camera/libcameraservice/api1/client2/ZslProcessor.h index 2099c38..5f50d7b 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor.h +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.h @@ -22,6 +22,7 @@ #include <utils/Vector.h> #include <utils/Mutex.h> #include <utils/Condition.h> +#include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> #include <camera/CameraMetadata.h> #include <camera/CaptureResult.h> @@ -103,7 +104,7 @@ class ZslProcessor: sp<ANativeWindow> mZslWindow; struct ZslPair { - BufferItemConsumer::BufferItem buffer; + BufferItem buffer; CameraMetadata frame; }; diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h index fc9f70c..2960478 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h @@ -22,6 +22,7 @@ #include <utils/Vector.h> #include <utils/Mutex.h> #include <utils/Condition.h> +#include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> #include <camera/CameraMetadata.h> @@ -104,7 +105,7 @@ class ZslProcessor3 : sp<camera3::Camera3ZslStream> mZslStream; struct ZslPair { - BufferItemConsumer::BufferItem buffer; + BufferItem buffer; CameraMetadata frame; }; diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp index 85ed88d..87907bf 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp @@ -18,6 +18,7 @@ #define ATRACE_TAG ATRACE_TAG_CAMERA //#define LOG_NDEBUG 0 +#include <gui/BufferItem.h> #include <utils/Log.h> #include <utils/Trace.h> #include "Camera3InputStream.h" diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h index ae49467..fd17f4f 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.h +++ b/services/camera/libcameraservice/device3/Camera3InputStream.h @@ -48,8 +48,6 @@ class Camera3InputStream : public Camera3IOStreamBase { private: - typedef BufferItemConsumer::BufferItem BufferItem; - sp<BufferItemConsumer> mConsumer; Vector<BufferItem> mBuffersInFlight; diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp index d0f29de..8cd6800 100644 --- a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp +++ b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp @@ -268,7 +268,7 @@ status_t RingBufferConsumer::releaseOldestBufferLocked(size_t* pinnedFrames) { return OK; } -void RingBufferConsumer::onFrameAvailable(const android::BufferItem& item) { +void RingBufferConsumer::onFrameAvailable(const BufferItem& item) { status_t err; { diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h index 90fd734..83e7298 100644 --- a/services/camera/libcameraservice/gui/RingBufferConsumer.h +++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h @@ -17,6 +17,7 @@ #ifndef ANDROID_GUI_RINGBUFFERCONSUMER_H #define ANDROID_GUI_RINGBUFFERCONSUMER_H +#include <gui/BufferItem.h> #include <gui/ConsumerBase.h> #include <ui/GraphicBuffer.h> @@ -54,8 +55,6 @@ class RingBufferConsumer : public ConsumerBase, public: typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; - typedef BufferQueue::BufferItem BufferItem; - enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT }; enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE }; @@ -165,7 +164,7 @@ class RingBufferConsumer : public ConsumerBase, private: // Override ConsumerBase::onFrameAvailable - virtual void onFrameAvailable(const android::BufferItem& item); + virtual void onFrameAvailable(const BufferItem& item); void pinBufferLocked(const BufferItem& item); void unpinBuffer(const BufferItem& item); diff --git a/services/radio/Android.mk b/services/radio/Android.mk new file mode 100644 index 0000000..5e89b22 --- /dev/null +++ b/services/radio/Android.mk @@ -0,0 +1,35 @@ +# Copyright 2014 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + + +LOCAL_SRC_FILES:= \ + RadioService.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libui \ + liblog \ + libutils \ + libbinder \ + libcutils \ + libhardware \ + libradio \ + libradio_metadata + +LOCAL_MODULE:= libradioservice + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/radio/RadioRegions.h b/services/radio/RadioRegions.h new file mode 100644 index 0000000..3335b8a --- /dev/null +++ b/services/radio/RadioRegions.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_HARDWARE_RADIO_REGIONS_H +#define ANDROID_HARDWARE_RADIO_REGIONS_H + +namespace android { + +#define RADIO_BAND_LOWER_FM_ITU1 87500 +#define RADIO_BAND_UPPER_FM_ITU1 108000 +#define RADIO_BAND_SPACING_FM_ITU1 100 + +#define RADIO_BAND_LOWER_FM_ITU2 87900 +#define RADIO_BAND_UPPER_FM_ITU2 107900 +#define RADIO_BAND_SPACING_FM_ITU2 200 + +#define RADIO_BAND_LOWER_FM_JAPAN 76000 +#define RADIO_BAND_UPPER_FM_JAPAN 90000 +#define RADIO_BAND_SPACING_FM_JAPAN 100 + +#define RADIO_BAND_LOWER_FM_OIRT 65800 +#define RADIO_BAND_UPPER_FM_OIRT 74000 +#define RADIO_BAND_SPACING_FM_OIRT 10 + +#define RADIO_BAND_LOWER_LW 153 +#define RADIO_BAND_UPPER_LW 279 +#define RADIO_BAND_SPACING_LW 9 + +#define RADIO_BAND_LOWER_MW_IUT1 531 +#define RADIO_BAND_UPPER_MW_ITU1 1611 +#define RADIO_BAND_SPACING_MW_ITU1 9 + +#define RADIO_BAND_LOWER_MW_IUT2 540 +#define RADIO_BAND_UPPER_MW_ITU2 1610 +#define RADIO_BAND_SPACING_MW_ITU2 10 + +#define RADIO_BAND_LOWER_SW 2300 +#define RADIO_BAND_UPPER_SW 26100 +#define RADIO_BAND_SPACING_SW 5 + + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +const radio_band_config_t sKnownRegionConfigs[] = { + { // FM ITU 1 + RADIO_REGION_ITU_1, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_ITU1, + RADIO_BAND_UPPER_FM_ITU1, + 1, + {RADIO_BAND_SPACING_FM_ITU1}, + { + RADIO_DEEMPHASIS_50, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM Americas + RADIO_REGION_ITU_2, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_ITU2, + RADIO_BAND_UPPER_FM_ITU2, + 1, + {RADIO_BAND_SPACING_FM_ITU2}, + { + RADIO_DEEMPHASIS_75, + true, + RADIO_RDS_US, + true, + true, + } + } + }, + { // FM Japan + RADIO_REGION_JAPAN, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_JAPAN, + RADIO_BAND_UPPER_FM_JAPAN, + 1, + {RADIO_BAND_SPACING_FM_JAPAN}, + { + RADIO_DEEMPHASIS_50, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM Korea + RADIO_REGION_KOREA, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_ITU1, + RADIO_BAND_UPPER_FM_ITU1, + 1, + {RADIO_BAND_SPACING_FM_ITU1}, + { + RADIO_DEEMPHASIS_75, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM OIRT + RADIO_REGION_OIRT, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_OIRT, + RADIO_BAND_UPPER_FM_OIRT, + 1, + {RADIO_BAND_SPACING_FM_OIRT}, + { + RADIO_DEEMPHASIS_50, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM US HD radio + RADIO_REGION_ITU_2, + { + RADIO_BAND_FM_HD, + false, + RADIO_BAND_LOWER_FM_ITU2, + RADIO_BAND_UPPER_FM_ITU2, + 1, + {RADIO_BAND_SPACING_FM_ITU2}, + { + RADIO_DEEMPHASIS_75, + true, + RADIO_RDS_US, + true, + true, + } + } + }, + { // AM LW + RADIO_REGION_ITU_1, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_LW, + RADIO_BAND_UPPER_LW, + 1, + {RADIO_BAND_SPACING_LW}, + { + } + } + }, + { // AM SW + RADIO_REGION_ITU_1, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_SW, + RADIO_BAND_UPPER_SW, + 1, + {RADIO_BAND_SPACING_SW}, + { + } + } + }, + { // AM MW ITU1 + RADIO_REGION_ITU_1, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_MW_IUT1, + RADIO_BAND_UPPER_MW_ITU1, + 1, + {RADIO_BAND_SPACING_MW_ITU1}, + { + } + } + }, + { // AM MW ITU2 + RADIO_REGION_ITU_2, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_MW_IUT2, + RADIO_BAND_UPPER_MW_ITU2, + 1, + {RADIO_BAND_SPACING_MW_ITU2}, + { + } + } + } +}; + + +} // namespace android + +#endif // ANDROID_HARDWARE_RADIO_REGIONS_H diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp new file mode 100644 index 0000000..152619b --- /dev/null +++ b/services/radio/RadioService.cpp @@ -0,0 +1,845 @@ +/* + * Copyright (C) 2015 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. + */ + +#define LOG_TAG "RadioService" +//#define LOG_NDEBUG 0 + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <pthread.h> + +#include <system/radio.h> +#include <system/radio_metadata.h> +#include <cutils/atomic.h> +#include <cutils/properties.h> +#include <hardware/hardware.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <binder/IServiceManager.h> +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <hardware/radio.h> +#include "RadioService.h" +#include "RadioRegions.h" + +namespace android { + + +RadioService::RadioService() + : BnRadioService(), mNextUniqueId(1) +{ + ALOGI("%s", __FUNCTION__); +} + +void RadioService::onFirstRef() +{ + const hw_module_t *mod; + int rc; + struct radio_hw_device *dev; + + ALOGI("%s", __FUNCTION__); + + rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, RADIO_HARDWARE_MODULE_ID_FM, &mod); + if (rc != 0) { + ALOGE("couldn't load radio module %s.%s (%s)", + RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + rc = radio_hw_device_open(mod, &dev); + if (rc != 0) { + ALOGE("couldn't open radio hw device in %s.%s (%s)", + RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + if (dev->common.version != RADIO_DEVICE_API_VERSION_CURRENT) { + ALOGE("wrong radio hw device version %04x", dev->common.version); + return; + } + + struct radio_hal_properties halProperties; + rc = dev->get_properties(dev, &halProperties); + if (rc != 0) { + ALOGE("could not read implementation properties"); + return; + } + + radio_properties_t properties; + properties.handle = + (radio_handle_t)android_atomic_inc(&mNextUniqueId); + + ALOGI("loaded default module %s, handle %d", properties.product, properties.handle); + + convertProperties(&properties, &halProperties); + sp<Module> module = new Module(this, dev, properties); + mModules.add(properties.handle, module); +} + +RadioService::~RadioService() +{ + for (size_t i = 0; i < mModules.size(); i++) { + radio_hw_device_close(mModules.valueAt(i)->hwDevice()); + } +} + +status_t RadioService::listModules(struct radio_properties *properties, + uint32_t *numModules) +{ + ALOGV("listModules"); + + AutoMutex lock(mServiceLock); + if (numModules == NULL || (*numModules != 0 && properties == NULL)) { + return BAD_VALUE; + } + size_t maxModules = *numModules; + *numModules = mModules.size(); + for (size_t i = 0; i < mModules.size() && i < maxModules; i++) { + properties[i] = mModules.valueAt(i)->properties(); + } + return NO_ERROR; +} + +status_t RadioService::attach(radio_handle_t handle, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool withAudio, + sp<IRadio>& radio) +{ + ALOGV("%s %d config %p withAudio %d", __FUNCTION__, handle, config, withAudio); + + AutoMutex lock(mServiceLock); + radio.clear(); + if (client == 0) { + return BAD_VALUE; + } + ssize_t index = mModules.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + sp<Module> module = mModules.valueAt(index); + + if (config == NULL) { + config = module->getDefaultConfig(); + if (config == NULL) { + return INVALID_OPERATION; + } + } + ALOGV("%s region %d type %d", __FUNCTION__, config->region, config->band.type); + + radio = module->addClient(client, config, withAudio); + + if (radio == 0) { + NO_INIT; + } + return NO_ERROR; +} + + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 60000; + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleep); + } + return locked; +} + +status_t RadioService::dump(int fd, const Vector<String16>& args __unused) { + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + result.appendFormat("Permission Denial: can't dump RadioService"); + write(fd, result.string(), result.size()); + } else { + bool locked = tryLock(mServiceLock); + // failed to lock - RadioService is probably deadlocked + if (!locked) { + result.append("RadioService may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + if (locked) mServiceLock.unlock(); + } + return NO_ERROR; +} + +status_t RadioService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + return BnRadioService::onTransact(code, data, reply, flags); +} + + +// static +void RadioService::callback(radio_hal_event_t *halEvent, void *cookie) +{ + CallbackThread *callbackThread = (CallbackThread *)cookie; + if (callbackThread == NULL) { + return; + } + callbackThread->sendEvent(halEvent); +} + +/* static */ +void RadioService::convertProperties(radio_properties_t *properties, + const radio_hal_properties_t *halProperties) +{ + memset(properties, 0, sizeof(struct radio_properties)); + properties->class_id = halProperties->class_id; + strlcpy(properties->implementor, halProperties->implementor, + RADIO_STRING_LEN_MAX); + strlcpy(properties->product, halProperties->product, + RADIO_STRING_LEN_MAX); + strlcpy(properties->version, halProperties->version, + RADIO_STRING_LEN_MAX); + strlcpy(properties->serial, halProperties->serial, + RADIO_STRING_LEN_MAX); + properties->num_tuners = halProperties->num_tuners; + properties->num_audio_sources = halProperties->num_audio_sources; + properties->supports_capture = halProperties->supports_capture; + + for (size_t i = 0; i < ARRAY_SIZE(sKnownRegionConfigs); i++) { + const radio_hal_band_config_t *band = &sKnownRegionConfigs[i].band; + size_t j; + for (j = 0; j < halProperties->num_bands; j++) { + const radio_hal_band_config_t *halBand = &halProperties->bands[j]; + size_t k; + if (band->type != halBand->type) continue; + if (band->lower_limit < halBand->lower_limit) continue; + if (band->upper_limit > halBand->upper_limit) continue; + for (k = 0; k < halBand->num_spacings; k++) { + if (band->spacings[0] == halBand->spacings[k]) break; + } + if (k == halBand->num_spacings) continue; + if (band->type == RADIO_BAND_AM) break; + if ((band->fm.deemphasis & halBand->fm.deemphasis) == 0) continue; + if (halBand->fm.rds == 0) break; + if ((band->fm.rds & halBand->fm.rds) != 0) break; + } + if (j == halProperties->num_bands) continue; + + ALOGI("convertProperties() Adding band type %d region %d", + sKnownRegionConfigs[i].band.type , sKnownRegionConfigs[i].region); + + memcpy(&properties->bands[properties->num_bands++], + &sKnownRegionConfigs[i], + sizeof(radio_band_config_t)); + } +} + +#undef LOG_TAG +#define LOG_TAG "RadioService::CallbackThread" + +RadioService::CallbackThread::CallbackThread(const wp<ModuleClient>& moduleClient) + : mModuleClient(moduleClient), mMemoryDealer(new MemoryDealer(1024 * 1024, "RadioService")) +{ +} + +RadioService::CallbackThread::~CallbackThread() +{ + mEventQueue.clear(); +} + +void RadioService::CallbackThread::onFirstRef() +{ + run("RadioService cbk", ANDROID_PRIORITY_URGENT_AUDIO); +} + +bool RadioService::CallbackThread::threadLoop() +{ + while (!exitPending()) { + sp<IMemory> eventMemory; + sp<ModuleClient> moduleClient; + { + Mutex::Autolock _l(mCallbackLock); + while (mEventQueue.isEmpty() && !exitPending()) { + ALOGV("CallbackThread::threadLoop() sleep"); + mCallbackCond.wait(mCallbackLock); + ALOGV("CallbackThread::threadLoop() wake up"); + } + if (exitPending()) { + break; + } + eventMemory = mEventQueue[0]; + mEventQueue.removeAt(0); + moduleClient = mModuleClient.promote(); + } + if (moduleClient != 0) { + moduleClient->onCallbackEvent(eventMemory); + eventMemory.clear(); + } + } + return false; +} + +void RadioService::CallbackThread::exit() +{ + Mutex::Autolock _l(mCallbackLock); + requestExit(); + mCallbackCond.broadcast(); +} + +sp<IMemory> RadioService::CallbackThread::prepareEvent(radio_hal_event_t *halEvent) +{ + sp<IMemory> eventMemory; + + size_t headerSize = + (sizeof(struct radio_event) + sizeof(unsigned int) - 1) /sizeof(unsigned int); + size_t metadataSize = 0; + switch (halEvent->type) { + case RADIO_EVENT_TUNED: + case RADIO_EVENT_AF_SWITCH: + if (radio_metadata_check(halEvent->info.metadata) == 0) { + metadataSize = radio_metadata_get_size(halEvent->info.metadata); + } + break; + case RADIO_EVENT_METADATA: + if (radio_metadata_check(halEvent->metadata) != 0) { + return eventMemory; + } + metadataSize = radio_metadata_get_size(halEvent->metadata); + break; + default: + break; + } + size_t size = headerSize + metadataSize; + eventMemory = mMemoryDealer->allocate(size); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + eventMemory.clear(); + return eventMemory; + } + struct radio_event *event = (struct radio_event *)eventMemory->pointer(); + event->type = halEvent->type; + event->status = halEvent->status; + + switch (event->type) { + case RADIO_EVENT_CONFIG: + event->config.band = halEvent->config; + break; + case RADIO_EVENT_TUNED: + case RADIO_EVENT_AF_SWITCH: + event->info = halEvent->info; + if (metadataSize != 0) { + memcpy((char *)event + headerSize, halEvent->info.metadata, metadataSize); + // replace meta data pointer by offset while in shared memory so that receiving side + // can restore the pointer in destination process. + event->info.metadata = (radio_metadata_t *)headerSize; + } + break; + case RADIO_EVENT_TA: + case RADIO_EVENT_ANTENNA: + case RADIO_EVENT_CONTROL: + event->on = halEvent->on; + break; + case RADIO_EVENT_METADATA: + memcpy((char *)event + headerSize, halEvent->metadata, metadataSize); + // replace meta data pointer by offset while in shared memory so that receiving side + // can restore the pointer in destination process. + event->metadata = (radio_metadata_t *)headerSize; + break; + case RADIO_EVENT_HW_FAILURE: + default: + break; + } + + return eventMemory; +} + +void RadioService::CallbackThread::sendEvent(radio_hal_event_t *event) + { + sp<IMemory> eventMemory = prepareEvent(event); + if (eventMemory == 0) { + return; + } + + AutoMutex lock(mCallbackLock); + mEventQueue.add(eventMemory); + mCallbackCond.signal(); + ALOGV("%s DONE", __FUNCTION__); +} + + +#undef LOG_TAG +#define LOG_TAG "RadioService::Module" + +RadioService::Module::Module(const sp<RadioService>& service, + radio_hw_device* hwDevice, + radio_properties properties) + : mService(service), mHwDevice(hwDevice), mProperties(properties), mMute(true) +{ +} + +RadioService::Module::~Module() { + mModuleClients.clear(); +} + +status_t RadioService::Module::dump(int fd __unused, const Vector<String16>& args __unused) { + String8 result; + return NO_ERROR; +} + +sp<RadioService::ModuleClient> RadioService::Module::addClient(const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio) +{ + ALOGV("addClient() %p config %p product %s", this, config, mProperties.product); + AutoMutex lock(mLock); + sp<ModuleClient> moduleClient; + int ret; + + for (size_t i = 0; i < mModuleClients.size(); i++) { + if (mModuleClients[i]->client() == client) { + // client already connected: reject + return moduleClient; + } + } + moduleClient = new ModuleClient(this, client, config, audio); + + struct radio_hal_band_config halConfig; + halConfig = config->band; + + sp<ModuleClient> oldestTuner; + sp<ModuleClient> oldestAudio; + size_t allocatedTuners = 0; + size_t allocatedAudio = 0; + for (size_t i = 0; i < mModuleClients.size(); i++) { + if (mModuleClients[i]->getTuner() != NULL) { + if (mModuleClients[i]->audio()) { + if (oldestAudio == 0) { + oldestAudio = mModuleClients[i]; + } + allocatedAudio++; + } else { + if (oldestTuner == 0) { + oldestTuner = mModuleClients[i]; + } + allocatedTuners++; + } + } + } + + const struct radio_tuner *halTuner; + if (audio) { + if (allocatedAudio >= mProperties.num_audio_sources) { + ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch"); + halTuner = oldestAudio->getTuner(); + oldestAudio->setTuner(NULL); + mHwDevice->close_tuner(mHwDevice, halTuner); + } + } else { + if (allocatedAudio + allocatedTuners >= mProperties.num_tuners) { + if (allocatedTuners != 0) { + ALOG_ASSERT(oldestTuner != 0, "addClient() allocatedTuners/oldestTuner mismatch"); + halTuner = oldestTuner->getTuner(); + oldestTuner->setTuner(NULL); + mHwDevice->close_tuner(mHwDevice, halTuner); + } else { + ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch"); + halTuner = oldestAudio->getTuner(); + oldestAudio->setTuner(NULL); + mHwDevice->close_tuner(mHwDevice, halTuner); + } + } + } + + ret = mHwDevice->open_tuner(mHwDevice, &halConfig, audio, + RadioService::callback, moduleClient->callbackThread().get(), + &halTuner); + if (ret == 0) { + ALOGV("addClient() setTuner %p", halTuner); + moduleClient->setTuner(halTuner); + mModuleClients.add(moduleClient); + } else { + moduleClient.clear(); + } + + //TODO notify audio device connection to audio policy manager if audio is on + + ALOGV("addClient() DONE moduleClient %p", moduleClient.get()); + + return moduleClient; +} + +void RadioService::Module::removeClient(const sp<ModuleClient>& moduleClient) { + ALOGV("removeClient()"); + AutoMutex lock(mLock); + int ret; + ssize_t index = -1; + + for (size_t i = 0; i < mModuleClients.size(); i++) { + if (mModuleClients[i] == moduleClient) { + index = i; + break; + } + } + if (index == -1) { + return; + } + + mModuleClients.removeAt(index); + const struct radio_tuner *halTuner = moduleClient->getTuner(); + if (halTuner == NULL) { + return; + } + + mHwDevice->close_tuner(mHwDevice, halTuner); + + //TODO notify audio device disconnection to audio policy manager if audio was on + mMute = true; + + if (mModuleClients.isEmpty()) { + return; + } + + sp<ModuleClient> youngestClient; + sp<ModuleClient> youngestClientAudio; + size_t allocatedTuners = 0; + size_t allocatedAudio = 0; + for (ssize_t i = mModuleClients.size(); i >= 0; i--) { + if (mModuleClients[i]->getTuner() == NULL) { + if (mModuleClients[i]->audio()) { + if (youngestClientAudio == 0) { + youngestClientAudio = mModuleClients[i]; + } + } else { + if (youngestClient == 0) { + youngestClient = mModuleClients[i]; + } + } + } else { + if (mModuleClients[i]->audio()) { + allocatedAudio++; + } else { + allocatedTuners++; + } + } + } + + ALOG_ASSERT(allocatedTuners + allocatedAudio < mProperties.num_tuners, + "removeClient() removed client but no tuner available"); + + ALOG_ASSERT(!moduleClient->audio() || allocatedAudio < mProperties.num_audio_sources, + "removeClient() removed audio client but no tuner with audio available"); + + if (allocatedAudio < mProperties.num_audio_sources && youngestClientAudio != 0) { + youngestClient = youngestClientAudio; + } + + ALOG_ASSERT(youngestClient != 0, "removeClient() removed client no candidate found for tuner"); + + struct radio_hal_band_config halConfig = youngestClient->halConfig(); + ret = mHwDevice->open_tuner(mHwDevice, &halConfig, youngestClient->audio(), + RadioService::callback, moduleClient->callbackThread().get(), + &halTuner); + + //TODO notify audio device connection to audio policy manager if audio is on + + if (ret == 0) { + youngestClient->setTuner(halTuner); + } +} + +status_t RadioService::Module::setMute(bool mute) +{ + Mutex::Autolock _l(mLock); + if (mute != mMute) { + mMute = mute; + //TODO notifify audio policy manager of media activity on radio audio device + } + return NO_ERROR; +} + +status_t RadioService::Module::getMute(bool *mute) +{ + Mutex::Autolock _l(mLock); + *mute = mMute; + return NO_ERROR; +} + + +const struct radio_band_config *RadioService::Module::getDefaultConfig() const +{ + if (mProperties.num_bands == 0) { + return NULL; + } + return &mProperties.bands[0]; +} + +#undef LOG_TAG +#define LOG_TAG "RadioService::ModuleClient" + +RadioService::ModuleClient::ModuleClient(const sp<Module>& module, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio) + : mModule(module), mClient(client), mConfig(*config), mAudio(audio), mTuner(NULL) +{ +} + +void RadioService::ModuleClient::onFirstRef() +{ + mCallbackThread = new CallbackThread(this); + IInterface::asBinder(mClient)->linkToDeath(this); +} + +RadioService::ModuleClient::~ModuleClient() { + if (mClient != 0) { + IInterface::asBinder(mClient)->unlinkToDeath(this); + mClient.clear(); + } + if (mCallbackThread != 0) { + mCallbackThread->exit(); + } +} + +status_t RadioService::ModuleClient::dump(int fd __unused, + const Vector<String16>& args __unused) { + String8 result; + return NO_ERROR; +} + +void RadioService::ModuleClient::detach() { + ALOGV("%s", __FUNCTION__); + sp<ModuleClient> strongMe = this; + { + AutoMutex lock(mLock); + if (mClient != 0) { + IInterface::asBinder(mClient)->unlinkToDeath(this); + mClient.clear(); + } + } + sp<Module> module = mModule.promote(); + if (module == 0) { + return; + } + module->removeClient(this); +} + +radio_hal_band_config_t RadioService::ModuleClient::halConfig() const +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + return mConfig.band; +} + +const struct radio_tuner *RadioService::ModuleClient::getTuner() const +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + return mTuner; +} + +void RadioService::ModuleClient::setTuner(const struct radio_tuner *tuner) +{ + ALOGV("%s %p", __FUNCTION__, this); + + AutoMutex lock(mLock); + mTuner = tuner; + ALOGV("%s locked", __FUNCTION__); + + radio_hal_event_t event; + event.type = RADIO_EVENT_CONTROL; + event.status = 0; + event.on = mTuner != NULL; + mCallbackThread->sendEvent(&event); + ALOGV("%s DONE", __FUNCTION__); + +} + +status_t RadioService::ModuleClient::setConfiguration(const struct radio_band_config *config) +{ + AutoMutex lock(mLock); + status_t status = NO_ERROR; + ALOGV("%s locked", __FUNCTION__); + + if (mTuner != NULL) { + struct radio_hal_band_config halConfig; + halConfig = config->band; + status = (status_t)mTuner->set_configuration(mTuner, &halConfig); + if (status == NO_ERROR) { + mConfig = *config; + } + } else { + mConfig = *config; + status == INVALID_OPERATION; + } + + return status; +} + +status_t RadioService::ModuleClient::getConfiguration(struct radio_band_config *config) +{ + AutoMutex lock(mLock); + status_t status = NO_ERROR; + ALOGV("%s locked", __FUNCTION__); + + if (mTuner != NULL) { + struct radio_hal_band_config halConfig; + status = (status_t)mTuner->get_configuration(mTuner, &halConfig); + if (status == NO_ERROR) { + mConfig.band = halConfig; + } + } + *config = mConfig; + + return status; +} + +status_t RadioService::ModuleClient::setMute(bool mute) +{ + sp<Module> module; + { + Mutex::Autolock _l(mLock); + ALOGV("%s locked", __FUNCTION__); + if (mTuner == NULL || !mAudio) { + return INVALID_OPERATION; + } + module = mModule.promote(); + if (module == 0) { + return NO_INIT; + } + } + module->setMute(mute); + return NO_ERROR; +} + +status_t RadioService::ModuleClient::getMute(bool *mute) +{ + sp<Module> module; + { + Mutex::Autolock _l(mLock); + ALOGV("%s locked", __FUNCTION__); + module = mModule.promote(); + if (module == 0) { + return NO_INIT; + } + } + return module->getMute(mute); +} + +status_t RadioService::ModuleClient::scan(radio_direction_t direction, bool skipSubChannel) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->scan(mTuner, direction, skipSubChannel); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::step(radio_direction_t direction, bool skipSubChannel) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->step(mTuner, direction, skipSubChannel); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::tune(unsigned int channel, unsigned int subChannel) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->tune(mTuner, channel, subChannel); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::cancel() +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->cancel(mTuner); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::getProgramInformation(struct radio_program_info *info) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->get_program_information(mTuner, info); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::hasControl(bool *hasControl) +{ + Mutex::Autolock lock(mLock); + ALOGV("%s locked", __FUNCTION__); + *hasControl = mTuner != NULL; + return NO_ERROR; +} + +void RadioService::ModuleClient::onCallbackEvent(const sp<IMemory>& eventMemory) +{ + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + + sp<IRadioClient> client; + { + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + radio_event_t *event = (radio_event_t *)eventMemory->pointer(); + switch (event->type) { + case RADIO_EVENT_CONFIG: + mConfig.band = event->config.band; + event->config.region = mConfig.region; + break; + default: + break; + } + + client = mClient; + } + if (client != 0) { + client->onEvent(eventMemory); + } +} + + +void RadioService::ModuleClient::binderDied( + const wp<IBinder> &who __unused) { + ALOGW("client binder died for client %p", this); + detach(); +} + +}; // namespace android diff --git a/services/radio/RadioService.h b/services/radio/RadioService.h new file mode 100644 index 0000000..9ede020 --- /dev/null +++ b/services/radio/RadioService.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_HARDWARE_RADIO_SERVICE_H +#define ANDROID_HARDWARE_RADIO_SERVICE_H + +#include <utils/Vector.h> +//#include <binder/AppOpsManager.h> +#include <binder/MemoryDealer.h> +#include <binder/BinderService.h> +#include <binder/IAppOpsCallback.h> +#include <radio/IRadioService.h> +#include <radio/IRadio.h> +#include <radio/IRadioClient.h> +#include <system/radio.h> +#include <hardware/radio.h> + +namespace android { + +class MemoryHeapBase; + +class RadioService : + public BinderService<RadioService>, + public BnRadioService +{ + friend class BinderService<RadioService>; + +public: + class ModuleClient; + class Module; + + static char const* getServiceName() { return "media.radio"; } + + RadioService(); + virtual ~RadioService(); + + // IRadioService + virtual status_t listModules(struct radio_properties *properties, + uint32_t *numModules); + + virtual status_t attach(radio_handle_t handle, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool withAudio, + sp<IRadio>& radio); + + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + virtual status_t dump(int fd, const Vector<String16>& args); + + + class Module : public virtual RefBase { + public: + + Module(const sp<RadioService>& service, + radio_hw_device* hwDevice, + struct radio_properties properties); + + virtual ~Module(); + + sp<ModuleClient> addClient(const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio); + + void removeClient(const sp<ModuleClient>& moduleClient); + + status_t setMute(bool mute); + + status_t getMute(bool *mute); + + virtual status_t dump(int fd, const Vector<String16>& args); + + const struct radio_hw_device *hwDevice() const { return mHwDevice; } + const struct radio_properties properties() const { return mProperties; } + const struct radio_band_config *getDefaultConfig() const ; + + wp<RadioService> service() const { return mService; } + + private: + + Mutex mLock; + wp<RadioService> mService; + const struct radio_hw_device *mHwDevice; + const struct radio_properties mProperties; + Vector< sp<ModuleClient> > mModuleClients; + bool mMute; + }; // class Module + + class CallbackThread : public Thread { + public: + + CallbackThread(const wp<ModuleClient>& moduleClient); + + virtual ~CallbackThread(); + + + // Thread virtuals + virtual bool threadLoop(); + + // RefBase + virtual void onFirstRef(); + + void exit(); + + void sendEvent(radio_hal_event_t *halEvent); + sp<IMemory> prepareEvent(radio_hal_event_t *halEvent); + + private: + wp<ModuleClient> mModuleClient; + Condition mCallbackCond; + Mutex mCallbackLock; + Vector< sp<IMemory> > mEventQueue; + sp<MemoryDealer> mMemoryDealer; + }; // class CallbackThread + + class ModuleClient : public BnRadio, + public IBinder::DeathRecipient { + public: + + ModuleClient(const sp<Module>& module, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio); + + virtual ~ModuleClient(); + + // IRadio + virtual void detach(); + + virtual status_t setConfiguration(const struct radio_band_config *config); + + virtual status_t getConfiguration(struct radio_band_config *config); + + virtual status_t setMute(bool mute); + + virtual status_t getMute(bool *mute); + + virtual status_t scan(radio_direction_t direction, bool skipSubChannel); + + virtual status_t step(radio_direction_t direction, bool skipSubChannel); + + virtual status_t tune(unsigned int channel, unsigned int subChannel); + + virtual status_t cancel(); + + virtual status_t getProgramInformation(struct radio_program_info *info); + + virtual status_t hasControl(bool *hasControl); + + virtual status_t dump(int fd, const Vector<String16>& args); + + sp<IRadioClient> client() const { return mClient; } + wp<Module> module() const { return mModule; } + radio_hal_band_config_t halConfig() const; + sp<CallbackThread> callbackThread() const { return mCallbackThread; } + void setTuner(const struct radio_tuner *tuner); + const struct radio_tuner *getTuner() const; + bool audio() const { return mAudio; } + + void onCallbackEvent(const sp<IMemory>& event); + + virtual void onFirstRef(); + + + // IBinder::DeathRecipient implementation + virtual void binderDied(const wp<IBinder> &who); + + private: + + mutable Mutex mLock; + wp<Module> mModule; + sp<IRadioClient> mClient; + radio_band_config_t mConfig; + sp<CallbackThread> mCallbackThread; + const bool mAudio; + const struct radio_tuner *mTuner; + }; // class ModuleClient + + + static void callback(radio_hal_event_t *halEvent, void *cookie); + +private: + + virtual void onFirstRef(); + + static void convertProperties(radio_properties_t *properties, + const radio_hal_properties_t *halProperties); + Mutex mServiceLock; + volatile int32_t mNextUniqueId; + DefaultKeyedVector< radio_handle_t, sp<Module> > mModules; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_RADIO_SERVICE_H |