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);