Merge "MediaMetrics: Implement AudioTrack status logging"
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 78a77d4..3687b15 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -173,6 +173,13 @@
void setTorchMode(String cameraId, boolean enabled, IBinder clientBinder);
+ // Change the brightness level of the flash unit associated with cameraId to strengthLevel.
+ // If the torch is in OFF state and strengthLevel > 0 then the torch will also be turned ON.
+ void turnOnTorchWithStrengthLevel(String cameraId, int strengthLevel, IBinder clientBinder);
+
+ // Get the brightness level of the flash unit associated with cameraId.
+ int getTorchStrengthLevel(String cameraId);
+
/**
* Notify the camera service of a system event. Should only be called from system_server.
*
diff --git a/camera/aidl/android/hardware/ICameraServiceListener.aidl b/camera/aidl/android/hardware/ICameraServiceListener.aidl
index c54813c..5f17f5b 100644
--- a/camera/aidl/android/hardware/ICameraServiceListener.aidl
+++ b/camera/aidl/android/hardware/ICameraServiceListener.aidl
@@ -83,6 +83,8 @@
oneway void onTorchStatusChanged(int status, String cameraId);
+ oneway void onTorchStrengthLevelChanged(String cameraId, int newTorchStrength);
+
/**
* Notify registered clients about camera access priority changes.
* Clients which were previously unable to open a certain camera device
diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h
index da887a2..d53d809 100644
--- a/camera/ndk/impl/ACameraManager.h
+++ b/camera/ndk/impl/ACameraManager.h
@@ -95,6 +95,9 @@
virtual binder::Status onTorchStatusChanged(int32_t, const String16&) {
return binder::Status::ok();
}
+ virtual binder::Status onTorchStrengthLevelChanged(const String16&, int32_t) {
+ return binder::Status::ok();
+ }
virtual binder::Status onCameraAccessPrioritiesChanged();
virtual binder::Status onCameraOpened(const String16&, const String16&) {
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 9f2f430..17ea512 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -96,6 +96,12 @@
return binder::Status::ok();
};
+ virtual binder::Status onTorchStrengthLevelChanged(const String16& /*cameraId*/,
+ int32_t /*torchStrength*/) {
+ // No op
+ return binder::Status::ok();
+ }
+
virtual binder::Status onCameraAccessPrioritiesChanged() {
// No op
return binder::Status::ok();
diff --git a/media/codec2/TEST_MAPPING b/media/codec2/TEST_MAPPING
index 16cb323..c6728c8 100644
--- a/media/codec2/TEST_MAPPING
+++ b/media/codec2/TEST_MAPPING
@@ -35,6 +35,17 @@
"exclude-filter": "android.media.audio.cts.AudioRecordTest"
}
]
+ },
+ {
+ "name": "CtsMediaPlayerTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.RequiresDevice"
+ }
+ ]
}
]
}
diff --git a/media/libaaudio/scripts/measure_device_power.py b/media/libaaudio/scripts/measure_device_power.py
new file mode 100755
index 0000000..9603f88
--- /dev/null
+++ b/media/libaaudio/scripts/measure_device_power.py
@@ -0,0 +1,272 @@
+#!/usr/bin/python3
+"""
+ * Copyright (C) 2021 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.
+"""
+
+'''
+Measure CPU related power on Pixel 6 or later devices using ODPM,
+the On Device Power Measurement tool.
+Generate a CSV report for putting in a spreadsheet
+'''
+
+import argparse
+import os
+import re
+import subprocess
+import sys
+import time
+
+# defaults
+PRE_DELAY_SECONDS = 0.5 # time to sleep before command to avoid adb unroot error
+DEFAULT_NUM_ITERATIONS = 5
+DEFAULT_FILE_NAME = 'energy_commands.txt'
+
+'''
+Default rail assignments
+philburk-macbookpro3:expt philburk$ adb shell cat /sys/bus/iio/devices/iio\:device0/energy_value
+t=349894
+CH0(T=349894)[S10M_VDD_TPU], 5578756
+CH1(T=349894)[VSYS_PWR_MODEM], 29110940
+CH2(T=349894)[VSYS_PWR_RFFE], 3166046
+CH3(T=349894)[S2M_VDD_CPUCL2], 30203502
+CH4(T=349894)[S3M_VDD_CPUCL1], 23377533
+CH5(T=349894)[S4M_VDD_CPUCL0], 46356942
+CH6(T=349894)[S5M_VDD_INT], 10771876
+CH7(T=349894)[S1M_VDD_MIF], 21091363
+philburk-macbookpro3:expt philburk$ adb shell cat /sys/bus/iio/devices/iio\:device1/energy_value
+t=359458
+CH0(T=359458)[VSYS_PWR_WLAN_BT], 45993209
+CH1(T=359458)[L2S_VDD_AOC_RET], 2822928
+CH2(T=359458)[S9S_VDD_AOC], 6923706
+CH3(T=359458)[S5S_VDDQ_MEM], 4658202
+CH4(T=359458)[S10S_VDD2L], 5506273
+CH5(T=359458)[S4S_VDD2H_MEM], 14254574
+CH6(T=359458)[S2S_VDD_G3D], 5315420
+CH7(T=359458)[VSYS_PWR_DISPLAY], 81221665
+'''
+
+'''
+LDO2M(L2M_ALIVE):DDR -> DRAM Array Core Power
+BUCK4S(S4S_VDD2H_MEM):DDR -> Normal operation data and control path logic circuits
+BUCK5S(S5S_VDDQ_MEM):DDR -> LPDDR I/O interface
+BUCK10S(S10S_VDD2L):DDR -> DVFSC (1600Mbps or lower) operation data and control path logic circuits
+BUCK1M (S1M_VDD_MIF): SoC side Memory InterFace and Controller
+'''
+
+# Map between rail name and human readable name.
+ENERGY_DICTIONARY = { \
+ 'S4M_VDD_CPUCL0': 'CPU0', \
+ 'S3M_VDD_CPUCL1': 'CPU1', \
+ 'S2M_VDD_CPUCL2': 'CPU2', \
+ 'S1M_VDD_MIF': 'MIF', \
+ 'L2M_ALIVE': 'DDRAC', \
+ 'S4S_VDD2H_MEM': 'DDRNO', \
+ 'S10S_VDD2L': 'DDR16', \
+ 'S5S_VDDQ_MEM': 'DDRIO', \
+ 'VSYS_PWR_DISPLAY': 'SCREEN'}
+
+SORTED_ENERGY_LIST = sorted(ENERGY_DICTIONARY, key=ENERGY_DICTIONARY.get)
+
+# Sometimes "adb unroot" returns 1!
+# So try several times.
+# @return 0 on success
+def adbUnroot():
+ returnCode = 1
+ count = 0
+ limit = 5
+ while count < limit and returnCode != 0:
+ print(('Try to adb unroot {} of {}'.format(count, limit)))
+ subprocess.call(["adb", "wait-for-device"])
+ time.sleep(PRE_DELAY_SECONDS)
+ returnCode = subprocess.call(["adb", "unroot"])
+ print(('returnCode = {}'.format(returnCode)))
+ count += 1
+ return returnCode
+
+# @param commandString String containing shell command
+# @return Both the stdout and stderr of the commands run
+def runCommand(commandString):
+ print(commandString)
+ if commandString == "adb unroot":
+ result = adbUnroot()
+ else:
+ commandArray = commandString.split(' ')
+ result = subprocess.run(commandArray, check=True, capture_output=True).stdout
+ return result
+
+# @param commandString String containing ADB command
+# @return Both the stdout and stderr of the commands run
+def adbCommand(commandString):
+ if commandString == "unroot":
+ result = adbUnroot()
+ else:
+ print(("adb " + commandString))
+ commandArray = ["adb"] + commandString.split(' ')
+ subprocess.call(["adb", "wait-for-device"])
+ result = subprocess.run(commandArray, check=True, capture_output=True).stdout
+ return result
+
+# Parse a line that looks like "CH3(T=10697635)[S2M_VDD_CPUCL2], 116655335"
+# Use S2M_VDD_CPUCL2 as the tag and set value to the number
+# in the report dictionary.
+def parseEnergyValue(string):
+ return tuple(re.split('\[|\], +', string)[1:])
+
+# Read accumulated energy into a dictionary.
+def measureEnergyForDevice(deviceIndex, report):
+ # print("measureEnergyForDevice " + str(deviceIndex))
+ tableBytes = adbCommand( \
+ 'shell cat /sys/bus/iio/devices/iio\:device{}/energy_value'\
+ .format(deviceIndex))
+ table = tableBytes.decode("utf-8")
+ # print(table)
+ for count, line in enumerate(table.splitlines()):
+ if count > 0:
+ tagEnergy = parseEnergyValue(line)
+ report[tagEnergy[0]] = int(tagEnergy[1].strip())
+ # print(report)
+
+def measureEnergyOnce():
+ adbCommand("root")
+ report = {}
+ d0 = measureEnergyForDevice(0, report)
+ d1 = measureEnergyForDevice(1, report)
+ adbUnroot()
+ return report
+
+# Subtract numeric values for matching keys.
+def subtractReports(A, B):
+ return {x: A[x] - B[x] for x in A if x in B}
+
+# Add numeric values for matching keys.
+def addReports(A, B):
+ return {x: A[x] + B[x] for x in A if x in B}
+
+# Divide numeric values by divisor.
+# @return Modified copy of report.
+def divideReport(report, divisor):
+ return {key: val / divisor for key, val in list(report.items())}
+
+# Generate a dictionary that is the difference between two measurements over time.
+def measureEnergyOverTime(duration):
+ report1 = measureEnergyOnce()
+ print(("Measure energy for " + str(duration) + " seconds."))
+ time.sleep(duration)
+ report2 = measureEnergyOnce()
+ return subtractReports(report2, report1)
+
+# Generate a CSV string containing the human readable headers.
+def formatEnergyHeader():
+ header = ""
+ for tag in SORTED_ENERGY_LIST:
+ header += ENERGY_DICTIONARY[tag] + ", "
+ return header
+
+# Generate a CSV string containing the numeric values.
+def formatEnergyData(report):
+ data = ""
+ for tag in SORTED_ENERGY_LIST:
+ if tag in list(report.keys()):
+ data += str(report[tag]) + ", "
+ else:
+ data += "-1,"
+ return data
+
+def printEnergyReport(report):
+ s = "\n"
+ s += "Values are in microWattSeconds\n"
+ s += "Report below is CSV format for pasting into a spreadsheet:\n"
+ s += formatEnergyHeader() + "\n"
+ s += formatEnergyData(report) + "\n"
+ print(s)
+
+# Generate a dictionary that is the difference between two measurements
+# before and after executing the command.
+def measureEnergyForCommand(command):
+ report1 = measureEnergyOnce()
+ print(("Measure energy for: " + command))
+ result = runCommand(command)
+ report2 = measureEnergyOnce()
+ # print(result)
+ return subtractReports(report2, report1)
+
+# Average the results of several measurements for one command.
+def averageEnergyForCommand(command, count):
+ print("=================== #0\n")
+ sumReport = measureEnergyForCommand(command)
+ for i in range(1, count):
+ print(("=================== #" + str(i) + "\n"))
+ report = measureEnergyForCommand(command)
+ sumReport = addReports(sumReport, report)
+ print(sumReport)
+ return divideReport(sumReport, count)
+
+# Parse a list of commands in a file.
+# Lines ending in "\" are continuation lines.
+# Lines beginning with "#" are comments.
+def measureEnergyForCommands(fileName):
+ finalReport = "------------------------------------\n"
+ finalReport += "comment, command, " + formatEnergyHeader() + "\n"
+ comment = ""
+ try:
+ fp = open(fileName)
+ line = fp.readline()
+ while line:
+ command = line.strip()
+ if command.endswith('\\'):
+ command = command[:-1].strip() # remove \\:
+ runCommand(command)
+ elif command.startswith("#"):
+ # ignore comment
+ print((command + "\n"))
+ comment = command
+ elif command:
+ report = averageEnergyForCommand(command, DEFAULT_NUM_ITERATIONS)
+ finalReport += comment + ", " + command + ", " + formatEnergyData(report) + "\n"
+ print(finalReport)
+ line = fp.readline()
+ finally:
+ fp.close()
+ return finalReport
+
+def main():
+ # parse command line args
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-s', '--seconds',
+ help="Measure power for N seconds. Ignore scriptFile.",
+ type=float)
+ parser.add_argument("fileName",
+ nargs = '?',
+ help="Path to file containing commands to be measured."
+ + " Default path = " + DEFAULT_FILE_NAME + "."
+ + " Lines ending in '\' are continuation lines."
+ + " Lines beginning with '#' are comments.",
+ default=DEFAULT_FILE_NAME)
+ args=parser.parse_args();
+
+ print(("seconds = " + str(args.seconds)))
+ print(("fileName = " + str(args.fileName)))
+ # Process command line
+ if args.seconds:
+ report = measureEnergyOverTime(args.seconds)
+ printEnergyReport(report)
+ else:
+ report = measureEnergyForCommands(args.fileName)
+ print(report)
+ print("Finished.\n")
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/media/libaaudio/scripts/setup_odpm_cpu_rails.sh b/media/libaaudio/scripts/setup_odpm_cpu_rails.sh
new file mode 100644
index 0000000..e9241b9
--- /dev/null
+++ b/media/libaaudio/scripts/setup_odpm_cpu_rails.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Configure ODPM rails to measure CPU specific power.
+# See go/odpm-p21-userguide
+
+adb root
+
+# LDO2M(L2M_ALIVE) - DRAM Array Core Power
+adb shell 'echo "CH0=LDO2M" > /sys/bus/iio/devices/iio\:device0/enabled_rails'
+
+# These are the defaults.
+# BUCK2M(S2M_VDD_CPUCL2):CPU(BIG)
+# adb shell 'echo "CH3=BUCK2M" > /sys/bus/iio/devices/iio\:device0/enabled_rails'
+# BUCK3M(S3M_VDD_CPUCL1):CPU(MID)
+# adb shell 'echo "CH4=BUCK3M" > /sys/bus/iio/devices/iio\:device0/enabled_rails'
+# BUCK4M(S4M_VDD_CPUCL0):CPU(LITTLE)
+# adb shell 'echo "CH5=BUCK4M" > /sys/bus/iio/devices/iio\:device0/enabled_rails'
+# BUCK1M(S1M_VDD_MIF):MIF
+# adb shell 'echo "CH7=BUCK1M" > /sys/bus/iio/devices/iio\:device0/enabled_rails'
+
+# These are default on device1.
+# BUCK5S(S5S_VDDQ_MEM):DDR
+# adb shell 'echo "CH3=BUCK5S" > /sys/bus/iio/devices/iio\:device1/enabled_rails'
+# BUCK10S(S10S_VDD2L):DDR
+# adb shell 'echo "CH4=BUCK10S" > /sys/bus/iio/devices/iio\:device1/enabled_rails'
+# BUCK4S(S4S_VDD2H_MEM):DDR
+# adb shell 'echo "CH5=BUCK4S" > /sys/bus/iio/devices/iio\:device1/enabled_rails'
+
+adb shell 'cat /sys/bus/iio/devices/iio\:device0/enabled_rails'
+adb shell 'cat /sys/bus/iio/devices/iio\:device1/enabled_rails'
+
+adb unroot
+
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 38bcb7c..f50b53a 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -214,6 +214,7 @@
"flowgraph/MonoBlend.cpp",
"flowgraph/MonoToMultiConverter.cpp",
"flowgraph/MultiToMonoConverter.cpp",
+ "flowgraph/MultiToManyConverter.cpp",
"flowgraph/RampLinear.cpp",
"flowgraph/SampleRateConverter.cpp",
"flowgraph/SinkFloat.cpp",
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
index d3e2912..5b46ae0 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.cpp
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -21,8 +21,10 @@
#include "AAudioFlowGraph.h"
#include <flowgraph/ClipToRange.h>
+#include <flowgraph/ManyToMultiConverter.h>
#include <flowgraph/MonoBlend.h>
#include <flowgraph/MonoToMultiConverter.h>
+#include <flowgraph/MultiToManyConverter.h>
#include <flowgraph/RampLinear.h>
#include <flowgraph/SinkFloat.h>
#include <flowgraph/SinkI16.h>
@@ -39,12 +41,15 @@
int32_t sourceChannelCount,
audio_format_t sinkFormat,
int32_t sinkChannelCount,
- bool useMonoBlend) {
+ bool useMonoBlend,
+ float audioBalance) {
FlowGraphPortFloatOutput *lastOutput = nullptr;
// TODO change back to ALOGD
- ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d",
- __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount);
+ ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d, "
+ "useMonoBlend = %d, audioBalance = %f",
+ __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount,
+ useMonoBlend, audioBalance);
switch (sourceFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
@@ -65,10 +70,11 @@
}
lastOutput = &mSource->output;
- // Apply volume as a ramp to avoid pops.
- mVolumeRamp = std::make_unique<RampLinear>(sourceChannelCount);
- lastOutput->connect(&mVolumeRamp->input);
- lastOutput = &mVolumeRamp->output;
+ if (useMonoBlend) {
+ mMonoBlend = std::make_unique<MonoBlend>(sourceChannelCount);
+ lastOutput->connect(&mMonoBlend->input);
+ lastOutput = &mMonoBlend->output;
+ }
// For a pure float graph, there is chance that the data range may be very large.
// So we should clip to a reasonable value that allows a little headroom.
@@ -78,12 +84,6 @@
lastOutput = &mClipper->output;
}
- if (useMonoBlend) {
- mMonoBlend = std::make_unique<MonoBlend>(sourceChannelCount);
- lastOutput->connect(&mMonoBlend->input);
- lastOutput = &mMonoBlend->output;
- }
-
// Expand the number of channels if required.
if (sourceChannelCount == 1 && sinkChannelCount > 1) {
mChannelConverter = std::make_unique<MonoToMultiConverter>(sinkChannelCount);
@@ -94,6 +94,23 @@
return AAUDIO_ERROR_UNIMPLEMENTED;
}
+ // Apply volume ramps to set the left/right audio balance and target volumes.
+ // The signals will be decoupled, volume ramps will be applied, before the signals are
+ // combined again.
+ mMultiToManyConverter = std::make_unique<MultiToManyConverter>(sinkChannelCount);
+ mManyToMultiConverter = std::make_unique<ManyToMultiConverter>(sinkChannelCount);
+ lastOutput->connect(&mMultiToManyConverter->input);
+ for (int i = 0; i < sinkChannelCount; i++) {
+ mVolumeRamps.emplace_back(std::make_unique<RampLinear>(1));
+ mPanningVolumes.emplace_back(1.0f);
+ lastOutput = mMultiToManyConverter->outputs[i].get();
+ lastOutput->connect(&(mVolumeRamps[i].get()->input));
+ lastOutput = &(mVolumeRamps[i].get()->output);
+ lastOutput->connect(mManyToMultiConverter->inputs[i].get());
+ }
+ lastOutput = &mManyToMultiConverter->output;
+ setAudioBalance(audioBalance);
+
switch (sinkFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
mSink = std::make_unique<SinkFloat>(sinkChannelCount);
@@ -125,9 +142,32 @@
* @param volume between 0.0 and 1.0
*/
void AAudioFlowGraph::setTargetVolume(float volume) {
- mVolumeRamp->setTarget(volume);
+ for (int i = 0; i < mVolumeRamps.size(); i++) {
+ mVolumeRamps[i]->setTarget(volume * mPanningVolumes[i]);
+ }
+ mTargetVolume = volume;
}
+/**
+ * @param audioBalance between -1.0 and 1.0
+ */
+void AAudioFlowGraph::setAudioBalance(float audioBalance) {
+ if (mPanningVolumes.size() >= 2) {
+ float leftMultiplier = 0;
+ float rightMultiplier = 0;
+ mBalance.computeStereoBalance(audioBalance, &leftMultiplier, &rightMultiplier);
+ mPanningVolumes[0] = leftMultiplier;
+ mPanningVolumes[1] = rightMultiplier;
+ mVolumeRamps[0]->setTarget(mTargetVolume * leftMultiplier);
+ mVolumeRamps[1]->setTarget(mTargetVolume * rightMultiplier);
+ }
+}
+
+/**
+ * @param numFrames to slowly adjust for volume changes
+ */
void AAudioFlowGraph::setRampLengthInFrames(int32_t numFrames) {
- mVolumeRamp->setLengthInFrames(numFrames);
+ for (auto& ramp : mVolumeRamps) {
+ ramp->setLengthInFrames(numFrames);
+ }
}
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h
index e719d91..2056b70 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.h
+++ b/media/libaaudio/src/client/AAudioFlowGraph.h
@@ -23,9 +23,12 @@
#include <system/audio.h>
#include <aaudio/AAudio.h>
+#include <audio_utils/Balance.h>
#include <flowgraph/ClipToRange.h>
+#include <flowgraph/ManyToMultiConverter.h>
#include <flowgraph/MonoBlend.h>
#include <flowgraph/MonoToMultiConverter.h>
+#include <flowgraph/MultiToManyConverter.h>
#include <flowgraph/RampLinear.h>
class AAudioFlowGraph {
@@ -37,13 +40,17 @@
* @param sourceChannelCount
* @param sinkFormat
* @param sinkChannelCount
+ * @param useMonoBlend
+ * @param audioBalance
+ * @param channelMask
* @return
*/
aaudio_result_t configure(audio_format_t sourceFormat,
int32_t sourceChannelCount,
audio_format_t sinkFormat,
int32_t sinkChannelCount,
- bool useMonoBlend);
+ bool useMonoBlend,
+ float audioBalance);
void process(const void *source, void *destination, int32_t numFrames);
@@ -52,14 +59,27 @@
*/
void setTargetVolume(float volume);
+ /**
+ * @param audioBalance between -1.0 and 1.0
+ */
+ void setAudioBalance(float audioBalance);
+
+ /**
+ * @param numFrames to slowly adjust for volume changes
+ */
void setRampLengthInFrames(int32_t numFrames);
private:
std::unique_ptr<flowgraph::FlowGraphSourceBuffered> mSource;
std::unique_ptr<flowgraph::MonoBlend> mMonoBlend;
- std::unique_ptr<flowgraph::RampLinear> mVolumeRamp;
std::unique_ptr<flowgraph::ClipToRange> mClipper;
std::unique_ptr<flowgraph::MonoToMultiConverter> mChannelConverter;
+ std::unique_ptr<flowgraph::ManyToMultiConverter> mManyToMultiConverter;
+ std::unique_ptr<flowgraph::MultiToManyConverter> mMultiToManyConverter;
+ std::vector<std::unique_ptr<flowgraph::RampLinear>> mVolumeRamps;
+ std::vector<float> mPanningVolumes;
+ float mTargetVolume = 1.0f;
+ android::audio_utils::Balance mBalance;
std::unique_ptr<flowgraph::FlowGraphSink> mSink;
};
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 1b8e224..afdc2ac 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -272,12 +272,15 @@
}
// Exclusive output streams should combine channels when mono audio adjustment
- // is enabled.
+ // is enabled. They should also adjust for audio balance.
if ((getDirection() == AAUDIO_DIRECTION_OUTPUT) &&
(getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE)) {
bool isMasterMono = false;
android::AudioSystem::getMasterMono(&isMasterMono);
setRequireMonoBlend(isMasterMono);
+ float audioBalance = 0;
+ android::AudioSystem::getMasterBalance(&audioBalance);
+ setAudioBalance(audioBalance);
}
// For debugging and analyzing the distribution of MMAP timestamps.
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index c17c7a0..8292573 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -53,7 +53,8 @@
getSamplesPerFrame(),
getDeviceFormat(),
getDeviceChannelCount(),
- getRequireMonoBlend());
+ getRequireMonoBlend(),
+ getAudioBalance());
if (result != AAUDIO_OK) {
safeReleaseClose();
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index a3af753..5fb4528 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -281,6 +281,10 @@
return mRequireMonoBlend;
}
+ float getAudioBalance() const {
+ return mAudioBalance;
+ }
+
/**
* This is only valid after setChannelMask() and setFormat()
* have been called.
@@ -642,6 +646,13 @@
mRequireMonoBlend = requireMonoBlend;
}
+ /**
+ * This should not be called after the open() call.
+ */
+ void setAudioBalance(float audioBalance) {
+ mAudioBalance = audioBalance;
+ }
+
std::string mMetricsId; // set once during open()
std::mutex mStreamLock;
@@ -684,6 +695,7 @@
aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_ALLOW_CAPTURE_BY_ALL;
bool mIsPrivacySensitive = false;
bool mRequireMonoBlend = false;
+ float mAudioBalance = 0;
int32_t mSessionId = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/flowgraph/MultiToManyConverter.cpp b/media/libaaudio/src/flowgraph/MultiToManyConverter.cpp
new file mode 100644
index 0000000..f074364
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MultiToManyConverter.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 <unistd.h>
+#include "FlowGraphNode.h"
+#include "MultiToManyConverter.h"
+
+using namespace flowgraph;
+
+MultiToManyConverter::MultiToManyConverter(int32_t channelCount)
+ : outputs(channelCount)
+ , input(*this, channelCount) {
+ for (int i = 0; i < channelCount; i++) {
+ outputs[i] = std::make_unique<FlowGraphPortFloatOutput>(*this, 1);
+ }
+}
+
+MultiToManyConverter::~MultiToManyConverter() = default;
+
+int32_t MultiToManyConverter::onProcess(int32_t numFrames) {
+ int32_t channelCount = input.getSamplesPerFrame();
+
+ for (int ch = 0; ch < channelCount; ch++) {
+ const float *inputBuffer = input.getBuffer() + ch;
+ float *outputBuffer = outputs[ch]->getBuffer();
+
+ for (int i = 0; i < numFrames; i++) {
+ *outputBuffer++ = *inputBuffer;
+ inputBuffer += channelCount;
+ }
+ }
+
+ return numFrames;
+}
+
diff --git a/media/libaaudio/src/flowgraph/MultiToManyConverter.h b/media/libaaudio/src/flowgraph/MultiToManyConverter.h
new file mode 100644
index 0000000..de31475
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MultiToManyConverter.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H
+#define FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "FlowGraphNode.h"
+
+namespace flowgraph {
+
+/**
+ * Convert a multi-channel interleaved stream to multiple mono-channel
+ * outputs
+ */
+ class MultiToManyConverter : public FlowGraphNode {
+ public:
+ explicit MultiToManyConverter(int32_t channelCount);
+
+ virtual ~MultiToManyConverter();
+
+ int32_t onProcess(int32_t numFrames) override;
+
+ const char *getName() override {
+ return "MultiToManyConverter";
+ }
+
+ std::vector<std::unique_ptr<flowgraph::FlowGraphPortFloatOutput>> outputs;
+ flowgraph::FlowGraphPortFloatInput input;
+ };
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H
diff --git a/media/libmediatranscoding/include/media/ControllerClientInterface.h b/media/libmediatranscoding/include/media/ControllerClientInterface.h
index 9311e2e..ea63da8 100644
--- a/media/libmediatranscoding/include/media/ControllerClientInterface.h
+++ b/media/libmediatranscoding/include/media/ControllerClientInterface.h
@@ -66,7 +66,7 @@
* Returns false if the session doesn't exist, or the client is already requesting the
* session. Returns true otherwise.
*/
- virtual bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid);
+ virtual bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) = 0;
/**
* Retrieves the (unsorted) list of all clients requesting the session identified by
@@ -81,7 +81,7 @@
* Returns false if the session doesn't exist. Returns true otherwise.
*/
virtual bool getClientUids(ClientIdType clientId, SessionIdType sessionId,
- std::vector<int32_t>* out_clientUids);
+ std::vector<int32_t>* out_clientUids) = 0;
protected:
virtual ~ControllerClientInterface() = default;
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index 0987a5b..7d4e168 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -40,6 +40,17 @@
"exclude-filter": "android.media.audio.cts.AudioRecordTest"
}
]
+ },
+ {
+ "name": "CtsMediaPlayerTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.RequiresDevice"
+ }
+ ]
}
],
"presubmit": [
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
index fb6c4e2..bb1cb0b 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -354,7 +354,7 @@
}
if (mpeg4type->eProfile != OMX_VIDEO_MPEG4ProfileCore ||
- mpeg4type->eLevel != OMX_VIDEO_MPEG4Level2 ||
+ mpeg4type->eLevel > OMX_VIDEO_MPEG4Level2 ||
(mpeg4type->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) ||
mpeg4type->nBFrames != 0 ||
mpeg4type->nIDCVLCThreshold != 0 ||
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index 3ee7626..15d6d3697 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -316,7 +316,7 @@
// IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
bool ok = PermissionCache::checkPermission(sCallAudioInterception, pid, uid);
- if (!ok) ALOGE("%s(): android.permission.CALL_AUDIO_INTERCEPTION denied for uid %d",
+ if (!ok) ALOGV("%s(): android.permission.CALL_AUDIO_INTERCEPTION denied for uid %d",
__func__, uid);
return ok;
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index ec414e0..ed4666f 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -4084,7 +4084,6 @@
// transfer all effects one by one so that new effect chain is created on new thread with
// correct buffer sizes and audio parameters and effect engines reconfigured accordingly
sp<EffectChain> dstChain;
- uint32_t strategy = 0; // prevent compiler warning
sp<EffectModule> effect = chain->getEffectFromId_l(0);
Vector< sp<EffectModule> > removed;
status_t status = NO_ERROR;
@@ -4109,7 +4108,6 @@
status = NO_INIT;
break;
}
- strategy = dstChain->strategy();
}
effect = chain->getEffectFromId_l(0);
}
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 3cce998..aecd4d3 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -193,6 +193,12 @@
}
}
+ static bool checkServerLatencySupported(
+ audio_format_t format, audio_output_flags_t flags) {
+ return audio_is_linear_pcm(format)
+ && (flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) == 0;
+ }
+
audio_output_flags_t getOutputFlags() const { return mFlags; }
float getSpeed() const { return mSpeed; }
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 616fd78..233865f 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -713,8 +713,7 @@
thread->mFastTrackAvailMask &= ~(1 << i);
}
- mServerLatencySupported = thread->type() == ThreadBase::MIXER
- || thread->type() == ThreadBase::DUPLICATING;
+ mServerLatencySupported = checkServerLatencySupported(format, flags);
#ifdef TEE_SINK
mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
+ "_" + std::to_string(mId) + "_T");
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp
index b0c376a..9a61a05 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp
@@ -299,8 +299,13 @@
if (device != nullptr) {
return DeviceVector(device);
}
+ return fromCache? getCachedDevices(strategy) : getDevicesForProductStrategy(strategy);
+}
- return fromCache? mDevicesForStrategies.at(strategy) : getDevicesForProductStrategy(strategy);
+DeviceVector Engine::getCachedDevices(product_strategy_t ps) const
+{
+ return mDevicesForStrategies.find(ps) != mDevicesForStrategies.end() ?
+ mDevicesForStrategies.at(ps) : DeviceVector{};
}
DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool fromCache) const
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h
index d8e2742..f665da5 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.h
+++ b/services/audiopolicy/engineconfigurable/src/Engine.h
@@ -126,6 +126,7 @@
status_t loadAudioPolicyEngineConfig();
DeviceVector getDevicesForProductStrategy(product_strategy_t strategy) const;
+ DeviceVector getCachedDevices(product_strategy_t ps) const;
/**
* Policy Parameter Manager hidden through a wrapper.
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 00c1f26..0bd5a55 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1702,6 +1702,8 @@
// The priority is as follows:
// 1: the output supporting haptic playback when requesting haptic playback
// 2: the output with the highest number of requested functional flags
+ // with tiebreak preferring the minimum number of extra functional flags
+ // (see b/200293124, the incorrect selection of AUDIO_OUTPUT_FLAG_VOIP_RX).
// 3: the output supporting the exact channel mask
// 4: the output with a higher channel count than requested
// 5: the output with a higher sampling rate than requested
@@ -1743,7 +1745,12 @@
}
// functional flags match
- currentMatchCriteria[1] = popcount(outputDesc->mFlags & functionalFlags);
+ const int matchingFunctionalFlags =
+ __builtin_popcount(outputDesc->mFlags & functionalFlags);
+ const int totalFunctionalFlags =
+ __builtin_popcount(outputDesc->mFlags & kFunctionalFlags);
+ // Prefer matching functional flags, but subtract unnecessary functional flags.
+ currentMatchCriteria[1] = 100 * (matchingFunctionalFlags + 1) - totalFunctionalFlags;
// channel mask and channel count match
uint32_t outputChannelCount = audio_channel_count_from_out_mask(
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index ccdd9e5..015ae2f 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -119,6 +119,59 @@
return res;
}
+status_t CameraFlashlight::turnOnTorchWithStrengthLevel(const String8& cameraId,
+ int32_t torchStrength) {
+ if (!mFlashlightMapInitialized) {
+ ALOGE("%s: findFlashUnits() must be called before this method.",
+ __FUNCTION__);
+ return NO_INIT;
+ }
+
+ ALOGV("%s: set torch strength of camera %s to %d", __FUNCTION__,
+ cameraId.string(), torchStrength);
+ status_t res = OK;
+ Mutex::Autolock l(mLock);
+
+ if (mOpenedCameraIds.indexOf(cameraId) != NAME_NOT_FOUND) {
+ ALOGE("%s: Camera device %s is in use, cannot be turned ON.",
+ __FUNCTION__, cameraId.string());
+ return -EBUSY;
+ }
+
+ if (mFlashControl == NULL) {
+ res = createFlashlightControl(cameraId);
+ if (res) {
+ return res;
+ }
+ }
+
+ res = mFlashControl->turnOnTorchWithStrengthLevel(cameraId, torchStrength);
+ return res;
+}
+
+
+status_t CameraFlashlight::getTorchStrengthLevel(const String8& cameraId,
+ int32_t* torchStrength) {
+ status_t res = OK;
+ if (!mFlashlightMapInitialized) {
+ ALOGE("%s: findFlashUnits() must be called before this method.",
+ __FUNCTION__);
+ return false;
+ }
+
+ Mutex::Autolock l(mLock);
+
+ if (mFlashControl == NULL) {
+ res = createFlashlightControl(cameraId);
+ if (res) {
+ return res;
+ }
+ }
+
+ res = mFlashControl->getTorchStrengthLevel(cameraId, torchStrength);
+ return res;
+}
+
status_t CameraFlashlight::findFlashUnits() {
Mutex::Autolock l(mLock);
status_t res;
@@ -306,6 +359,22 @@
return mProviderManager->setTorchMode(cameraId.string(), enabled);
}
+
+status_t ProviderFlashControl::turnOnTorchWithStrengthLevel(const String8& cameraId,
+ int32_t torchStrength) {
+ ALOGV("%s: change torch strength level of camera %s to %d", __FUNCTION__,
+ cameraId.string(), torchStrength);
+
+ return mProviderManager->turnOnTorchWithStrengthLevel(cameraId.string(), torchStrength);
+}
+
+status_t ProviderFlashControl::getTorchStrengthLevel(const String8& cameraId,
+ int32_t* torchStrength) {
+ ALOGV("%s: get torch strength level of camera %s", __FUNCTION__,
+ cameraId.string());
+
+ return mProviderManager->getTorchStrengthLevel(cameraId.string(), torchStrength);
+}
// ProviderFlashControl implementation ends
}
diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h
index b97fa5f..1703ddc 100644
--- a/services/camera/libcameraservice/CameraFlashlight.h
+++ b/services/camera/libcameraservice/CameraFlashlight.h
@@ -44,6 +44,14 @@
// set the torch mode to on or off.
virtual status_t setTorchMode(const String8& cameraId,
bool enabled) = 0;
+
+ // Change the brightness level of the torch. If the torch is OFF and
+ // torchStrength >= 1, then the torch will also be turned ON.
+ virtual status_t turnOnTorchWithStrengthLevel(const String8& cameraId,
+ int32_t torchStrength) = 0;
+
+ // Returns the torch strength level.
+ virtual status_t getTorchStrengthLevel(const String8& cameraId, int32_t* torchStrength) = 0;
};
/**
@@ -67,6 +75,12 @@
// set the torch mode to on or off.
status_t setTorchMode(const String8& cameraId, bool enabled);
+ // Change the torch strength level of the flash unit in torch mode.
+ status_t turnOnTorchWithStrengthLevel(const String8& cameraId, int32_t torchStrength);
+
+ // Get the torch strength level
+ status_t getTorchStrengthLevel(const String8& cameraId, int32_t* torchStrength);
+
// Notify CameraFlashlight that camera service is going to open a camera
// device. CameraFlashlight will free the resources that may cause the
// camera open to fail. Camera service must call this function before
@@ -115,6 +129,8 @@
// FlashControlBase
status_t hasFlashUnit(const String8& cameraId, bool *hasFlash);
status_t setTorchMode(const String8& cameraId, bool enabled);
+ status_t turnOnTorchWithStrengthLevel(const String8& cameraId, int32_t torchStrength);
+ status_t getTorchStrengthLevel(const String8& cameraId, int32_t* torchStrength);
private:
sp<CameraProviderManager> mProviderManager;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 40e49bf..5a18582 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -569,6 +569,15 @@
onTorchStatusChangedLocked(cameraId, newStatus, systemCameraKind);
}
+void CameraService::broadcastTorchStrengthLevel(const String8& cameraId,
+ int32_t newStrengthLevel) {
+ Mutex::Autolock lock(mStatusListenerLock);
+ for (auto& i : mListenerList) {
+ i->getListener()->onTorchStrengthLevelChanged(String16{cameraId},
+ newStrengthLevel);
+ }
+}
+
void CameraService::onTorchStatusChangedLocked(const String8& cameraId,
TorchModeStatus newStatus, SystemCameraKind systemCameraKind) {
ALOGI("%s: Torch status changed for cameraId=%s, newStatus=%d",
@@ -804,6 +813,31 @@
return ret;
}
+Status CameraService::getTorchStrengthLevel(const String16& cameraId,
+ int32_t* torchStrength) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mServiceLock);
+ if (!mInitialized) {
+ ALOGE("%s: Camera HAL couldn't be initialized.", __FUNCTION__);
+ return STATUS_ERROR(ERROR_DISCONNECTED, "Camera HAL couldn't be initialized.");
+ }
+
+ if(torchStrength == NULL) {
+ ALOGE("%s: strength level must not be null.", __FUNCTION__);
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Strength level should not be null.");
+ }
+
+ status_t res = mCameraProviderManager->getTorchStrengthLevel(String8(cameraId).string(),
+ torchStrength);
+ if (res != OK) {
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve torch "
+ "strength level for device %s: %s (%d)", String8(cameraId).string(),
+ strerror(-res), res);
+ }
+ ALOGI("%s: Torch strength level is: %d", __FUNCTION__, *torchStrength);
+ return Status::ok();
+}
+
String8 CameraService::getFormattedCurrentTime() {
time_t now = time(nullptr);
char formattedTime[64];
@@ -2006,6 +2040,132 @@
return OK;
}
+Status CameraService::turnOnTorchWithStrengthLevel(const String16& cameraId, int32_t torchStrength,
+ const sp<IBinder>& clientBinder) {
+ Mutex::Autolock lock(mServiceLock);
+
+ ATRACE_CALL();
+ if (clientBinder == nullptr) {
+ ALOGE("%s: torch client binder is NULL", __FUNCTION__);
+ return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT,
+ "Torch client binder in null.");
+ }
+
+ String8 id = String8(cameraId.string());
+ int uid = CameraThreadState::getCallingUid();
+
+ if (shouldRejectSystemCameraConnection(id)) {
+ return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT, "Unable to change the strength level"
+ "for system only device %s: ", id.string());
+ }
+
+ // verify id is valid
+ auto state = getCameraState(id);
+ if (state == nullptr) {
+ ALOGE("%s: camera id is invalid %s", __FUNCTION__, id.string());
+ return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
+ "Camera ID \"%s\" is a not valid camera ID", id.string());
+ }
+
+ StatusInternal cameraStatus = state->getStatus();
+ if (cameraStatus != StatusInternal::NOT_AVAILABLE &&
+ cameraStatus != StatusInternal::PRESENT) {
+ ALOGE("%s: camera id is invalid %s, status %d", __FUNCTION__, id.string(),
+ (int)cameraStatus);
+ return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
+ "Camera ID \"%s\" is a not valid camera ID", id.string());
+ }
+
+ {
+ Mutex::Autolock al(mTorchStatusMutex);
+ TorchModeStatus status;
+ status_t err = getTorchStatusLocked(id, &status);
+ if (err != OK) {
+ if (err == NAME_NOT_FOUND) {
+ return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
+ "Camera \"%s\" does not have a flash unit", id.string());
+ }
+ ALOGE("%s: getting current torch status failed for camera %s",
+ __FUNCTION__, id.string());
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
+ "Error changing torch strength level for camera \"%s\": %s (%d)",
+ id.string(), strerror(-err), err);
+ }
+
+ if (status == TorchModeStatus::NOT_AVAILABLE) {
+ if (cameraStatus == StatusInternal::NOT_AVAILABLE) {
+ ALOGE("%s: torch mode of camera %s is not available because "
+ "camera is in use.", __FUNCTION__, id.string());
+ return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
+ "Torch for camera \"%s\" is not available due to an existing camera user",
+ id.string());
+ } else {
+ ALOGE("%s: torch mode of camera %s is not available due to "
+ "insufficient resources", __FUNCTION__, id.string());
+ return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
+ "Torch for camera \"%s\" is not available due to insufficient resources",
+ id.string());
+ }
+ }
+ }
+
+ {
+ Mutex::Autolock al(mTorchUidMapMutex);
+ updateTorchUidMapLocked(cameraId, uid);
+ }
+ // Check if the current torch strength level is same as the new one.
+ bool shouldSkipTorchStrengthUpdates = mCameraProviderManager->shouldSkipTorchStrengthUpdate(
+ id.string(), torchStrength);
+
+ status_t err = mFlashlight->turnOnTorchWithStrengthLevel(id, torchStrength);
+
+ if (err != OK) {
+ int32_t errorCode;
+ String8 msg;
+ switch (err) {
+ case -ENOSYS:
+ msg = String8::format("Camera \"%s\" has no flashlight.",
+ id.string());
+ errorCode = ERROR_ILLEGAL_ARGUMENT;
+ break;
+ case -EBUSY:
+ msg = String8::format("Camera \"%s\" is in use",
+ id.string());
+ errorCode = ERROR_CAMERA_IN_USE;
+ break;
+ default:
+ msg = String8::format("Changing torch strength level failed.");
+ errorCode = ERROR_INVALID_OPERATION;
+
+ }
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(errorCode, msg.string());
+ }
+
+ {
+ // update the link to client's death
+ // Store the last client that turns on each camera's torch mode.
+ Mutex::Autolock al(mTorchClientMapMutex);
+ ssize_t index = mTorchClientMap.indexOfKey(id);
+ if (index == NAME_NOT_FOUND) {
+ mTorchClientMap.add(id, clientBinder);
+ } else {
+ mTorchClientMap.valueAt(index)->unlinkToDeath(this);
+ mTorchClientMap.replaceValueAt(index, clientBinder);
+ }
+ clientBinder->linkToDeath(this);
+ }
+
+ int clientPid = CameraThreadState::getCallingPid();
+ const char *id_cstr = id.c_str();
+ ALOGI("%s: Torch strength for camera id %s changed to %d for client PID %d",
+ __FUNCTION__, id_cstr, torchStrength, clientPid);
+ if (!shouldSkipTorchStrengthUpdates) {
+ broadcastTorchStrengthLevel(id, torchStrength);
+ }
+ return Status::ok();
+}
+
Status CameraService::setTorchMode(const String16& cameraId, bool enabled,
const sp<IBinder>& clientBinder) {
Mutex::Autolock lock(mServiceLock);
@@ -2077,13 +2237,7 @@
// Update UID map - this is used in the torch status changed callbacks, so must be done
// before setTorchMode
Mutex::Autolock al(mTorchUidMapMutex);
- if (mTorchUidMap.find(id) == mTorchUidMap.end()) {
- mTorchUidMap[id].first = uid;
- mTorchUidMap[id].second = uid;
- } else {
- // Set the pending UID
- mTorchUidMap[id].first = uid;
- }
+ updateTorchUidMapLocked(cameraId, uid);
}
status_t err = mFlashlight->setTorchMode(id, enabled);
@@ -2138,6 +2292,17 @@
return Status::ok();
}
+void CameraService::updateTorchUidMapLocked(const String16& cameraId, int uid) {
+ String8 id = String8(cameraId.string());
+ if (mTorchUidMap.find(id) == mTorchUidMap.end()) {
+ mTorchUidMap[id].first = uid;
+ mTorchUidMap[id].second = uid;
+ } else {
+ // Set the pending UID
+ mTorchUidMap[id].first = uid;
+ }
+}
+
Status CameraService::notifySystemEvent(int32_t eventId,
const std::vector<int32_t>& args) {
const int pid = CameraThreadState::getCallingPid();
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 51c734f..060f075 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -172,6 +172,12 @@
virtual binder::Status setTorchMode(const String16& cameraId, bool enabled,
const sp<IBinder>& clientBinder);
+ virtual binder::Status turnOnTorchWithStrengthLevel(const String16& cameraId,
+ int32_t torchStrength, const sp<IBinder>& clientBinder);
+
+ virtual binder::Status getTorchStrengthLevel(const String16& cameraId,
+ int32_t* torchStrength);
+
virtual binder::Status notifySystemEvent(int32_t eventId,
const std::vector<int32_t>& args);
@@ -1232,6 +1238,8 @@
hardware::camera::common::V1_0::TorchModeStatus status,
SystemCameraKind systemCameraKind);
+ void broadcastTorchStrengthLevel(const String8& cameraId, int32_t newTorchStrengthLevel);
+
void disconnectClient(const String8& id, sp<BasicClient> clientToDisconnect);
// Regular online and offline devices must not be in conflict at camera service layer.
@@ -1310,6 +1318,8 @@
bool mInjectionInitPending = false;
// Guard mInjectionInternalCamId and mInjectionInitPending.
Mutex mInjectionParametersLock;
+
+ void updateTorchUidMapLocked(const String16& cameraId, int uid);
};
} // namespace android
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 0cce2ca..d37d717 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -20,7 +20,7 @@
#include "CameraProviderManager.h"
-#include <android/hardware/camera/device/3.7/ICameraDevice.h>
+#include <android/hardware/camera/device/3.8/ICameraDevice.h>
#include <algorithm>
#include <chrono>
@@ -307,6 +307,50 @@
return OK;
}
+status_t CameraProviderManager::getTorchStrengthLevel(const std::string &id,
+ int32_t* torchStrength /*out*/) {
+ std::lock_guard<std::mutex> lock(mInterfaceMutex);
+
+ auto deviceInfo = findDeviceInfoLocked(id);
+ if (deviceInfo == nullptr) return NAME_NOT_FOUND;
+
+ return deviceInfo->getTorchStrengthLevel(torchStrength);
+}
+
+status_t CameraProviderManager::turnOnTorchWithStrengthLevel(const std::string &id,
+ int32_t torchStrength) {
+ std::lock_guard<std::mutex> lock(mInterfaceMutex);
+
+ auto deviceInfo = findDeviceInfoLocked(id);
+ if (deviceInfo == nullptr) return NAME_NOT_FOUND;
+
+ return deviceInfo->turnOnTorchWithStrengthLevel(torchStrength);
+}
+
+bool CameraProviderManager::shouldSkipTorchStrengthUpdate(const std::string &id,
+ int32_t torchStrength) const {
+ std::lock_guard<std::mutex> lock(mInterfaceMutex);
+
+ auto deviceInfo = findDeviceInfoLocked(id);
+ if (deviceInfo == nullptr) return NAME_NOT_FOUND;
+
+ if (deviceInfo->mTorchStrengthLevel == torchStrength) {
+ ALOGV("%s: Skipping torch strength level updates prev_level: %d, new_level: %d",
+ __FUNCTION__, deviceInfo->mTorchStrengthLevel, torchStrength);
+ return true;
+ }
+ return false;
+}
+
+int32_t CameraProviderManager::getTorchDefaultStrengthLevel(const std::string &id) const {
+ std::lock_guard<std::mutex> lock(mInterfaceMutex);
+
+ auto deviceInfo = findDeviceInfoLocked(id);
+ if (deviceInfo == nullptr) return NAME_NOT_FOUND;
+
+ return deviceInfo->mTorchDefaultStrengthLevel;
+}
+
bool CameraProviderManager::supportSetTorchMode(const std::string &id) const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
for (auto& provider : mProviders) {
@@ -2385,6 +2429,22 @@
mHasFlashUnit = false;
}
+ camera_metadata_entry entry =
+ mCameraCharacteristics.find(ANDROID_FLASH_INFO_STRENGTH_DEFAULT_LEVEL);
+ if (entry.count == 1) {
+ mTorchDefaultStrengthLevel = entry.data.i32[0];
+ } else {
+ mTorchDefaultStrengthLevel = 0;
+ }
+
+ entry = mCameraCharacteristics.find(ANDROID_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL);
+ if (entry.count == 1) {
+ mTorchMaximumStrengthLevel = entry.data.i32[0];
+ } else {
+ mTorchMaximumStrengthLevel = 0;
+ }
+
+ mTorchStrengthLevel = 0;
queryPhysicalCameraIds();
// Get physical camera characteristics if applicable
@@ -2468,6 +2528,80 @@
return setTorchModeForDevice<InterfaceT>(enabled);
}
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::turnOnTorchWithStrengthLevel(
+ int32_t torchStrength) {
+ const sp<CameraProviderManager::ProviderInfo::DeviceInfo3::InterfaceT> interface =
+ startDeviceInterface<CameraProviderManager::ProviderInfo::DeviceInfo3::InterfaceT>();
+ if (interface == nullptr) {
+ return DEAD_OBJECT;
+ }
+ sp<hardware::camera::device::V3_8::ICameraDevice> interface_3_8 = nullptr;
+ auto castResult_3_8 = device::V3_8::ICameraDevice::castFrom(interface);
+ if (castResult_3_8.isOk()) {
+ interface_3_8 = castResult_3_8;
+ }
+
+ if (interface_3_8 == nullptr) {
+ return INVALID_OPERATION;
+ }
+
+ Status s = interface_3_8->turnOnTorchWithStrengthLevel(torchStrength);
+ if (s == Status::OK) {
+ mTorchStrengthLevel = torchStrength;
+ }
+ return mapToStatusT(s);
+}
+
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::getTorchStrengthLevel(
+ int32_t *torchStrength) {
+ if (torchStrength == nullptr) {
+ return BAD_VALUE;
+ }
+ const sp<CameraProviderManager::ProviderInfo::DeviceInfo3::InterfaceT> interface =
+ startDeviceInterface<CameraProviderManager::ProviderInfo::DeviceInfo3::InterfaceT>();
+ if (interface == nullptr) {
+ return DEAD_OBJECT;
+ }
+ auto castResult_3_8 = device::V3_8::ICameraDevice::castFrom(interface);
+ sp<hardware::camera::device::V3_8::ICameraDevice> interface_3_8 = nullptr;
+ if (castResult_3_8.isOk()) {
+ interface_3_8 = castResult_3_8;
+ }
+
+ if (interface_3_8 == nullptr) {
+ return INVALID_OPERATION;
+ }
+
+ Status callStatus;
+ status_t res;
+ hardware::Return<void> ret = interface_3_8->getTorchStrengthLevel([&callStatus, &torchStrength]
+ (Status status, const int32_t& torchStrengthLevel) {
+ callStatus = status;
+ if (status == Status::OK) {
+ *torchStrength = torchStrengthLevel;
+ } });
+
+ if (ret.isOk()) {
+ switch (callStatus) {
+ case Status::OK:
+ // Expected case, do nothing.
+ res = OK;
+ break;
+ case Status::METHOD_NOT_SUPPORTED:
+ res = INVALID_OPERATION;
+ break;
+ default:
+ ALOGE("%s: Get torch strength level failed: %d", __FUNCTION__, callStatus);
+ res = UNKNOWN_ERROR;
+ }
+ } else {
+ ALOGE("%s: Unexpected binder error: %s", __FUNCTION__, ret.description().c_str());
+ res = UNKNOWN_ERROR;
+ }
+
+ return res;
+}
+
status_t CameraProviderManager::ProviderInfo::DeviceInfo3::getCameraInfo(
hardware::CameraInfo *info) const {
if (info == nullptr) return BAD_VALUE;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index f28d128..7d13941 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -258,6 +258,17 @@
bool supportSetTorchMode(const std::string &id) const;
/**
+ * Check if torch strength update should be skipped or not.
+ */
+ bool shouldSkipTorchStrengthUpdate(const std::string &id, int32_t torchStrength) const;
+
+ /**
+ * Return the default torch strength level if the torch strength control
+ * feature is supported.
+ */
+ int32_t getTorchDefaultStrengthLevel(const std::string &id) const;
+
+ /**
* Turn on or off the flashlight on a given camera device.
* May fail if the device does not support this API, is in active use, or if the device
* doesn't exist, etc.
@@ -265,6 +276,24 @@
status_t setTorchMode(const std::string &id, bool enabled);
/**
+ * Change the brightness level of the flash unit associated with the cameraId and
+ * set it to the value in torchStrength.
+ * If the torch is OFF and torchStrength > 0, the torch will be turned ON with the
+ * specified strength level. If the torch is ON, only the brightness level will be
+ * changed.
+ *
+ * This operation will fail if the device does not have flash unit, has flash unit
+ * but does not support this API, torchStrength is invalid or if the device doesn't
+ * exist etc.
+ */
+ status_t turnOnTorchWithStrengthLevel(const std::string &id, int32_t torchStrength);
+
+ /**
+ * Return the torch strength level of this camera device.
+ */
+ status_t getTorchStrengthLevel(const std::string &id, int32_t* torchStrength);
+
+ /**
* Setup vendor tags for all registered providers
*/
status_t setUpVendorTags();
@@ -475,10 +504,17 @@
hardware::camera::common::V1_0::CameraDeviceStatus mStatus;
wp<ProviderInfo> mParentProvider;
+ // Torch strength default, maximum levels if the torch strength control
+ // feature is supported.
+ int32_t mTorchStrengthLevel;
+ int32_t mTorchMaximumStrengthLevel;
+ int32_t mTorchDefaultStrengthLevel;
bool hasFlashUnit() const { return mHasFlashUnit; }
bool supportNativeZoomRatio() const { return mSupportNativeZoomRatio; }
virtual status_t setTorchMode(bool enabled) = 0;
+ virtual status_t turnOnTorchWithStrengthLevel(int32_t torchStrength) = 0;
+ virtual status_t getTorchStrengthLevel(int32_t *torchStrength) = 0;
virtual status_t getCameraInfo(hardware::CameraInfo *info) const = 0;
virtual bool isAPI1Compatible() const = 0;
virtual status_t dumpState(int fd) = 0;
@@ -551,6 +587,9 @@
typedef hardware::camera::device::V3_2::ICameraDevice InterfaceT;
virtual status_t setTorchMode(bool enabled) override;
+ virtual status_t turnOnTorchWithStrengthLevel(int32_t torchStrength) override;
+ virtual status_t getTorchStrengthLevel(int32_t *torchStrength) override;
+
virtual status_t getCameraInfo(hardware::CameraInfo *info) const override;
virtual bool isAPI1Compatible() const override;
virtual status_t dumpState(int fd) override;
diff --git a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.cpp b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.cpp
index 8e619e1..cca3f2e 100644
--- a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.cpp
+++ b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.cpp
@@ -70,6 +70,11 @@
return binder::Status::ok();
}
+::android::binder::Status H2BCameraServiceListener::onTorchStrengthLevelChanged(
+ const ::android::String16&, int32_t) {
+ return binder::Status::ok();
+}
+
} // implementation
} // V2_0
} // common
diff --git a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
index 7148035..7ef413f 100644
--- a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
+++ b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
@@ -54,6 +54,8 @@
virtual ::android::binder::Status onTorchStatusChanged(
int32_t status, const ::android::String16& cameraId) override;
+ virtual ::android::binder::Status onTorchStrengthLevelChanged(
+ const ::android::String16& cameraId, int32_t newStrengthLevel) override;
virtual binder::Status onCameraAccessPrioritiesChanged() {
// TODO: no implementation yet.
return binder::Status::ok();
diff --git a/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp b/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
index e46bf74..97d7bf4 100644
--- a/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
+++ b/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
@@ -466,6 +466,12 @@
// No op
return binder::Status::ok();
}
+
+ virtual binder::Status onTorchStrengthLevelChanged(const String16& /*cameraId*/,
+ int32_t /*torchStrength*/) {
+ // No op
+ return binder::Status::ok();
+ }
};
class TestCameraDeviceCallbacks : public hardware::camera2::BnCameraDeviceCallbacks {
diff --git a/services/oboeservice/AAudioCommandQueue.cpp b/services/oboeservice/AAudioCommandQueue.cpp
index ddaabe8..9bd18b3 100644
--- a/services/oboeservice/AAudioCommandQueue.cpp
+++ b/services/oboeservice/AAudioCommandQueue.cpp
@@ -28,6 +28,10 @@
aaudio_result_t AAudioCommandQueue::sendCommand(std::shared_ptr<AAudioCommand> command) {
{
std::scoped_lock<std::mutex> _l(mLock);
+ if (!mRunning) {
+ ALOGE("Tried to send command while it was not running");
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
mCommands.push(command);
mWaitWorkCond.notify_one();
}
@@ -68,7 +72,7 @@
return !mRunning || !mCommands.empty();
});
}
- if (!mCommands.empty()) {
+ if (!mCommands.empty() && mRunning) {
command = mCommands.front();
mCommands.pop();
}
@@ -76,9 +80,27 @@
return command;
}
+void AAudioCommandQueue::startWaiting() {
+ std::scoped_lock<std::mutex> _l(mLock);
+ mRunning = true;
+}
+
void AAudioCommandQueue::stopWaiting() {
std::scoped_lock<std::mutex> _l(mLock);
mRunning = false;
+ // Clear all commands in the queue as the command thread is stopped.
+ while (!mCommands.empty()) {
+ auto command = mCommands.front();
+ mCommands.pop();
+ std::scoped_lock<std::mutex> _cl(command->lock);
+ // If the command is waiting for result, returns AAUDIO_ERROR_INVALID_STATE
+ // as there is no thread waiting for the command.
+ if (command->isWaitingForReply) {
+ command->result = AAUDIO_ERROR_INVALID_STATE;
+ command->isWaitingForReply = false;
+ command->conditionVariable.notify_one();
+ }
+ }
mWaitWorkCond.notify_one();
}
diff --git a/services/oboeservice/AAudioCommandQueue.h b/services/oboeservice/AAudioCommandQueue.h
index 5f25507..64442a3 100644
--- a/services/oboeservice/AAudioCommandQueue.h
+++ b/services/oboeservice/AAudioCommandQueue.h
@@ -78,6 +78,12 @@
std::shared_ptr<AAudioCommand> waitForCommand(int64_t timeoutNanos = -1);
/**
+ * Start waiting for commands. Commands can only be pushed into the command queue after it
+ * starts waiting.
+ */
+ void startWaiting();
+
+ /**
* Force stop waiting for next command
*/
void stopWaiting();
@@ -87,7 +93,7 @@
std::condition_variable mWaitWorkCond;
std::queue<std::shared_ptr<AAudioCommand>> mCommands GUARDED_BY(mLock);
- bool mRunning GUARDED_BY(mLock) = true;
+ bool mRunning GUARDED_BY(mLock) = false;
};
} // namespace aaudio
\ No newline at end of file
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index a25a791..8b5ccaa 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -52,7 +52,6 @@
, mAtomicStreamTimestamp()
, mAudioService(audioService) {
mMmapClient.attributionSource = AttributionSourceState();
- mThreadEnabled = true;
}
AAudioServiceStreamBase::~AAudioServiceStreamBase() {
@@ -178,6 +177,7 @@
// Make sure this object does not get deleted before the run() method
// can protect it by making a strong pointer.
+ mCommandQueue.startWaiting();
mThreadEnabled = true;
incStrong(nullptr); // See run() method.
result = mCommandThread.start(this);
@@ -188,14 +188,15 @@
return result;
error:
- close();
+ closeAndClear();
+ mThreadEnabled = false;
+ mCommandQueue.stopWaiting();
+ mCommandThread.stop();
return result;
}
aaudio_result_t AAudioServiceStreamBase::close() {
- auto command = std::make_shared<AAudioCommand>(
- CLOSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
- aaudio_result_t result = mCommandQueue.sendCommand(command);
+ aaudio_result_t result = sendCommand(CLOSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
// Stop the command thread as the stream is closed.
mThreadEnabled = false;
@@ -213,25 +214,7 @@
// This will stop the stream, just in case it was not already stopped.
stop_l();
- aaudio_result_t result = AAUDIO_OK;
- sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
- if (endpoint == nullptr) {
- result = AAUDIO_ERROR_INVALID_STATE;
- } else {
- endpoint->unregisterStream(this);
- AAudioEndpointManager &endpointManager = AAudioEndpointManager::getInstance();
- endpointManager.closeEndpoint(endpoint);
-
- // AAudioService::closeStream() prevents two threads from closing at the same time.
- mServiceEndpoint.clear(); // endpoint will hold the pointer after this method returns.
- }
-
- setState(AAUDIO_STREAM_STATE_CLOSED);
-
- mediametrics::LogItem(mMetricsId)
- .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CLOSE)
- .record();
- return result;
+ return closeAndClear();
}
aaudio_result_t AAudioServiceStreamBase::startDevice() {
@@ -250,9 +233,7 @@
* An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamBase::start() {
- auto command = std::make_shared<AAudioCommand>(
- START, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
- return mCommandQueue.sendCommand(command);
+ return sendCommand(START, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
}
aaudio_result_t AAudioServiceStreamBase::start_l() {
@@ -300,9 +281,7 @@
}
aaudio_result_t AAudioServiceStreamBase::pause() {
- auto command = std::make_shared<AAudioCommand>(
- PAUSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
- return mCommandQueue.sendCommand(command);
+ return sendCommand(PAUSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
}
aaudio_result_t AAudioServiceStreamBase::pause_l() {
@@ -338,9 +317,7 @@
}
aaudio_result_t AAudioServiceStreamBase::stop() {
- auto command = std::make_shared<AAudioCommand>(
- STOP, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
- return mCommandQueue.sendCommand(command);
+ return sendCommand(STOP, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
}
aaudio_result_t AAudioServiceStreamBase::stop_l() {
@@ -385,9 +362,7 @@
}
aaudio_result_t AAudioServiceStreamBase::flush() {
- auto command = std::make_shared<AAudioCommand>(
- FLUSH, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
- return mCommandQueue.sendCommand(command);
+ return sendCommand(FLUSH, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
}
aaudio_result_t AAudioServiceStreamBase::flush_l() {
@@ -514,8 +489,7 @@
}
void AAudioServiceStreamBase::disconnect() {
- auto command = std::make_shared<AAudioCommand>(DISCONNECT);
- mCommandQueue.sendCommand(command);
+ sendCommand(DISCONNECT);
}
void AAudioServiceStreamBase::disconnect_l() {
@@ -533,12 +507,10 @@
aaudio_result_t AAudioServiceStreamBase::registerAudioThread(pid_t clientThreadId, int priority) {
const pid_t ownerPid = IPCThreadState::self()->getCallingPid(); // TODO review
- auto command = std::make_shared<AAudioCommand>(
- REGISTER_AUDIO_THREAD,
+ return sendCommand(REGISTER_AUDIO_THREAD,
std::make_shared<RegisterAudioThreadParam>(ownerPid, clientThreadId, priority),
true /*waitForReply*/,
TIMEOUT_NANOS);
- return mCommandQueue.sendCommand(command);
}
aaudio_result_t AAudioServiceStreamBase::registerAudioThread_l(
@@ -561,12 +533,10 @@
}
aaudio_result_t AAudioServiceStreamBase::unregisterAudioThread(pid_t clientThreadId) {
- auto command = std::make_shared<AAudioCommand>(
- UNREGISTER_AUDIO_THREAD,
+ return sendCommand(UNREGISTER_AUDIO_THREAD,
std::make_shared<UnregisterAudioThreadParam>(clientThreadId),
true /*waitForReply*/,
TIMEOUT_NANOS);
- return mCommandQueue.sendCommand(command);
}
aaudio_result_t AAudioServiceStreamBase::unregisterAudioThread_l(pid_t clientThreadId) {
@@ -682,12 +652,11 @@
* used to communicate with the underlying HAL or Service.
*/
aaudio_result_t AAudioServiceStreamBase::getDescription(AudioEndpointParcelable &parcelable) {
- auto command = std::make_shared<AAudioCommand>(
+ return sendCommand(
GET_DESCRIPTION,
std::make_shared<GetDescriptionParam>(&parcelable),
true /*waitForReply*/,
TIMEOUT_NANOS);
- return mCommandQueue.sendCommand(command);
}
aaudio_result_t AAudioServiceStreamBase::getDescription_l(AudioEndpointParcelable* parcelable) {
@@ -707,3 +676,33 @@
void AAudioServiceStreamBase::onVolumeChanged(float volume) {
sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
}
+
+aaudio_result_t AAudioServiceStreamBase::sendCommand(aaudio_command_opcode opCode,
+ std::shared_ptr<AAudioCommandParam> param,
+ bool waitForReply,
+ int64_t timeoutNanos) {
+ return mCommandQueue.sendCommand(std::make_shared<AAudioCommand>(
+ opCode, param, waitForReply, timeoutNanos));
+}
+
+aaudio_result_t AAudioServiceStreamBase::closeAndClear() {
+ aaudio_result_t result = AAUDIO_OK;
+ sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+ if (endpoint == nullptr) {
+ result = AAUDIO_ERROR_INVALID_STATE;
+ } else {
+ endpoint->unregisterStream(this);
+ AAudioEndpointManager &endpointManager = AAudioEndpointManager::getInstance();
+ endpointManager.closeEndpoint(endpoint);
+
+ // AAudioService::closeStream() prevents two threads from closing at the same time.
+ mServiceEndpoint.clear(); // endpoint will hold the pointer after this method returns.
+ }
+
+ setState(AAUDIO_STREAM_STATE_CLOSED);
+
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CLOSE)
+ .record();
+ return result;
+}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index aa8e8cf..dddd69f 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -366,6 +366,13 @@
aaudio_result_t sendServiceEvent(aaudio_service_event_t event,
double dataDouble);
+ aaudio_result_t sendCommand(aaudio_command_opcode opCode,
+ std::shared_ptr<AAudioCommandParam> param = nullptr,
+ bool waitForReply = false,
+ int64_t timeoutNanos = 0);
+
+ aaudio_result_t closeAndClear();
+
/**
* @return true if the queue is getting full.
*/
diff --git a/services/tuner/hidl/TunerHidlFilter.cpp b/services/tuner/hidl/TunerHidlFilter.cpp
index 7b76093..b738b57 100644
--- a/services/tuner/hidl/TunerHidlFilter.cpp
+++ b/services/tuner/hidl/TunerHidlFilter.cpp
@@ -1036,6 +1036,8 @@
media.streamId = static_cast<int32_t>(mediaEvent.streamId);
media.isPtsPresent = mediaEvent.isPtsPresent;
media.pts = static_cast<int64_t>(mediaEvent.pts);
+ media.isDtsPresent = false;
+ media.dts = static_cast<int64_t>(-1);
media.dataLength = static_cast<int64_t>(mediaEvent.dataLength);
media.offset = static_cast<int64_t>(mediaEvent.offset);
media.isSecureMemory = mediaEvent.isSecureMemory;
@@ -1078,7 +1080,7 @@
section.tableId = static_cast<int32_t>(sectionEvent.tableId);
section.version = static_cast<int32_t>(sectionEvent.version);
section.sectionNum = static_cast<int32_t>(sectionEvent.sectionNum);
- section.dataLength = static_cast<int32_t>(sectionEvent.dataLength);
+ section.dataLength = static_cast<int64_t>(sectionEvent.dataLength);
DemuxFilterEvent filterEvent;
filterEvent.set<DemuxFilterEvent::section>(move(section));
@@ -1186,6 +1188,7 @@
DemuxFilterDownloadEvent download;
download.itemId = static_cast<int32_t>(downloadEvent.itemId);
+ download.downloadId = -1;
download.itemFragmentIndex = static_cast<int32_t>(downloadEvent.itemFragmentIndex);
download.mpuSequenceNumber = static_cast<int32_t>(downloadEvent.mpuSequenceNumber);
download.lastItemFragmentIndex = static_cast<int32_t>(downloadEvent.lastItemFragmentIndex);