Merge "Increase OggExtractor's confidence"
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 4a801a7..488641d 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -7758,6 +7758,13 @@
      */
     ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA         = 13,
 
+    /**
+     * <p>The camera device is only accessible by Android's system components and privileged
+     * applications. Processes need to have the android.permission.SYSTEM_CAMERA in
+     * addition to android.permission.CAMERA in order to connect to this camera device.</p>
+     */
+    ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA             = 14,
+
 } acamera_metadata_enum_android_request_available_capabilities_t;
 
 
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 98164fd..e39f885 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -417,6 +417,7 @@
     int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec);
     DisplayInfo mainDpyInfo;
     Vector<int64_t> timestamps;
+    bool firstFrame = true;
 
     assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL));
 
@@ -433,6 +434,11 @@
         int64_t ptsUsec;
         uint32_t flags;
 
+        if (firstFrame) {
+            ATRACE_NAME("first_frame");
+            firstFrame = false;
+        }
+
         if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) {
             if (gVerbose) {
                 printf("Time limit reached\n");
diff --git a/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp b/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp
index 99fd883..a510487 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp
@@ -38,7 +38,7 @@
     configureRpcThreadpool(8, true /* callerWillJoin */);
 
     // Setup hwbinder service
-    LazyServiceRegistrar serviceRegistrar;
+    auto serviceRegistrar = LazyServiceRegistrar::getInstance();
 
     // Setup hwbinder service
     CHECK_EQ(serviceRegistrar.registerService(drmFactory, "clearkey"), android::NO_ERROR)
diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h
index 712f118..16e794a 100644
--- a/include/private/media/VideoFrame.h
+++ b/include/private/media/VideoFrame.h
@@ -41,7 +41,7 @@
             uint32_t angle, uint32_t bpp, bool hasData, size_t iccSize):
         mWidth(width), mHeight(height),
         mDisplayWidth(displayWidth), mDisplayHeight(displayHeight),
-        mTileWidth(tileWidth), mTileHeight(tileHeight),
+        mTileWidth(tileWidth), mTileHeight(tileHeight), mDurationUs(0),
         mRotationAngle(angle), mBytesPerPixel(bpp), mRowBytes(bpp * width),
         mSize(hasData ? (bpp * width * height) : 0),
         mIccSize(iccSize), mReserved(0) {
@@ -78,6 +78,7 @@
     uint32_t mDisplayHeight;   // Display height before rotation
     uint32_t mTileWidth;       // Tile width (0 if image doesn't have grid)
     uint32_t mTileHeight;      // Tile height (0 if image doesn't have grid)
+    int64_t  mDurationUs;      // Frame duration in microseconds
     int32_t  mRotationAngle;   // Rotation angle, clockwise, should be multiple of 90
     uint32_t mBytesPerPixel;   // Number of bytes per pixel
     uint32_t mRowBytes;        // Number of bytes per row before rotation
diff --git a/media/bufferpool/1.0/AccessorImpl.cpp b/media/bufferpool/1.0/AccessorImpl.cpp
index 6b90088..09006ca 100644
--- a/media/bufferpool/1.0/AccessorImpl.cpp
+++ b/media/bufferpool/1.0/AccessorImpl.cpp
@@ -151,6 +151,7 @@
                 newConnection->initialize(accessor, id);
                 *connection = newConnection;
                 *pConnectionId = id;
+                mBufferPool.mConnectionIds.insert(id);
                 ++sSeqId;
             }
         }
@@ -305,7 +306,12 @@
         found->second->mSenderValidated = true;
         return true;
     }
-    // TODO: verify there is target connection Id
+    if (mConnectionIds.find(message.targetConnectionId) == mConnectionIds.end()) {
+        // N.B: it could be fake or receive connection already closed.
+        ALOGD("bufferpool %p receiver connection %lld is no longer valid",
+              this, (long long)message.targetConnectionId);
+        return false;
+    }
     mStats.onBufferSent();
     mTransactions.insert(std::make_pair(
             message.transactionId,
@@ -450,6 +456,7 @@
             }
         }
     }
+    mConnectionIds.erase(connectionId);
     return true;
 }
 
diff --git a/media/bufferpool/1.0/AccessorImpl.h b/media/bufferpool/1.0/AccessorImpl.h
index c04dbf3..84cb685 100644
--- a/media/bufferpool/1.0/AccessorImpl.h
+++ b/media/bufferpool/1.0/AccessorImpl.h
@@ -94,6 +94,7 @@
 
         std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
         std::set<BufferId> mFreeBuffers;
+        std::set<ConnectionId> mConnectionIds;
 
         /// Buffer pool statistics which tracks allocation and transfer statistics.
         struct Stats {
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
index 32eaae9..929a20e 100644
--- a/media/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -163,6 +163,7 @@
                 *connection = newConnection;
                 *pConnectionId = id;
                 *pMsgId = mBufferPool.mInvalidation.mInvalidationId;
+                mBufferPool.mConnectionIds.insert(id);
                 mBufferPool.mInvalidationChannel.getDesc(invDescPtr);
                 mBufferPool.mInvalidation.onConnect(id, observer);
                 ++sSeqId;
@@ -474,7 +475,12 @@
         found->second->mSenderValidated = true;
         return true;
     }
-    // TODO: verify there is target connection Id
+    if (mConnectionIds.find(message.targetConnectionId) == mConnectionIds.end()) {
+        // N.B: it could be fake or receive connection already closed.
+        ALOGD("bufferpool2 %p receiver connection %lld is no longer valid",
+              this, (long long)message.targetConnectionId);
+        return false;
+    }
     mStats.onBufferSent();
     mTransactions.insert(std::make_pair(
             message.transactionId,
@@ -644,6 +650,7 @@
             }
         }
     }
+    mConnectionIds.erase(connectionId);
     return true;
 }
 
@@ -774,11 +781,19 @@
             std::mutex &mutex,
             std::condition_variable &cv,
             bool &ready) {
+    constexpr uint32_t NUM_SPIN_TO_INCREASE_SLEEP = 1024;
+    constexpr uint32_t NUM_SPIN_TO_LOG = 1024*8;
+    constexpr useconds_t MAX_SLEEP_US = 10000;
+    uint32_t numSpin = 0;
+    useconds_t sleepUs = 1;
+
     while(true) {
         std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> copied;
         {
             std::unique_lock<std::mutex> lock(mutex);
             if (!ready) {
+                numSpin = 0;
+                sleepUs = 1;
                 cv.wait(lock);
             }
             copied.insert(accessors.begin(), accessors.end());
@@ -800,9 +815,20 @@
             if (accessors.size() == 0) {
                 ready = false;
             } else {
-                // prevent draining cpu.
+                // TODO Use an efficient way to wait over FMQ.
+                // N.B. Since there is not a efficient way to wait over FMQ,
+                // polling over the FMQ is the current way to prevent draining
+                // CPU.
                 lock.unlock();
-                std::this_thread::yield();
+                ++numSpin;
+                if (numSpin % NUM_SPIN_TO_INCREASE_SLEEP == 0 &&
+                    sleepUs < MAX_SLEEP_US) {
+                    sleepUs *= 10;
+                }
+                if (numSpin % NUM_SPIN_TO_LOG == 0) {
+                    ALOGW("invalidator thread spinning");
+                }
+                ::usleep(sleepUs);
             }
         }
     }
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
index eea72b9..807e0f1 100644
--- a/media/bufferpool/2.0/AccessorImpl.h
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -111,6 +111,7 @@
 
         std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
         std::set<BufferId> mFreeBuffers;
+        std::set<ConnectionId> mConnectionIds;
 
         struct Invalidation {
             static std::atomic<std::uint32_t> sInvSeqId;
diff --git a/media/codec2/components/aom/Android.bp b/media/codec2/components/aom/Android.bp
index 0fabf5c..61dbd4c 100644
--- a/media/codec2/components/aom/Android.bp
+++ b/media/codec2/components/aom/Android.bp
@@ -1,10 +1,16 @@
 cc_library_shared {
-    name: "libcodec2_soft_av1dec",
+    name: "libcodec2_soft_av1dec_aom",
     defaults: [
         "libcodec2_soft-defaults",
         "libcodec2_soft_sanitize_all-defaults",
     ],
 
+    // coordinated with frameworks/av/media/codec2/components/gav1/Android.bp
+    // so only 1 of them has the official c2.android.av1.decoder name
+    cflags: [
+        "-DCODECNAME=\"c2.android.av1-aom.decoder\"",
+    ],
+
     srcs: ["C2SoftAomDec.cpp"],
     static_libs: ["libaom"],
 
diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp
index 0cf277f..36137e6 100644
--- a/media/codec2/components/aom/C2SoftAomDec.cpp
+++ b/media/codec2/components/aom/C2SoftAomDec.cpp
@@ -29,7 +29,8 @@
 
 namespace android {
 
-constexpr char COMPONENT_NAME[] = "c2.android.av1.decoder";
+// codecname set and passed in as a compile flag from Android.bp
+constexpr char COMPONENT_NAME[] = CODECNAME;
 
 class C2SoftAomDec::IntfImpl : public SimpleInterface<void>::BaseParams {
   public:
diff --git a/media/codec2/components/gav1/Android.bp b/media/codec2/components/gav1/Android.bp
index 0a0545d..5c4abb7 100644
--- a/media/codec2/components/gav1/Android.bp
+++ b/media/codec2/components/gav1/Android.bp
@@ -1,10 +1,16 @@
 cc_library_shared {
-    name: "libcodec2_soft_gav1dec",
+    name: "libcodec2_soft_av1dec_gav1",
     defaults: [
         "libcodec2_soft-defaults",
         "libcodec2_soft_sanitize_all-defaults",
     ],
 
+    // coordinated with frameworks/av/media/codec2/components/aom/Android.bp
+    // so only 1 of them has the official c2.android.av1.decoder name
+    cflags: [
+        "-DCODECNAME=\"c2.android.av1.decoder\"",
+    ],
+
     srcs: ["C2SoftGav1Dec.cpp"],
     static_libs: ["libgav1"],
 
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
index f5321ba..ec5f549 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
@@ -27,7 +27,8 @@
 
 namespace android {
 
-constexpr char COMPONENT_NAME[] = "c2.android.gav1.decoder";
+// codecname set and passed in as a compile flag from Android.bp
+constexpr char COMPONENT_NAME[] = CODECNAME;
 
 class C2SoftGav1Dec::IntfImpl : public SimpleInterface<void>::BaseParams {
  public:
diff --git a/media/codec2/hidl/1.0/vts/functional/common/README.md b/media/codec2/hidl/1.0/vts/functional/common/README.md
index 50e8356..f2f579c 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/README.md
+++ b/media/codec2/hidl/1.0/vts/functional/common/README.md
@@ -1,31 +1,36 @@
-## Codec2 VTS Hal @ 1.0 tests ##
----
-#### master :
+# Codec2 VTS Hal @ 1.0 tests #
+
+## master :
 Functionality of master is to enumerate all the Codec2 components available in C2 media service.
 
-usage: VtsHalMediaC2V1\_0TargetMasterTest -I default
+usage: `VtsHalMediaC2V1_0TargetMasterTest -I default`
 
-#### component :
+## component :
 Functionality of component test is to validate common functionality across all the Codec2 components available in C2 media service. For a standard C2 component, these tests are expected to pass.
 
-usage: VtsHalMediaC2V1\_0TargetComponentTest -I software -C <comp name>
-example: VtsHalMediaC2V1\_0TargetComponentTest -I software -C c2.android.vorbis.decoder
+usage: `VtsHalMediaC2V1_0TargetComponentTest -I software -C <comp name>`
 
-#### audio :
-Functionality of audio test is to validate audio specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests.
+example: `VtsHalMediaC2V1_0TargetComponentTest -I software -C c2.android.vorbis.decoder`
 
-usage: VtsHalMediaC2V1\_0TargetAudioDecTest -I default -C <comp name> -P /sdcard/media/
-usage: VtsHalMediaC2V1\_0TargetAudioEncTest -I software -C <comp name> -P /sdcard/media/
+## audio :
+Functionality of audio test is to validate audio specific functionality of Codec2 components. The resource files for this test are taken from `frameworks/av/media/codec2/hidl/1.0/vts/functional/res`. The path to these files on the device can be specified with `-P`. (If the device path is omitted, `/data/local/tmp/media/` is the default value.)
 
-example: VtsHalMediaC2V1\_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /sdcard/media/
-example: VtsHalMediaC2V1\_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /sdcard/media/
+usage: `VtsHalMediaC2V1_0TargetAudioDecTest -I default -C <comp name> -P <path to resource files>`
 
-#### video :
-Functionality of video test is to validate video specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests.
+usage: `VtsHalMediaC2V1_0TargetAudioEncTest -I software -C <comp name> -P <path to resource files>`
 
-usage: VtsHalMediaC2V1\_0TargetVideoDecTest -I default -C <comp name> -P /sdcard/media/
-usage: VtsHalMediaC2V1\_0TargetVideoEncTest -I software -C <comp name> -P /sdcard/media/
+example: `VtsHalMediaC2V1_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /data/local/tmp/media/`
 
-example: VtsHalMediaC2V1\_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /sdcard/media/
-example: VtsHalMediaC2V1\_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /sdcard/media/
+example: `VtsHalMediaC2V1_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /data/local/tmp/media/`
+
+## video :
+Functionality of video test is to validate video specific functionality of Codec2 components. The resource files for this test are taken from `frameworks/av/media/codec2/hidl/1.0/vts/functional/res`. The path to these files on the device can be specified with `-P`. (If the device path is omitted, `/data/local/tmp/media/` is the default value.)
+
+usage: `VtsHalMediaC2V1_0TargetVideoDecTest -I default -C <comp name> -P <path to resource files>`
+
+usage: `VtsHalMediaC2V1_0TargetVideoEncTest -I software -C <comp name> -P <path to resource files>`
+
+example: `VtsHalMediaC2V1_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /data/local/tmp/media/`
+
+example: `VtsHalMediaC2V1_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /data/local/tmp/media/`
 
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
index c577dac..db59e54 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
@@ -118,7 +118,7 @@
         registerTestService<IComponentStore>();
     }
 
-    ComponentTestEnvironment() : res("/sdcard/media/") {}
+    ComponentTestEnvironment() : res("/data/local/tmp/media/") {}
 
     void setComponent(const char* _component) { component = _component; }
 
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 0e1bb0a..1883225 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -29,6 +29,7 @@
 #include <android/hardware/cas/native/1.0/IDescrambler.h>
 #include <android-base/stringprintf.h>
 #include <binder/MemoryDealer.h>
+#include <cutils/properties.h>
 #include <gui/Surface.h>
 #include <media/openmax/OMX_Core.h>
 #include <media/stagefright/foundation/ABuffer.h>
diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp
index 6b4ed35..5b2bd7b 100644
--- a/media/codec2/vndk/C2Store.cpp
+++ b/media/codec2/vndk/C2Store.cpp
@@ -848,8 +848,8 @@
     emplace("libcodec2_soft_amrnbenc.so");
     emplace("libcodec2_soft_amrwbdec.so");
     emplace("libcodec2_soft_amrwbenc.so");
-    emplace("libcodec2_soft_av1dec.so");
-    emplace("libcodec2_soft_gav1dec.so");
+    //emplace("libcodec2_soft_av1dec_aom.so"); // deprecated for the gav1 implementation
+    emplace("libcodec2_soft_av1dec_gav1.so");
     emplace("libcodec2_soft_avcdec.so");
     emplace("libcodec2_soft_avcenc.so");
     emplace("libcodec2_soft_flacdec.so");
diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp
index ffeff42..26431a4 100644
--- a/media/extractors/amr/AMRExtractor.cpp
+++ b/media/extractors/amr/AMRExtractor.cpp
@@ -144,6 +144,7 @@
 
 AMRExtractor::AMRExtractor(DataSourceHelper *source)
     : mDataSource(source),
+      mMeta(NULL),
       mInitCheck(NO_INIT),
       mOffsetTableLength(0) {
     float confidence;
@@ -191,7 +192,9 @@
 
 AMRExtractor::~AMRExtractor() {
     delete mDataSource;
-    AMediaFormat_delete(mMeta);
+    if (mMeta) {
+        AMediaFormat_delete(mMeta);
+    }
 }
 
 media_status_t AMRExtractor::getMetaData(AMediaFormat *meta) {
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 36cab1d..b91d16f 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -5820,11 +5820,11 @@
                       meta, AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale);
                 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
 
-                int32_t byteOrder;
-                AMediaFormat_getInt32(mFormat,
+                int32_t byteOrder = 0;
+                bool isGetBigEndian = AMediaFormat_getInt32(mFormat,
                         AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN, &byteOrder);
 
-                if (byteOrder == 1) {
+                if (isGetBigEndian && byteOrder == 1) {
                     // Big-endian -> little-endian
                     uint16_t *dstData = (uint16_t *)buf;
                     uint16_t *srcData = (uint16_t *)buf;
diff --git a/media/extractors/mp4/SampleTable.cpp b/media/extractors/mp4/SampleTable.cpp
index e7e8901..59c8200 100644
--- a/media/extractors/mp4/SampleTable.cpp
+++ b/media/extractors/mp4/SampleTable.cpp
@@ -652,6 +652,7 @@
     }
 
     mSampleTimeEntries = new (std::nothrow) SampleTimeEntry[mNumSampleSizes];
+    memset(mSampleTimeEntries, 0, sizeof(SampleTimeEntry) * mNumSampleSizes);
     if (!mSampleTimeEntries) {
         ALOGE("Cannot allocate sample entry table with %llu entries.",
                 (unsigned long long)mNumSampleSizes);
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index dd95e34..efa0512 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -1339,10 +1339,14 @@
         }
         case GET_EFFECT_DESCRIPTOR: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            effect_uuid_t uuid;
-            data.read(&uuid, sizeof(effect_uuid_t));
-            effect_uuid_t type;
-            data.read(&type, sizeof(effect_uuid_t));
+            effect_uuid_t uuid = {};
+            if (data.read(&uuid, sizeof(effect_uuid_t)) != NO_ERROR) {
+                android_errorWriteLog(0x534e4554, "139417189");
+            }
+            effect_uuid_t type = {};
+            if (data.read(&type, sizeof(effect_uuid_t)) != NO_ERROR) {
+                android_errorWriteLog(0x534e4554, "139417189");
+            }
             uint32_t preferredTypeFlag = data.readUint32();
             effect_descriptor_t desc = {};
             status_t status = getEffectDescriptor(&uuid, &type, preferredTypeFlag, &desc);
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index a7c0f84..bad4210 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -44,8 +44,7 @@
     info->mHeight = videoFrame->mHeight;
     info->mRotationAngle = videoFrame->mRotationAngle;
     info->mBytesPerPixel = videoFrame->mBytesPerPixel;
-    // TODO: retrieve per-frame duration from extractor/metadataretriever.
-    info->mDurationUs = 33333;
+    info->mDurationUs = videoFrame->mDurationUs;
     if (videoFrame->mIccSize > 0) {
         info->mIccData.assign(
                 videoFrame->getFlattenedIccData(),
diff --git a/media/libmedia/IResourceManagerService.cpp b/media/libmedia/IResourceManagerService.cpp
index 00f9b88..f8a0a14 100644
--- a/media/libmedia/IResourceManagerService.cpp
+++ b/media/libmedia/IResourceManagerService.cpp
@@ -32,6 +32,7 @@
     CONFIG = IBinder::FIRST_CALL_TRANSACTION,
     ADD_RESOURCE,
     REMOVE_RESOURCE,
+    REMOVE_CLIENT,
     RECLAIM_RESOURCE,
 };
 
@@ -87,13 +88,23 @@
         remote()->transact(ADD_RESOURCE, data, &reply);
     }
 
-    virtual void removeResource(int pid, int64_t clientId) {
+    virtual void removeResource(int pid, int64_t clientId, const Vector<MediaResource> &resources) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
+        data.writeInt32(pid);
+        data.writeInt64(clientId);
+        writeToParcel(&data, resources);
+
+        remote()->transact(REMOVE_RESOURCE, data, &reply);
+    }
+
+    virtual void removeClient(int pid, int64_t clientId) {
         Parcel data, reply;
         data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
         data.writeInt32(pid);
         data.writeInt64(clientId);
 
-        remote()->transact(REMOVE_RESOURCE, data, &reply);
+        remote()->transact(REMOVE_CLIENT, data, &reply);
     }
 
     virtual bool reclaimResource(int callingPid, const Vector<MediaResource> &resources) {
@@ -148,7 +159,17 @@
             CHECK_INTERFACE(IResourceManagerService, data, reply);
             int pid = data.readInt32();
             int64_t clientId = data.readInt64();
-            removeResource(pid, clientId);
+            Vector<MediaResource> resources;
+            readFromParcel(data, &resources);
+            removeResource(pid, clientId, resources);
+            return NO_ERROR;
+        } break;
+
+        case REMOVE_CLIENT: {
+            CHECK_INTERFACE(IResourceManagerService, data, reply);
+            int pid = data.readInt32();
+            int64_t clientId = data.readInt64();
+            removeClient(pid, clientId);
             return NO_ERROR;
         } break;
 
diff --git a/media/libmedia/include/media/IResourceManagerService.h b/media/libmedia/include/media/IResourceManagerService.h
index 404519b..8992f8b 100644
--- a/media/libmedia/include/media/IResourceManagerService.h
+++ b/media/libmedia/include/media/IResourceManagerService.h
@@ -44,7 +44,10 @@
             const sp<IResourceManagerClient> client,
             const Vector<MediaResource> &resources) = 0;
 
-    virtual void removeResource(int pid, int64_t clientId) = 0;
+    virtual void removeResource(int pid, int64_t clientId,
+            const Vector<MediaResource> &resources) = 0;
+
+    virtual void removeClient(int pid, int64_t clientId) = 0;
 
     virtual bool reclaimResource(
             int callingPid,
diff --git a/media/libmedia/include/media/MediaResource.h b/media/libmedia/include/media/MediaResource.h
index 10d0e3b..10a07bb 100644
--- a/media/libmedia/include/media/MediaResource.h
+++ b/media/libmedia/include/media/MediaResource.h
@@ -63,6 +63,8 @@
         case MediaResource::kSecureCodec:    return "secure-codec";
         case MediaResource::kNonSecureCodec: return "non-secure-codec";
         case MediaResource::kGraphicMemory:  return "graphic-memory";
+        case MediaResource::kCpuBoost:       return "cpu-boost";
+        case MediaResource::kBattery:        return "battery";
         default:                             return def;
     }
 }
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index dfd3933..8ac169f 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -58,6 +58,7 @@
 #include <media/AudioTrack.h>
 #include <media/MemoryLeakTrackUtil.h>
 #include <media/stagefright/InterfaceUtils.h>
+#include <media/stagefright/MediaCodecConstants.h>
 #include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/Utils.h>
@@ -264,6 +265,172 @@
     return ok;
 }
 
+static void dumpCodecDetails(int fd, const sp<IMediaCodecList> &codecList, bool queryDecoders) {
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    const char *codecType = queryDecoders? "Decoder" : "Encoder";
+    snprintf(buffer, SIZE - 1, "\n%s infos by media types:\n"
+             "=============================\n", codecType);
+    result.append(buffer);
+
+    size_t numCodecs = codecList->countCodecs();
+
+    // gather all media types supported by codec class, and link to codecs that support them
+    KeyedVector<AString, Vector<sp<MediaCodecInfo>>> allMediaTypes;
+    for (size_t codec_ix = 0; codec_ix < numCodecs; ++codec_ix) {
+        sp<MediaCodecInfo> info = codecList->getCodecInfo(codec_ix);
+        if (info->isEncoder() == !queryDecoders) {
+            Vector<AString> supportedMediaTypes;
+            info->getSupportedMediaTypes(&supportedMediaTypes);
+            if (!supportedMediaTypes.size()) {
+                snprintf(buffer, SIZE - 1, "warning: %s does not support any media types\n",
+                        info->getCodecName());
+                result.append(buffer);
+            } else {
+                for (const AString &mediaType : supportedMediaTypes) {
+                    if (allMediaTypes.indexOfKey(mediaType) < 0) {
+                        allMediaTypes.add(mediaType, Vector<sp<MediaCodecInfo>>());
+                    }
+                    allMediaTypes.editValueFor(mediaType).add(info);
+                }
+            }
+        }
+    }
+
+    KeyedVector<AString, bool> visitedCodecs;
+    for (size_t type_ix = 0; type_ix < allMediaTypes.size(); ++type_ix) {
+        const AString &mediaType = allMediaTypes.keyAt(type_ix);
+        snprintf(buffer, SIZE - 1, "\nMedia type '%s':\n", mediaType.c_str());
+        result.append(buffer);
+
+        for (const sp<MediaCodecInfo> &info : allMediaTypes.valueAt(type_ix)) {
+            sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(mediaType.c_str());
+            if (caps == NULL) {
+                snprintf(buffer, SIZE - 1, "warning: %s does not have capabilities for type %s\n",
+                        info->getCodecName(), mediaType.c_str());
+                result.append(buffer);
+                continue;
+            }
+            snprintf(buffer, SIZE - 1, "  %s \"%s\" supports\n",
+                       codecType, info->getCodecName());
+            result.append(buffer);
+
+            auto printList = [&](const char *type, const Vector<AString> &values){
+                snprintf(buffer, SIZE - 1, "    %s: [", type);
+                result.append(buffer);
+                for (size_t j = 0; j < values.size(); ++j) {
+                    snprintf(buffer, SIZE - 1, "\n      %s%s", values[j].c_str(),
+                            j == values.size() - 1 ? " " : ",");
+                    result.append(buffer);
+                }
+                result.append("]\n");
+            };
+
+            if (visitedCodecs.indexOfKey(info->getCodecName()) < 0) {
+                visitedCodecs.add(info->getCodecName(), true);
+                {
+                    Vector<AString> aliases;
+                    info->getAliases(&aliases);
+                    // quote alias
+                    for (AString &alias : aliases) {
+                        alias.insert("\"", 1, 0);
+                        alias.append('"');
+                    }
+                    printList("aliases", aliases);
+                }
+                {
+                    uint32_t attrs = info->getAttributes();
+                    Vector<AString> list;
+                    list.add(AStringPrintf("encoder: %d",
+                                           !!(attrs & MediaCodecInfo::kFlagIsEncoder)));
+                    list.add(AStringPrintf("vendor: %d",
+                                           !!(attrs & MediaCodecInfo::kFlagIsVendor)));
+                    list.add(AStringPrintf("software-only: %d",
+                                           !!(attrs & MediaCodecInfo::kFlagIsSoftwareOnly)));
+                    list.add(AStringPrintf("hw-accelerated: %d",
+                                           !!(attrs & MediaCodecInfo::kFlagIsHardwareAccelerated)));
+                    printList(AStringPrintf("attributes: %#x", attrs).c_str(), list);
+                }
+
+                snprintf(buffer, SIZE - 1, "    owner: \"%s\"\n", info->getOwnerName());
+                result.append(buffer);
+                snprintf(buffer, SIZE - 1, "    rank: %u\n", info->getRank());
+                result.append(buffer);
+            } else {
+                result.append("    aliases, attributes, owner, rank: see above\n");
+            }
+
+            {
+                Vector<AString> list;
+                Vector<MediaCodecInfo::ProfileLevel> profileLevels;
+                caps->getSupportedProfileLevels(&profileLevels);
+                for (const MediaCodecInfo::ProfileLevel &pl : profileLevels) {
+                    const char *niceProfile =
+                        mediaType.equalsIgnoreCase(MIMETYPE_AUDIO_AAC)
+                            ? asString_AACObject(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2)
+                            ? asString_MPEG2Profile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263)
+                            ? asString_H263Profile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4)
+                            ? asString_MPEG4Profile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC)
+                            ? asString_AVCProfile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8)
+                            ? asString_VP8Profile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC)
+                            ? asString_HEVCProfile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9)
+                            ? asString_VP9Profile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1)
+                            ? asString_AV1Profile(pl.mProfile) : "??";
+                    const char *niceLevel =
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2)
+                            ? asString_MPEG2Level(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263)
+                            ? asString_H263Level(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4)
+                            ? asString_MPEG4Level(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC)
+                            ? asString_AVCLevel(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8)
+                            ? asString_VP8Level(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC)
+                            ? asString_HEVCTierLevel(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9)
+                            ? asString_VP9Level(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1)
+                            ? asString_AV1Level(pl.mLevel) : "??";
+
+                    list.add(AStringPrintf("% 5u/% 5u (%s/%s)",
+                            pl.mProfile, pl.mLevel, niceProfile, niceLevel));
+                }
+                printList("profile/levels", list);
+            }
+
+            {
+                Vector<AString> list;
+                Vector<uint32_t> colors;
+                caps->getSupportedColorFormats(&colors);
+                for (uint32_t color : colors) {
+                    list.add(AStringPrintf("%#x (%s)", color,
+                            asString_ColorFormat((int32_t)color)));
+                }
+                printList("colors", list);
+            }
+
+            snprintf(buffer, SIZE - 1, "    details: %s\n",
+                     caps->getDetails()->debugString(6).c_str());
+            result.append(buffer);
+        }
+    }
+    result.append("\n");
+    ::write(fd, result.string(), result.size());
+}
+
+
 // TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround
 /* static */ int MediaPlayerService::AudioOutput::mMinBufferCount = 4;
 /* static */ bool MediaPlayerService::AudioOutput::mIsOnEmulator = false;
@@ -423,7 +590,7 @@
     SortedVector< sp<MediaRecorderClient> > mediaRecorderClients;
 
     if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
-        snprintf(buffer, SIZE, "Permission Denial: "
+        snprintf(buffer, SIZE - 1, "Permission Denial: "
                 "can't dump MediaPlayerService from pid=%d, uid=%d\n",
                 IPCThreadState::self()->getCallingPid(),
                 IPCThreadState::self()->getCallingUid());
@@ -452,11 +619,11 @@
         }
 
         result.append(" Files opened and/or mapped:\n");
-        snprintf(buffer, SIZE, "/proc/%d/maps", getpid());
+        snprintf(buffer, SIZE - 1, "/proc/%d/maps", getpid());
         FILE *f = fopen(buffer, "r");
         if (f) {
             while (!feof(f)) {
-                fgets(buffer, SIZE, f);
+                fgets(buffer, SIZE - 1, f);
                 if (strstr(buffer, " /storage/") ||
                     strstr(buffer, " /system/sounds/") ||
                     strstr(buffer, " /data/") ||
@@ -472,13 +639,13 @@
             result.append("\n");
         }
 
-        snprintf(buffer, SIZE, "/proc/%d/fd", getpid());
+        snprintf(buffer, SIZE - 1, "/proc/%d/fd", getpid());
         DIR *d = opendir(buffer);
         if (d) {
             struct dirent *ent;
             while((ent = readdir(d)) != NULL) {
                 if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
-                    snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name);
+                    snprintf(buffer, SIZE - 1, "/proc/%d/fd/%s", getpid(), ent->d_name);
                     struct stat s;
                     if (lstat(buffer, &s) == 0) {
                         if ((s.st_mode & S_IFMT) == S_IFLNK) {
@@ -543,6 +710,11 @@
         }
     }
     write(fd, result.string(), result.size());
+
+    sp<IMediaCodecList> codecList = getCodecList();
+    dumpCodecDetails(fd, codecList, true /* decoders */);
+    dumpCodecDetails(fd, codecList, false /* !decoders */);
+
     return NO_ERROR;
 }
 
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index c6ec6de..f99dd1c 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -43,6 +43,7 @@
 
 static const int64_t kBufferTimeOutUs = 10000LL; // 10 msec
 static const size_t kRetryCount = 50; // must be >0
+static const int64_t kDefaultSampleDurationUs = 33333LL; // 33ms
 
 sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
         int32_t width, int32_t height, int32_t tileWidth, int32_t tileHeight,
@@ -199,7 +200,19 @@
             tileWidth = tileHeight = 0;
         }
     }
-    return allocMetaFrame(trackMeta, width, height, tileWidth, tileHeight, dstBpp);
+
+    sp<IMemory> metaMem = allocMetaFrame(trackMeta, width, height, tileWidth, tileHeight, dstBpp);
+
+    // try to fill sequence meta's duration based on average frame rate,
+    // default to 33ms if frame rate is unavailable.
+    int32_t frameRate;
+    VideoFrame* meta = static_cast<VideoFrame*>(metaMem->pointer());
+    if (trackMeta->findInt32(kKeyFrameRate, &frameRate) && frameRate > 0) {
+        meta->mDurationUs = 1000000LL / frameRate;
+    } else {
+        meta->mDurationUs = kDefaultSampleDurationUs;
+    }
+    return metaMem;
 }
 
 FrameDecoder::FrameDecoder(
@@ -443,7 +456,8 @@
       mFrame(NULL),
       mIsAvcOrHevc(false),
       mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
-      mTargetTimeUs(-1LL) {
+      mTargetTimeUs(-1LL),
+      mDefaultSampleDurationUs(0) {
 }
 
 sp<AMessage> VideoFrameDecoder::onGetFormatAndSeekOptions(
@@ -506,6 +520,13 @@
         }
     }
 
+    int32_t frameRate;
+    if (trackMeta()->findInt32(kKeyFrameRate, &frameRate) && frameRate > 0) {
+        mDefaultSampleDurationUs = 1000000LL / frameRate;
+    } else {
+        mDefaultSampleDurationUs = kDefaultSampleDurationUs;
+    }
+
     return videoFormat;
 }
 
@@ -526,6 +547,12 @@
         // option, in which case we need to actually decode to targetTimeUs.
         *flags |= MediaCodec::BUFFER_FLAG_EOS;
     }
+    int64_t durationUs;
+    if (sampleMeta.findInt64(kKeyDuration, &durationUs)) {
+        mSampleDurations.push_back(durationUs);
+    } else {
+        mSampleDurations.push_back(mDefaultSampleDurationUs);
+    }
     return OK;
 }
 
@@ -533,6 +560,11 @@
         const sp<MediaCodecBuffer> &videoFrameBuffer,
         const sp<AMessage> &outputFormat,
         int64_t timeUs, bool *done) {
+    int64_t durationUs = mDefaultSampleDurationUs;
+    if (!mSampleDurations.empty()) {
+        durationUs = *mSampleDurations.begin();
+        mSampleDurations.erase(mSampleDurations.begin());
+    }
     bool shouldOutput = (mTargetTimeUs < 0LL) || (timeUs >= mTargetTimeUs);
 
     // If this is not the target frame, skip color convert.
@@ -587,6 +619,8 @@
         setFrame(frameMem);
     }
 
+    mFrame->mDurationUs = durationUs;
+
     if (mSurfaceControl != nullptr) {
         return captureSurfaceControl();
     }
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index a6a856c..eceb84e 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -48,6 +48,7 @@
 #include <media/stagefright/foundation/avc_utils.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/ACodec.h>
+#include <media/stagefright/BatteryChecker.h>
 #include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MediaCodecList.h>
@@ -207,12 +208,22 @@
     mService->addResource(mPid, mUid, clientId, client, resources);
 }
 
-void MediaCodec::ResourceManagerServiceProxy::removeResource(int64_t clientId) {
+void MediaCodec::ResourceManagerServiceProxy::removeResource(
+        int64_t clientId,
+        const Vector<MediaResource> &resources) {
     Mutex::Autolock _l(mLock);
     if (mService == NULL) {
         return;
     }
-    mService->removeResource(mPid, clientId);
+    mService->removeResource(mPid, clientId, resources);
+}
+
+void MediaCodec::ResourceManagerServiceProxy::removeClient(int64_t clientId) {
+    Mutex::Autolock _l(mLock);
+    if (mService == NULL) {
+        return;
+    }
+    mService->removeClient(mPid, clientId);
 }
 
 bool MediaCodec::ResourceManagerServiceProxy::reclaimResource(
@@ -517,7 +528,6 @@
       mStickyError(OK),
       mSoftRenderer(NULL),
       mAnalyticsItem(NULL),
-      mBatteryStatNotified(false),
       mIsVideo(false),
       mVideoWidth(0),
       mVideoHeight(0),
@@ -543,7 +553,7 @@
 
 MediaCodec::~MediaCodec() {
     CHECK_EQ(mState, UNINITIALIZED);
-    mResourceManagerService->removeResource(getId(mResourceManagerClient));
+    mResourceManagerService->removeClient(getId(mResourceManagerClient));
 
     flushAnalyticsItem();
 }
@@ -742,6 +752,12 @@
         return;
     }
 
+    if (mBatteryChecker != nullptr) {
+        mBatteryChecker->onCodecActivity([this] () {
+            addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
+        });
+    }
+
     const int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
     BufferFlightTiming_t startdata = { presentationUs, nowNs };
 
@@ -776,6 +792,12 @@
         return;
     }
 
+    if (mBatteryChecker != nullptr) {
+        mBatteryChecker->onCodecActivity([this] () {
+            addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
+        });
+    }
+
     BufferFlightTiming_t startdata;
     bool valid = false;
     while (mBuffersInFlight.size() > 0) {
@@ -964,6 +986,10 @@
         mAnalyticsItem->setCString(kCodecMode, mIsVideo ? kCodecModeVideo : kCodecModeAudio);
     }
 
+    if (mIsVideo) {
+        mBatteryChecker = new BatteryChecker(new AMessage(kWhatCheckBatteryStats, this));
+    }
+
     status_t err;
     Vector<MediaResource> resources;
     MediaResource::Type type =
@@ -1221,6 +1247,13 @@
             getId(mResourceManagerClient), mResourceManagerClient, resources);
 }
 
+void MediaCodec::removeResource(
+        MediaResource::Type type, MediaResource::SubType subtype, uint64_t value) {
+    Vector<MediaResource> resources;
+    resources.push_back(MediaResource(type, subtype, value));
+    mResourceManagerService->removeResource(getId(mResourceManagerClient), resources);
+}
+
 status_t MediaCodec::start() {
     sp<AMessage> msg = new AMessage(kWhatStart, this);
 
@@ -1682,6 +1715,59 @@
     }
 }
 
+BatteryChecker::BatteryChecker(const sp<AMessage> &msg, int64_t timeoutUs)
+    : mTimeoutUs(timeoutUs)
+    , mLastActivityTimeUs(-1ll)
+    , mBatteryStatNotified(false)
+    , mBatteryCheckerGeneration(0)
+    , mIsExecuting(false)
+    , mBatteryCheckerMsg(msg) {}
+
+void BatteryChecker::onCodecActivity(std::function<void()> batteryOnCb) {
+    if (!isExecuting()) {
+        // ignore if not executing
+        return;
+    }
+    if (!mBatteryStatNotified) {
+        batteryOnCb();
+        mBatteryStatNotified = true;
+        sp<AMessage> msg = mBatteryCheckerMsg->dup();
+        msg->setInt32("generation", mBatteryCheckerGeneration);
+
+        // post checker and clear last activity time
+        msg->post(mTimeoutUs);
+        mLastActivityTimeUs = -1ll;
+    } else {
+        // update last activity time
+        mLastActivityTimeUs = ALooper::GetNowUs();
+    }
+}
+
+void BatteryChecker::onCheckBatteryTimer(
+        const sp<AMessage> &msg, std::function<void()> batteryOffCb) {
+    // ignore if this checker already expired because the client resource was removed
+    int32_t generation;
+    if (!msg->findInt32("generation", &generation)
+            || generation != mBatteryCheckerGeneration) {
+        return;
+    }
+
+    if (mLastActivityTimeUs < 0ll) {
+        // timed out inactive, do not repost checker
+        batteryOffCb();
+        mBatteryStatNotified = false;
+    } else {
+        // repost checker and clear last activity time
+        msg->post(mTimeoutUs + mLastActivityTimeUs - ALooper::GetNowUs());
+        mLastActivityTimeUs = -1ll;
+    }
+}
+
+void BatteryChecker::onClientRemoved() {
+    mBatteryStatNotified = false;
+    mBatteryCheckerGeneration++;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 void MediaCodec::cancelPendingDequeueOperations() {
@@ -1977,11 +2063,6 @@
                     if (mIsVideo) {
                         // audio codec is currently ignored.
                         addResource(resourceType, MediaResource::kVideoCodec, 1);
-                        // TODO: track battery on/off by actual queueing/dequeueing
-                        // For now, keep existing behavior and request battery on/off
-                        // together with codec init/uninit. We'll improve the tracking
-                        // later by adding/removing this based on queue/dequeue timing.
-                        addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
                     }
 
                     (new AMessage)->postReply(mReplyID);
@@ -2323,7 +2404,12 @@
 
                     mFlags &= ~kFlagIsComponentAllocated;
 
-                    mResourceManagerService->removeResource(getId(mResourceManagerClient));
+                    // off since we're removing all resources including the battery on
+                    if (mBatteryChecker != nullptr) {
+                        mBatteryChecker->onClientRemoved();
+                    }
+
+                    mResourceManagerService->removeClient(getId(mResourceManagerClient));
 
                     (new AMessage)->postReply(mReplyID);
                     break;
@@ -3034,6 +3120,16 @@
             break;
         }
 
+        case kWhatCheckBatteryStats:
+        {
+            if (mBatteryChecker != nullptr) {
+                mBatteryChecker->onCheckBatteryTimer(msg, [this] () {
+                    removeResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
+                });
+            }
+            break;
+        }
+
         default:
             TRESPASS();
     }
@@ -3130,6 +3226,10 @@
 
     mState = newState;
 
+    if (mBatteryChecker != nullptr) {
+        mBatteryChecker->setExecuting(isExecuting());
+    }
+
     cancelPendingDequeueOperations();
 }
 
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index 96818eb..c1b270c 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -7,6 +7,9 @@
           "include-annotation": "android.platform.test.annotations.RequiresDevice"
         }
       ]
+    },
+    {
+       "name": "BatteryChecker_test"
     }
   ]
 }
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index cf91405..4711315 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -31,9 +31,14 @@
 
 namespace android {
 
-static int ALIGN(int x, int y) {
-    // y must be a power of 2.
-    return (x + y - 1) & ~(y - 1);
+inline void initDstYUV(
+        const android_ycbcr &ycbcr, int32_t cropTop, int32_t cropLeft,
+        uint8_t **dst_y, uint8_t **dst_u, uint8_t **dst_v) {
+    *dst_y = (uint8_t *)ycbcr.y + cropTop * ycbcr.ystride + cropLeft;
+
+    int32_t c_offset = (cropTop / 2) * ycbcr.cstride + cropLeft / 2;
+    *dst_v = (uint8_t *)ycbcr.cr + c_offset;
+    *dst_u = (uint8_t *)ycbcr.cb + c_offset;
 }
 
 SoftwareRenderer::SoftwareRenderer(
@@ -300,20 +305,14 @@
         const uint8_t *src_u = (const uint8_t *)data + mStride * mHeight + mCropTop * mStride / 4;
         const uint8_t *src_v = (const uint8_t *)src_u + mStride * mHeight / 4;
 
-        uint8_t *dst_y = (uint8_t *)ycbcr.y;
-        uint8_t *dst_v = (uint8_t *)ycbcr.cr;
-        uint8_t *dst_u = (uint8_t *)ycbcr.cb;
-        size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
-
-        dst_y += mCropTop * buf->stride + mCropLeft;
-        dst_v += (mCropTop/2) * dst_c_stride + mCropLeft/2;
-        dst_u += (mCropTop/2) * dst_c_stride + mCropLeft/2;
+        uint8_t *dst_y, *dst_u, *dst_v;
+        initDstYUV(ycbcr, mCropTop, mCropLeft, &dst_y, &dst_u, &dst_v);
 
         for (int y = 0; y < mCropHeight; ++y) {
             memcpy(dst_y, src_y, mCropWidth);
 
             src_y += mStride;
-            dst_y += buf->stride;
+            dst_y += ycbcr.ystride;
         }
 
         for (int y = 0; y < (mCropHeight + 1) / 2; ++y) {
@@ -322,22 +321,16 @@
 
             src_u += mStride / 2;
             src_v += mStride / 2;
-            dst_u += dst_c_stride;
-            dst_v += dst_c_stride;
+            dst_u += ycbcr.cstride;
+            dst_v += ycbcr.cstride;
         }
     } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar16) {
         const uint8_t *src_y = (const uint8_t *)data + mCropTop * mStride + mCropLeft * 2;
         const uint8_t *src_u = (const uint8_t *)data + mStride * mHeight + mCropTop * mStride / 4;
         const uint8_t *src_v = (const uint8_t *)src_u + mStride * mHeight / 4;
 
-        uint8_t *dst_y = (uint8_t *)ycbcr.y;
-        uint8_t *dst_v = (uint8_t *)ycbcr.cr;
-        uint8_t *dst_u = (uint8_t *)ycbcr.cb;
-        size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
-
-        dst_y += mCropTop * buf->stride + mCropLeft;
-        dst_v += (mCropTop / 2) * dst_c_stride + mCropLeft / 2;
-        dst_u += (mCropTop / 2) * dst_c_stride + mCropLeft / 2;
+        uint8_t *dst_y, *dst_u, *dst_v;
+        initDstYUV(ycbcr, mCropTop, mCropLeft, &dst_y, &dst_u, &dst_v);
 
         for (int y = 0; y < mCropHeight; ++y) {
             for (int x = 0; x < mCropWidth; ++x) {
@@ -345,7 +338,7 @@
             }
 
             src_y += mStride;
-            dst_y += buf->stride;
+            dst_y += ycbcr.ystride;
         }
 
         for (int y = 0; y < (mCropHeight + 1) / 2; ++y) {
@@ -356,8 +349,8 @@
 
             src_u += mStride / 2;
             src_v += mStride / 2;
-            dst_u += dst_c_stride;
-            dst_v += dst_c_stride;
+            dst_u += ycbcr.cstride;
+            dst_v += ycbcr.cstride;
         }
     } else if (mColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
             || mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
@@ -368,20 +361,14 @@
         src_y += mCropLeft + mCropTop * mWidth;
         src_uv += (mCropLeft + mCropTop * mWidth) / 2;
 
-        uint8_t *dst_y = (uint8_t *)ycbcr.y;
-        uint8_t *dst_v = (uint8_t *)ycbcr.cr;
-        uint8_t *dst_u = (uint8_t *)ycbcr.cb;
-        size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
-
-        dst_y += mCropTop * buf->stride + mCropLeft;
-        dst_v += (mCropTop/2) * dst_c_stride + mCropLeft/2;
-        dst_u += (mCropTop/2) * dst_c_stride + mCropLeft/2;
+        uint8_t *dst_y, *dst_u, *dst_v;
+        initDstYUV(ycbcr, mCropTop, mCropLeft, &dst_y, &dst_u, &dst_v);
 
         for (int y = 0; y < mCropHeight; ++y) {
             memcpy(dst_y, src_y, mCropWidth);
 
             src_y += mWidth;
-            dst_y += buf->stride;
+            dst_y += ycbcr.ystride;
         }
 
         for (int y = 0; y < (mCropHeight + 1) / 2; ++y) {
@@ -392,8 +379,8 @@
             }
 
             src_uv += mWidth;
-            dst_u += dst_c_stride;
-            dst_v += dst_c_stride;
+            dst_u += ycbcr.cstride;
+            dst_v += ycbcr.cstride;
         }
     } else if (mColorFormat == OMX_COLOR_Format24bitRGB888) {
         uint8_t* srcPtr = (uint8_t*)data + mWidth * mCropTop * 3 + mCropLeft * 3;
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index 1af6276..8e42fcf 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -139,6 +139,8 @@
     bool mIsAvcOrHevc;
     MediaSource::ReadOptions::SeekMode mSeekMode;
     int64_t mTargetTimeUs;
+    List<int64_t> mSampleDurations;
+    int64_t mDefaultSampleDurationUs;
 
     sp<Surface> initSurfaceControl();
     status_t captureSurfaceControl();
diff --git a/media/libstagefright/include/media/stagefright/BatteryChecker.h b/media/libstagefright/include/media/stagefright/BatteryChecker.h
new file mode 100644
index 0000000..2ec4ac0
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/BatteryChecker.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 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 BATTERY_CHECKER_H_
+#define BATTERY_CHECKER_H_
+
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+struct BatteryChecker : public RefBase {
+    BatteryChecker(const sp<AMessage> &msg, int64_t timeout = 3000000ll);
+
+    void setExecuting(bool executing) { mIsExecuting = executing; }
+    void onCodecActivity(std::function<void()> batteryOnCb);
+    void onCheckBatteryTimer(const sp<AMessage>& msg, std::function<void()> batteryOffCb);
+    void onClientRemoved();
+
+private:
+    const int64_t mTimeoutUs;
+    int64_t mLastActivityTimeUs;
+    bool mBatteryStatNotified;
+    int32_t mBatteryCheckerGeneration;
+    bool mIsExecuting;
+    sp<AMessage> mBatteryCheckerMsg;
+
+    bool isExecuting() { return mIsExecuting; }
+
+    DISALLOW_EVIL_CONSTRUCTORS(BatteryChecker);
+};
+
+}  // namespace android
+
+#endif // BATTERY_CHECKER_H_
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 0218a88..cd30347 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -36,6 +36,7 @@
 struct AMessage;
 struct AReplyToken;
 struct AString;
+struct BatteryChecker;
 class BufferChannelBase;
 struct CodecBase;
 class IBatteryStats;
@@ -257,6 +258,7 @@
         kWhatSetCallback                    = 'setC',
         kWhatSetNotification                = 'setN',
         kWhatDrmReleaseCrypto               = 'rDrm',
+        kWhatCheckBatteryStats              = 'chkB',
     };
 
     enum {
@@ -296,7 +298,11 @@
                 const sp<IResourceManagerClient> &client,
                 const Vector<MediaResource> &resources);
 
-        void removeResource(int64_t clientId);
+        void removeResource(
+                int64_t clientId,
+                const Vector<MediaResource> &resources);
+
+        void removeClient(int64_t clientId);
 
         bool reclaimResource(const Vector<MediaResource> &resources);
 
@@ -336,7 +342,6 @@
     sp<IResourceManagerClient> mResourceManagerClient;
     sp<ResourceManagerServiceProxy> mResourceManagerService;
 
-    bool mBatteryStatNotified;
     bool mIsVideo;
     int32_t mVideoWidth;
     int32_t mVideoHeight;
@@ -430,6 +435,7 @@
 
     uint64_t getGraphicBufferSize();
     void addResource(MediaResource::Type type, MediaResource::SubType subtype, uint64_t value);
+    void removeResource(MediaResource::Type type, MediaResource::SubType subtype, uint64_t value);
     void requestCpuBoostIfNeeded();
 
     bool hasPendingBuffer(int portIndex);
@@ -458,6 +464,8 @@
     Mutex mLatencyLock;
     int64_t mLatencyUnknown;    // buffers for which we couldn't calculate latency
 
+    sp<BatteryChecker> mBatteryChecker;
+
     void statsBufferSent(int64_t presentationUs);
     void statsBufferReceived(int64_t presentationUs);
 
diff --git a/media/libstagefright/tests/Android.bp b/media/libstagefright/tests/Android.bp
index be10fdc..a7f94c1 100644
--- a/media/libstagefright/tests/Android.bp
+++ b/media/libstagefright/tests/Android.bp
@@ -27,3 +27,21 @@
         "-Wall",
     ],
 }
+
+cc_test {
+    name: "BatteryChecker_test",
+    srcs: ["BatteryChecker_test.cpp"],
+    test_suites: ["device-tests"],
+
+    shared_libs: [
+        "libstagefright",
+        "libstagefright_foundation",
+        "libutils",
+        "liblog",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+}
\ No newline at end of file
diff --git a/media/libstagefright/tests/BatteryChecker_test.cpp b/media/libstagefright/tests/BatteryChecker_test.cpp
new file mode 100644
index 0000000..0c5ee9b
--- /dev/null
+++ b/media/libstagefright/tests/BatteryChecker_test.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "BatteryChecker_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include <media/stagefright/BatteryChecker.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AHandler.h>
+
+#include <vector>
+
+namespace android {
+
+static const int kBatteryTimeoutUs = 1000000ll; // 1 seconds
+static const int kTestMarginUs = 50000ll; // 50ms
+static const int kWaitStatusChangeUs = kBatteryTimeoutUs + kTestMarginUs;
+static const int kSparseFrameIntervalUs = kBatteryTimeoutUs - kTestMarginUs;
+
+class BatteryCheckerTestHandler : public AHandler {
+    enum EventType {
+        // Events simulating MediaCodec
+        kWhatStart = 0,             // codec entering executing state
+        kWhatStop,                  // codec exiting executing state
+        kWhatActivity,        // codec queue input or dequeue output
+        kWhatReleased,     // codec released
+        kWhatCheckpoint,        // test checkpoing with expected values on On/Off
+
+        // Message for battery checker monitor (not for testing through runTest())
+        kWhatBatteryChecker,
+    };
+
+    struct Operation {
+        int32_t event;
+        int64_t delay = 0;
+        uint32_t repeatCount = 0;
+        int32_t expectedOnCounter = 0;
+        int32_t expectedOffCounter = 0;
+    };
+
+    std::vector<Operation> mOps;
+    sp<BatteryChecker> mBatteryChecker;
+    int32_t mOnCounter;
+    int32_t mOffCounter;
+    Condition mDone;
+    Mutex mLock;
+
+    BatteryCheckerTestHandler() : mOnCounter(0), mOffCounter(0) {}
+
+    void runTest(const std::vector<Operation> &ops, int64_t timeoutUs) {
+        mOps = ops;
+
+        mBatteryChecker = new BatteryChecker(
+                new AMessage(kWhatBatteryChecker, this), kBatteryTimeoutUs);
+
+        (new AMessage(ops[0].event, this))->post();
+
+        // wait for done
+        AutoMutex lock(mLock);
+        EXPECT_NE(TIMED_OUT, mDone.waitRelative(mLock, timeoutUs * 1000ll));
+    }
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+    friend class BatteryCheckerTest;
+};
+
+class BatteryCheckerTest : public ::testing::Test {
+public:
+    BatteryCheckerTest()
+        : mLooper(new ALooper)
+        , mHandler(new BatteryCheckerTestHandler()) {
+        mLooper->setName("BatterCheckerLooper");
+        mLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+        mLooper->registerHandler(mHandler);
+    }
+
+protected:
+    using EventType = BatteryCheckerTestHandler::EventType;
+    using Operation = BatteryCheckerTestHandler::Operation;
+
+    virtual ~BatteryCheckerTest() {
+        mLooper->stop();
+        mLooper->unregisterHandler(mHandler->id());
+    }
+
+    void runTest(const std::vector<Operation> &ops, int64_t timeoutUs) {
+        mHandler->runTest(ops, timeoutUs);
+    }
+
+    sp<ALooper> mLooper;
+    sp<BatteryCheckerTestHandler> mHandler;
+};
+
+void BatteryCheckerTestHandler::onMessageReceived(const sp<AMessage> &msg) {
+    switch(msg->what()) {
+    case kWhatStart:
+        mBatteryChecker->setExecuting(true);
+        break;
+    case kWhatStop:
+        mBatteryChecker->setExecuting(false);
+        break;
+    case kWhatActivity:
+        mBatteryChecker->onCodecActivity([this] () { mOnCounter++; });
+        break;
+    case kWhatReleased:
+        mBatteryChecker->onClientRemoved();
+        break;
+    case kWhatBatteryChecker:
+        mBatteryChecker->onCheckBatteryTimer(msg, [this] () { mOffCounter++;  });
+        break;
+    case kWhatCheckpoint:
+        // verify ON/OFF state and total events
+        EXPECT_EQ(mOnCounter, mOps[0].expectedOnCounter);
+        EXPECT_EQ(mOffCounter, mOps[0].expectedOffCounter);
+        break;
+    default:
+        TRESPASS();
+    }
+    if (msg->what() != kWhatBatteryChecker) {
+        EXPECT_EQ(msg->what(), mOps[0].event);
+        // post next message
+        if (!mOps[0].repeatCount) {
+            mOps.erase(mOps.begin());
+        } else {
+            mOps[0].repeatCount--;
+        }
+        int64_t duration = mOps[0].delay;
+        if (!mOps.empty()) {
+            (new AMessage(mOps[0].event, this))->post(duration);
+        } else {
+            AutoMutex lock(mLock);
+            mDone.signal();
+        }
+    }
+}
+
+TEST_F(BatteryCheckerTest, testNormalOperations) {
+    runTest({
+        {EventType::kWhatStart,        0ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 0, 0},
+        {EventType::kWhatActivity,     33333ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 1, 0},                 // ON
+        {EventType::kWhatActivity,     33333ll, 2*kWaitStatusChangeUs/33333ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 1, 0},
+        {EventType::kWhatCheckpoint,   kWaitStatusChangeUs, 0, 1, 1}, // OFF
+    }, 10000000ll);
+}
+
+TEST_F(BatteryCheckerTest, testPauseResume) {
+    runTest({
+        {EventType::kWhatStart,        0ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 0, 0},
+        {EventType::kWhatActivity,     33333ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 1, 0},                 // ON
+        {EventType::kWhatCheckpoint,   kWaitStatusChangeUs, 0, 1, 1}, // OFF
+        {EventType::kWhatActivity,     33333ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 2, 1},                 // ON
+        {EventType::kWhatCheckpoint,   kWaitStatusChangeUs, 0, 2, 2}, // OFF
+    }, 10000000ll);
+}
+
+TEST_F(BatteryCheckerTest, testClientRemovedAndRestart) {
+    runTest({
+        {EventType::kWhatStart,        0ll},
+        {EventType::kWhatActivity,     33333ll, kWaitStatusChangeUs/33333ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 1, 0},
+
+        // stop executing state itself shouldn't trigger any calls
+        {EventType::kWhatStop,         0ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 1, 0},
+
+        // release shouldn't trigger any calls either,
+        // client resource will be removed entirely
+        {EventType::kWhatReleased,     0ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 1, 0},
+        {EventType::kWhatCheckpoint,   kWaitStatusChangeUs, 0, 1, 0},
+
+        // start pushing buffers again, On should be received without any Off
+        {EventType::kWhatStart,        0ll},
+        {EventType::kWhatActivity,     33333ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 2, 0},
+
+        // double check that only new checker msg triggers OFF,
+        // left-over checker msg from stale generate discarded
+        {EventType::kWhatCheckpoint,   kWaitStatusChangeUs, 0, 2, 1},
+    }, 10000000ll);
+}
+
+TEST_F(BatteryCheckerTest, testActivityWhileNotExecuting) {
+    runTest({
+        // activity before start shouldn't trigger
+        {EventType::kWhatActivity,     0ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 0, 0},
+
+        {EventType::kWhatStart,        0ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 0, 0},
+
+        // activity after start before stop should trigger
+        {EventType::kWhatActivity,     33333ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 1, 0},
+
+        // stop executing state itself shouldn't trigger any calls
+        {EventType::kWhatStop,         0ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 1, 0},
+
+        // keep pushing another 3 seconds after stop, expected to OFF
+        {EventType::kWhatActivity,     33333ll, kWaitStatusChangeUs/33333ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 1, 1},
+    }, 10000000ll);
+}
+
+TEST_F(BatteryCheckerTest, testSparseActivity) {
+    runTest({
+        {EventType::kWhatStart,        0ll},
+        {EventType::kWhatCheckpoint,   0ll, 0, 0, 0},
+
+        // activity arrives sparsely with interval only slightly small than timeout
+        // should only trigger 1 ON
+        {EventType::kWhatActivity,     kSparseFrameIntervalUs, 2},
+        {EventType::kWhatCheckpoint,   0ll, 0, 1, 0},
+        {EventType::kWhatCheckpoint,   kSparseFrameIntervalUs, 0, 1, 0},
+        {EventType::kWhatCheckpoint,   kTestMarginUs, 0, 1, 1}, // OFF
+    }, 10000000ll);
+}
+} // namespace android
diff --git a/media/libstagefright/xmlparser/api/current.txt b/media/libstagefright/xmlparser/api/current.txt
index 9d7c57d..16c8af8 100644
--- a/media/libstagefright/xmlparser/api/current.txt
+++ b/media/libstagefright/xmlparser/api/current.txt
@@ -138,7 +138,12 @@
 
   public class Variant {
     ctor public Variant();
+    method public java.util.List<media.codecs.Alias> getAlias_optional();
+    method public java.util.List<media.codecs.Quirk> getAttribute_optional();
+    method public java.util.List<media.codecs.Feature> getFeature_optional();
+    method public java.util.List<media.codecs.Limit> getLimit_optional();
     method public String getName();
+    method public java.util.List<media.codecs.Quirk> getQuirk_optional();
     method public void setName(String);
   }
 
diff --git a/media/libstagefright/xmlparser/media_codecs.xsd b/media/libstagefright/xmlparser/media_codecs.xsd
index 63ec5d0..3b5681f 100644
--- a/media/libstagefright/xmlparser/media_codecs.xsd
+++ b/media/libstagefright/xmlparser/media_codecs.xsd
@@ -107,6 +107,13 @@
         <xs:attribute name="value" type="xs:string"/>
     </xs:complexType>
     <xs:complexType name="Variant">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="Quirk" type="Quirk"/>
+            <xs:element name="Attribute" type="Quirk"/>
+            <xs:element name="Alias" type="Alias"/>
+            <xs:element name="Limit" type="Limit"/>
+            <xs:element name="Feature" type="Feature"/>
+        </xs:choice>
         <xs:attribute name="name" type="xs:string"/>
     </xs:complexType>
     <xs:complexType name="Setting">
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 355d945..b6da11e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1933,7 +1933,8 @@
                                                   &output.notificationFrameCount,
                                                   callingPid, clientUid, &output.flags,
                                                   input.clientInfo.clientTid,
-                                                  &lStatus, portId);
+                                                  &lStatus, portId,
+                                                  input.opPackageName);
         LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
 
         // lStatus == BAD_TYPE means FAST flag was rejected: request a new input from
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 08660dd..c8397cd 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -19,6 +19,39 @@
     #error This header file should only be included from AudioFlinger.h
 #endif
 
+// Checks and monitors OP_RECORD_AUDIO
+class OpRecordAudioMonitor : public RefBase {
+public:
+    ~OpRecordAudioMonitor() override;
+    bool hasOpRecordAudio() const;
+
+    static sp<OpRecordAudioMonitor> createIfNeeded(uid_t uid, const String16& opPackageName);
+
+private:
+    OpRecordAudioMonitor(uid_t uid, const String16& opPackageName);
+    void onFirstRef() override;
+
+    AppOpsManager mAppOpsManager;
+
+    class RecordAudioOpCallback : public BnAppOpsCallback {
+    public:
+        explicit RecordAudioOpCallback(const wp<OpRecordAudioMonitor>& monitor);
+        void opChanged(int32_t op, const String16& packageName) override;
+
+    private:
+        const wp<OpRecordAudioMonitor> mMonitor;
+    };
+
+    sp<RecordAudioOpCallback> mOpCallback;
+    // called by RecordAudioOpCallback when OP_RECORD_AUDIO is updated in AppOp callback
+    // and in onFirstRef()
+    void checkRecordAudio();
+
+    std::atomic_bool mHasOpRecordAudio;
+    const uid_t mUid;
+    const String16 mPackage;
+};
+
 // record track
 class RecordTrack : public TrackBase {
 public:
@@ -36,6 +69,7 @@
                                 uid_t uid,
                                 audio_input_flags_t flags,
                                 track_type type,
+                                const String16& opPackageName,
                                 audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE);
     virtual             ~RecordTrack();
     virtual status_t    initCheck() const;
@@ -68,7 +102,7 @@
                                 { return (mFlags & AUDIO_INPUT_FLAG_DIRECT) != 0; }
 
             void        setSilenced(bool silenced) { if (!isPatchTrack()) mSilenced = silenced; }
-            bool        isSilenced() const { return mSilenced; }
+            bool        isSilenced() const;
 
             status_t    getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
 
@@ -111,6 +145,11 @@
             audio_input_flags_t                mFlags;
 
             bool                               mSilenced;
+
+            // used to enforce OP_RECORD_AUDIO
+            uid_t                              mUid;
+            String16                           mOpPackageName;
+            sp<OpRecordAudioMonitor>           mOpRecordAudioMonitor;
 };
 
 // playback track, used by PatchPanel
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 392beb4..868739f 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -6363,7 +6363,9 @@
             }
         }
         // compute volume for this track
-        processVolume_l(track, last);
+        if (track->isReady()) {  // check ready to prevent premature start.
+            processVolume_l(track, last);
+        }
     }
 
     // make sure the pause/flush/resume sequence is executed in the right order.
@@ -7300,7 +7302,7 @@
                         // Sanitize before releasing if the track has no access to the source data
                         // An idle UID receives silence from non virtual devices until active
                         if (activeTrack->isSilenced()) {
-                            memset(activeTrack->mSink.raw, 0, framesOut * mFrameSize);
+                            memset(activeTrack->mSink.raw, 0, framesOut * activeTrack->frameSize());
                         }
                         activeTrack->releaseBuffer(&activeTrack->mSink);
                     }
@@ -7461,7 +7463,8 @@
         audio_input_flags_t *flags,
         pid_t tid,
         status_t *status,
-        audio_port_handle_t portId)
+        audio_port_handle_t portId,
+        const String16& opPackageName)
 {
     size_t frameCount = *pFrameCount;
     size_t notificationFrameCount = *pNotificationFrameCount;
@@ -7595,7 +7598,7 @@
         track = new RecordTrack(this, client, attr, sampleRate,
                       format, channelMask, frameCount,
                       nullptr /* buffer */, (size_t)0 /* bufferSize */, sessionId, creatorPid, uid,
-                      *flags, TrackBase::TYPE_DEFAULT, portId);
+                      *flags, TrackBase::TYPE_DEFAULT, opPackageName, portId);
 
         lStatus = track->initCheck();
         if (lStatus != NO_ERROR) {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index fc8aa13..87bebf3 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1553,7 +1553,8 @@
                     audio_input_flags_t *flags,
                     pid_t tid,
                     status_t *status /*non-NULL*/,
-                    audio_port_handle_t portId);
+                    audio_port_handle_t portId,
+                    const String16& opPackageName);
 
             status_t    start(RecordTrack* recordTrack,
                               AudioSystem::sync_event_t event,
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 8f720b5..7cf34c1 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -205,6 +205,16 @@
 protected:
     DISALLOW_COPY_AND_ASSIGN(TrackBase);
 
+    void releaseCblk() {
+        if (mCblk != nullptr) {
+            mCblk->~audio_track_cblk_t();   // destroy our shared-structure.
+            if (mClient == 0) {
+                free(mCblk);
+            }
+            mCblk = nullptr;
+        }
+    }
+
     // AudioBufferProvider interface
     virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0;
     virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
@@ -215,6 +225,8 @@
 
     uint32_t channelCount() const { return mChannelCount; }
 
+    size_t frameSize() const { return mFrameSize; }
+
     audio_channel_mask_t channelMask() const { return mChannelMask; }
 
     virtual uint32_t sampleRate() const { return mSampleRate; }
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 78db80c..07f0538 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -237,12 +237,7 @@
 {
     // delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
     mServerProxy.clear();
-    if (mCblk != NULL) {
-        mCblk->~audio_track_cblk_t();   // destroy our shared-structure.
-        if (mClient == 0) {
-            free(mCblk);
-        }
-    }
+    releaseCblk();
     mCblkMemory.clear();    // free the shared memory before releasing the heap it belongs to
     if (mClient != 0) {
         // Client destructor must run with AudioFlinger client mutex locked
@@ -442,7 +437,7 @@
     return mHasOpPlayAudio.load();
 }
 
-// Note this method is never called (and never to be) for audio server / root track
+// Note this method is never called (and never to be) for audio server / patch record track
 // - not called from constructor due to check on UID,
 // - not called from PlayAudioOpCallback because the callback is not installed in this case
 void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::checkPlayAudioForUsage()
@@ -549,6 +544,12 @@
         return;
     }
 
+    if (!thread->isTrackAllowed_l(channelMask, format, sessionId, uid)) {
+        ALOGE("%s(%d): no more tracks available", __func__, mId);
+        releaseCblk(); // this makes the track invalid.
+        return;
+    }
+
     if (sharedBuffer == 0) {
         mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
                 mFrameSize, !isExternalTrack(), sampleRate);
@@ -558,10 +559,6 @@
     }
     mServerProxy = mAudioTrackServerProxy;
 
-    if (!thread->isTrackAllowed_l(channelMask, format, sessionId, uid)) {
-        ALOGE("%s(%d): no more tracks available", __func__, mId);
-        return;
-    }
     // only allocate a fast track index if we were able to allocate a normal track name
     if (flags & AUDIO_OUTPUT_FLAG_FAST) {
         // FIXME: Not calling framesReadyIsCalledByMultipleThreads() exposes a potential
@@ -1883,6 +1880,105 @@
 // ----------------------------------------------------------------------------
 //      Record
 // ----------------------------------------------------------------------------
+
+
+// ----------------------------------------------------------------------------
+//      AppOp for audio recording
+// -------------------------------
+
+#undef LOG_TAG
+#define LOG_TAG "AF::OpRecordAudioMonitor"
+
+// static
+sp<AudioFlinger::RecordThread::OpRecordAudioMonitor>
+AudioFlinger::RecordThread::OpRecordAudioMonitor::createIfNeeded(
+            uid_t uid, const String16& opPackageName)
+{
+    if (isServiceUid(uid)) {
+        ALOGV("not silencing record for service uid:%d pack:%s",
+                uid, String8(opPackageName).string());
+        return nullptr;
+    }
+
+    if (opPackageName.size() == 0) {
+        Vector<String16> packages;
+        // no package name, happens with SL ES clients
+        // query package manager to find one
+        PermissionController permissionController;
+        permissionController.getPackagesForUid(uid, packages);
+        if (packages.isEmpty()) {
+            return nullptr;
+        } else {
+            ALOGV("using pack:%s for uid:%d", String8(packages[0]).string(), uid);
+            return new OpRecordAudioMonitor(uid, packages[0]);
+        }
+    }
+
+    return new OpRecordAudioMonitor(uid, opPackageName);
+}
+
+AudioFlinger::RecordThread::OpRecordAudioMonitor::OpRecordAudioMonitor(
+        uid_t uid, const String16& opPackageName)
+        : mHasOpRecordAudio(true), mUid(uid), mPackage(opPackageName)
+{
+}
+
+AudioFlinger::RecordThread::OpRecordAudioMonitor::~OpRecordAudioMonitor()
+{
+    if (mOpCallback != 0) {
+        mAppOpsManager.stopWatchingMode(mOpCallback);
+    }
+    mOpCallback.clear();
+}
+
+void AudioFlinger::RecordThread::OpRecordAudioMonitor::onFirstRef()
+{
+    checkRecordAudio();
+    mOpCallback = new RecordAudioOpCallback(this);
+    ALOGV("start watching OP_RECORD_AUDIO for pack:%s", String8(mPackage).string());
+    mAppOpsManager.startWatchingMode(AppOpsManager::OP_RECORD_AUDIO, mPackage, mOpCallback);
+}
+
+bool AudioFlinger::RecordThread::OpRecordAudioMonitor::hasOpRecordAudio() const {
+    return mHasOpRecordAudio.load();
+}
+
+// Called by RecordAudioOpCallback when OP_RECORD_AUDIO is updated in AppOp callback
+// and in onFirstRef()
+// Note this method is never called (and never to be) for audio server / root track
+// due to the UID in createIfNeeded(). As a result for those record track, it's:
+// - not called from constructor,
+// - not called from RecordAudioOpCallback because the callback is not installed in this case
+void AudioFlinger::RecordThread::OpRecordAudioMonitor::checkRecordAudio()
+{
+    const int32_t mode = mAppOpsManager.checkOp(AppOpsManager::OP_RECORD_AUDIO,
+            mUid, mPackage);
+    const bool hasIt =  (mode == AppOpsManager::MODE_ALLOWED);
+    // verbose logging only log when appOp changed
+    ALOGI_IF(hasIt != mHasOpRecordAudio.load(),
+            "OP_RECORD_AUDIO missing, %ssilencing record uid%d pack:%s",
+            hasIt ? "un" : "", mUid, String8(mPackage).string());
+    mHasOpRecordAudio.store(hasIt);
+}
+
+AudioFlinger::RecordThread::OpRecordAudioMonitor::RecordAudioOpCallback::RecordAudioOpCallback(
+        const wp<OpRecordAudioMonitor>& monitor) : mMonitor(monitor)
+{ }
+
+void AudioFlinger::RecordThread::OpRecordAudioMonitor::RecordAudioOpCallback::opChanged(int32_t op,
+            const String16& packageName) {
+    UNUSED(packageName);
+    if (op != AppOpsManager::OP_RECORD_AUDIO) {
+        return;
+    }
+    sp<OpRecordAudioMonitor> monitor = mMonitor.promote();
+    if (monitor != NULL) {
+        monitor->checkRecordAudio();
+    }
+}
+
+
+
 #undef LOG_TAG
 #define LOG_TAG "AF::RecordHandle"
 
@@ -1954,6 +2050,7 @@
             uid_t uid,
             audio_input_flags_t flags,
             track_type type,
+            const String16& opPackageName,
             audio_port_handle_t portId)
     :   TrackBase(thread, client, attr, sampleRate, format,
                   channelMask, frameCount, buffer, bufferSize, sessionId,
@@ -1967,7 +2064,8 @@
         mResamplerBufferProvider(NULL), // initialize in case of early constructor exit
         mRecordBufferConverter(NULL),
         mFlags(flags),
-        mSilenced(false)
+        mSilenced(false),
+        mOpRecordAudioMonitor(OpRecordAudioMonitor::createIfNeeded(uid, opPackageName))
 {
     if (mCblk == NULL) {
         return;
@@ -2218,6 +2316,14 @@
     mServerLatencyMs.store(latencyMs);
 }
 
+bool AudioFlinger::RecordThread::RecordTrack::isSilenced() const {
+    if (mSilenced) {
+        return true;
+    }
+    // The monitor is only created for record tracks that can be silenced.
+    return mOpRecordAudioMonitor ? !mOpRecordAudioMonitor->hasOpRecordAudio() : false;
+}
+
 status_t AudioFlinger::RecordThread::RecordTrack::getActiveMicrophones(
         std::vector<media::MicrophoneInfo>* activeMicrophones)
 {
@@ -2268,7 +2374,7 @@
                 audio_attributes_t{} /* currently unused for patch track */,
                 sampleRate, format, channelMask, frameCount,
                 buffer, bufferSize, AUDIO_SESSION_NONE, getpid(), AID_AUDIOSERVER,
-                flags, TYPE_PATCH),
+                flags, TYPE_PATCH, String16()),
         PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true),
                        *recordThread, timeout)
 {
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index a4868bf..9a91ea0 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -117,7 +117,12 @@
 
 // ----------------------------------------------------------------------------
 
+static const String16 sDumpPermission("android.permission.DUMP");
 static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA");
+static const String16 sCameraPermission("android.permission.CAMERA");
+static const String16 sSystemCameraPermission("android.permission.SYSTEM_CAMERA");
+static const String16
+        sCameraSendSystemEventsPermission("android.permission.CAMERA_SEND_SYSTEM_EVENTS");
 
 // Matches with PERCEPTIBLE_APP_ADJ in ProcessList.java
 static constexpr int32_t kVendorClientScore = 200;
@@ -239,7 +244,7 @@
     Mutex::Autolock lock(mStatusListenerLock);
 
     for (auto& i : mListenerList) {
-        i.second->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId});
+        i->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId});
     }
 }
 
@@ -514,6 +519,11 @@
                 "Camera subsystem is not available");;
     }
 
+    if (shouldRejectSystemCameraConnection(String8(cameraId))) {
+        return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera"
+                "characteristics for system only device %s: ", String8(cameraId).string());
+    }
+
     Status ret{};
 
     status_t res = mCameraProviderManager->getCameraCharacteristics(
@@ -527,9 +537,12 @@
     int callingPid = CameraThreadState::getCallingPid();
     int callingUid = CameraThreadState::getCallingUid();
     std::vector<int32_t> tagsRemoved;
-    // If it's not calling from cameraserver, check the permission.
+    // If it's not calling from cameraserver, check the permission only if
+    // android.permission.CAMERA is required. If android.permission.SYSTEM_CAMERA was needed,
+    // it would've already been checked in shouldRejectSystemCameraConnection.
     if ((callingPid != getpid()) &&
-            !checkPermission(String16("android.permission.CAMERA"), callingPid, callingUid)) {
+            (getSystemCameraKind(String8(cameraId)) != SystemCameraKind::SYSTEM_ONLY_CAMERA) &&
+            !checkPermission(sCameraPermission, callingPid, callingUid)) {
         res = cameraInfo->removePermissionEntries(
                 mCameraProviderManager->getProviderTagIdLocked(String8(cameraId).string()),
                 &tagsRemoved);
@@ -969,9 +982,18 @@
                 clientName8.string(), clientUid, clientPid);
     }
 
-    // If it's not calling from cameraserver, check the permission.
+    if (shouldRejectSystemCameraConnection(cameraId)) {
+        ALOGW("Attempting to connect to system-only camera id %s, connection rejected",
+                cameraId.c_str());
+        return STATUS_ERROR_FMT(ERROR_DISCONNECTED, "No camera device with ID \"%s\" is"
+                                "available", cameraId.string());
+    }
+    // If it's not calling from cameraserver, check the permission if the
+    // device isn't a system only camera (shouldRejectSystemCameraConnection already checks for
+    // android.permission.SYSTEM_CAMERA for system only camera devices).
     if (callingPid != getpid() &&
-            !checkPermission(String16("android.permission.CAMERA"), clientPid, clientUid)) {
+                (getSystemCameraKind(cameraId) != SystemCameraKind::SYSTEM_ONLY_CAMERA) &&
+                !checkPermission(sCameraPermission, clientPid, clientUid)) {
         ALOGE("Permission Denial: can't use the camera pid=%d, uid=%d", clientPid, clientUid);
         return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
                 "Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" without camera permission",
@@ -1324,18 +1346,66 @@
     return ret;
 }
 
-bool CameraService::shouldRejectHiddenCameraConnection(const String8 & cameraId) {
-    // If the thread serving this call is not a hwbinder thread and the caller
-    // isn't the cameraserver itself, and the camera id being requested is to be
-    // publically hidden, we should reject the connection.
-    if (!hardware::IPCThreadState::self()->isServingCall() &&
-            CameraThreadState::getCallingPid() != getpid() &&
-            mCameraProviderManager->isPublicallyHiddenSecureCamera(cameraId.c_str())) {
+static bool hasPermissionsForSystemCamera(int callingPid, int callingUid) {
+    return checkPermission(sSystemCameraPermission, callingPid, callingUid) &&
+            checkPermission(sCameraPermission, callingPid, callingUid);
+}
+
+bool CameraService::shouldSkipStatusUpdates(const String8& cameraId, bool isVendorListener,
+        int clientPid, int clientUid) const {
+    SystemCameraKind systemCameraKind = getSystemCameraKind(cameraId);
+    // If the client is not a vendor client, don't add listener if
+    //   a) the camera is a publicly hidden secure camera OR
+    //   b) the camera is a system only camera and the client doesn't
+    //      have android.permission.SYSTEM_CAMERA permissions.
+    if (!isVendorListener && (systemCameraKind == SystemCameraKind::HIDDEN_SECURE_CAMERA ||
+            (systemCameraKind == SystemCameraKind::SYSTEM_ONLY_CAMERA &&
+            !hasPermissionsForSystemCamera(clientPid, clientUid)))) {
         return true;
     }
     return false;
 }
 
+bool CameraService::shouldRejectSystemCameraConnection(const String8& cameraId) const {
+    // Rules for rejection:
+    // 1) If cameraserver tries to access this camera device, accept the
+    //    connection.
+    // 2) The camera device is a publicly hidden secure camera device AND some
+    //    component is trying to access it on a non-hwbinder thread (generally a non HAL client),
+    //    reject it.
+    // 3) if the camera device is advertised by the camera HAL as SYSTEM_ONLY
+    //    and the serving thread is a non hwbinder thread, the client must have
+    //    android.permission.SYSTEM_CAMERA permissions to connect.
+
+    int cPid = CameraThreadState::getCallingPid();
+    int cUid = CameraThreadState::getCallingUid();
+    SystemCameraKind systemCameraKind = getSystemCameraKind(cameraId);
+
+    // (1) Cameraserver trying to connect, accept.
+    if (CameraThreadState::getCallingPid() == getpid()) {
+        return false;
+    }
+    // (2)
+    if (!hardware::IPCThreadState::self()->isServingCall() &&
+            systemCameraKind == SystemCameraKind::HIDDEN_SECURE_CAMERA) {
+        ALOGW("Rejecting access to secure hidden camera %s", cameraId.c_str());
+        return true;
+    }
+    // (3) Here we only check for permissions if it is a system only camera device. This is since
+    //     getCameraCharacteristics() allows for calls to succeed (albeit after hiding some
+    //     characteristics) even if clients don't have android.permission.CAMERA. We do not want the
+    //     same behavior for system camera devices.
+    if (!hardware::IPCThreadState::self()->isServingCall() &&
+            systemCameraKind == SystemCameraKind::SYSTEM_ONLY_CAMERA &&
+            !hasPermissionsForSystemCamera(cPid, cUid)) {
+        ALOGW("Rejecting access to system only camera %s, inadequete permissions",
+                cameraId.c_str());
+        return true;
+    }
+
+    return false;
+}
+
 Status CameraService::connectDevice(
         const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
         const String16& cameraId,
@@ -1385,14 +1455,6 @@
             (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
             static_cast<int>(effectiveApiLevel));
 
-    if (shouldRejectHiddenCameraConnection(cameraId)) {
-        ALOGW("Attempting to connect to system-only camera id %s, connection rejected",
-              cameraId.c_str());
-        return STATUS_ERROR_FMT(ERROR_DISCONNECTED,
-                                "No camera device with ID \"%s\" currently available",
-                                cameraId.string());
-
-    }
     sp<CLIENT> client = nullptr;
     {
         // Acquire mServiceLock and prevent other clients from connecting
@@ -1668,8 +1730,7 @@
     if (pid != selfPid) {
         // Ensure we're being called by system_server, or similar process with
         // permissions to notify the camera service about system events
-        if (!checkCallingPermission(
-                String16("android.permission.CAMERA_SEND_SYSTEM_EVENTS"))) {
+        if (!checkCallingPermission(sCameraSendSystemEventsPermission)) {
             const int uid = CameraThreadState::getCallingUid();
             ALOGE("Permission Denial: cannot send updates to camera service about system"
                     " events from pid=%d, uid=%d", pid, uid);
@@ -1704,7 +1765,7 @@
     Mutex::Autolock lock(mStatusListenerLock);
 
     for (const auto& it : mListenerList) {
-        auto ret = it.second->getListener()->onCameraAccessPrioritiesChanged();
+        auto ret = it->getListener()->onCameraAccessPrioritiesChanged();
         if (!ret.isOk()) {
             ALOGE("%s: Failed to trigger permission callback: %d", __FUNCTION__,
                     ret.exceptionCode());
@@ -1720,8 +1781,7 @@
     if (pid != selfPid) {
         // Ensure we're being called by system_server, or similar process with
         // permissions to notify the camera service about system events
-        if (!checkCallingPermission(
-                String16("android.permission.CAMERA_SEND_SYSTEM_EVENTS"))) {
+        if (!checkCallingPermission(sCameraSendSystemEventsPermission)) {
             const int uid = CameraThreadState::getCallingUid();
             ALOGE("Permission Denial: cannot send updates to camera service about device"
                     " state changes from pid=%d, uid=%d", pid, uid);
@@ -1775,20 +1835,23 @@
         return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Null listener given to addListener");
     }
 
+    auto clientUid = CameraThreadState::getCallingUid();
+    auto clientPid = CameraThreadState::getCallingPid();
+
     Mutex::Autolock lock(mServiceLock);
 
     {
         Mutex::Autolock lock(mStatusListenerLock);
         for (const auto &it : mListenerList) {
-            if (IInterface::asBinder(it.second->getListener()) == IInterface::asBinder(listener)) {
+            if (IInterface::asBinder(it->getListener()) == IInterface::asBinder(listener)) {
                 ALOGW("%s: Tried to add listener %p which was already subscribed",
                       __FUNCTION__, listener.get());
                 return STATUS_ERROR(ERROR_ALREADY_EXISTS, "Listener already registered");
             }
         }
 
-        auto clientUid = CameraThreadState::getCallingUid();
-        sp<ServiceListener> serviceListener = new ServiceListener(this, listener, clientUid);
+        sp<ServiceListener> serviceListener =
+                new ServiceListener(this, listener, clientUid, clientPid, isVendorListener);
         auto ret = serviceListener->initialize();
         if (ret != NO_ERROR) {
             String8 msg = String8::format("Failed to initialize service listener: %s (%d)",
@@ -1796,7 +1859,10 @@
             ALOGE("%s: %s", __FUNCTION__, msg.string());
             return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
         }
-        mListenerList.emplace_back(isVendorListener, serviceListener);
+        // The listener still needs to be added to the list of listeners, regardless of what
+        // permissions the listener process has / whether it is a vendor listener. Since it might be
+        // eligible to listen to other camera ids.
+        mListenerList.emplace_back(serviceListener);
         mUidPolicy->registerMonitorUid(clientUid);
     }
 
@@ -1804,8 +1870,7 @@
     {
         Mutex::Autolock lock(mCameraStatesLock);
         for (auto& i : mCameraStates) {
-            if (!isVendorListener &&
-                mCameraProviderManager->isPublicallyHiddenSecureCamera(i.first.c_str())) {
+            if (shouldSkipStatusUpdates(i.first, isVendorListener, clientPid, clientUid)) {
                 ALOGV("Cannot add public listener for hidden system-only %s for pid %d",
                       i.first.c_str(), CameraThreadState::getCallingPid());
                 continue;
@@ -1844,9 +1909,9 @@
     {
         Mutex::Autolock lock(mStatusListenerLock);
         for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) {
-            if (IInterface::asBinder(it->second->getListener()) == IInterface::asBinder(listener)) {
-                mUidPolicy->unregisterMonitorUid(it->second->getListenerUid());
-                IInterface::asBinder(listener)->unlinkToDeath(it->second);
+            if (IInterface::asBinder((*it)->getListener()) == IInterface::asBinder(listener)) {
+                mUidPolicy->unregisterMonitorUid((*it)->getListenerUid());
+                IInterface::asBinder(listener)->unlinkToDeath(*it);
                 mListenerList.erase(it);
                 return Status::ok();
             }
@@ -3029,7 +3094,7 @@
 status_t CameraService::dump(int fd, const Vector<String16>& args) {
     ATRACE_CALL();
 
-    if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+    if (checkCallingPermission(sDumpPermission) == false) {
         dprintf(fd, "Permission Denial: can't dump CameraService from pid=%d, uid=%d\n",
                 CameraThreadState::getCallingPid(),
                 CameraThreadState::getCallingUid());
@@ -3261,13 +3326,13 @@
             Mutex::Autolock lock(mStatusListenerLock);
 
             for (auto& listener : mListenerList) {
-                if (!listener.first &&
-                    mCameraProviderManager->isPublicallyHiddenSecureCamera(cameraId.c_str())) {
+                if (shouldSkipStatusUpdates(cameraId, listener->isVendorListener(),
+                        listener->getListenerPid(), listener->getListenerUid())) {
                     ALOGV("Skipping camera discovery callback for system-only camera %s",
-                          cameraId.c_str());
+                            cameraId.c_str());
                     continue;
                 }
-                listener.second->getListener()->onStatusChanged(mapToInterface(status),
+                listener->getListener()->onStatusChanged(mapToInterface(status),
                         String16(cameraId));
             }
         });
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index cf93a41..67829dd 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -633,9 +633,20 @@
         sp<BasicClient>* client,
         std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial);
 
-    // Should an operation attempt on a cameraId be rejected, if the camera id is
-    // advertised as a publically hidden secure camera, by the camera HAL ?
-    bool shouldRejectHiddenCameraConnection(const String8 & cameraId);
+    // Should an operation attempt on a cameraId be rejected ? (this can happen
+    // under various conditions. For example if a camera device is advertised as
+    // system only or hidden secure camera, amongst possible others.
+    bool shouldRejectSystemCameraConnection(const String8 & cameraId) const;
+
+    // Should a device status update be skipped for a particular camera device ? (this can happen
+    // under various conditions. For example if a camera device is advertised as
+    // system only or hidden secure camera, amongst possible others.
+    bool shouldSkipStatusUpdates(const String8& cameraId, bool isVendorListener, int clientPid,
+            int clientUid) const;
+
+    inline SystemCameraKind getSystemCameraKind(const String8& cameraId) const {
+        return mCameraProviderManager->getSystemCameraKind(cameraId.c_str());
+    }
 
     // Single implementation shared between the various connect calls
     template<class CALLBACK, class CLIENT>
@@ -810,7 +821,9 @@
     class ServiceListener : public virtual IBinder::DeathRecipient {
         public:
             ServiceListener(sp<CameraService> parent, sp<hardware::ICameraServiceListener> listener,
-                    int uid) : mParent(parent), mListener(listener), mListenerUid(uid) {}
+                    int uid, int pid, bool isVendorClient)
+                    : mParent(parent), mListener(listener), mListenerUid(uid), mListenerPid(pid),
+                      mIsVendorListener(isVendorClient) { }
 
             status_t initialize() {
                 return IInterface::asBinder(mListener)->linkToDeath(this);
@@ -824,16 +837,20 @@
             }
 
             int getListenerUid() { return mListenerUid; }
+            int getListenerPid() { return mListenerPid; }
             sp<hardware::ICameraServiceListener> getListener() { return mListener; }
+            bool isVendorListener() { return mIsVendorListener; }
 
         private:
             wp<CameraService> mParent;
             sp<hardware::ICameraServiceListener> mListener;
-            int mListenerUid;
+            int mListenerUid = -1;
+            int mListenerPid = -1;
+            bool mIsVendorListener = false;
     };
 
     // Guarded by mStatusListenerMutex
-    std::vector<std::pair<bool, sp<ServiceListener>>> mListenerList;
+    std::vector<sp<ServiceListener>> mListenerList;
 
     Mutex       mStatusListenerLock;
 
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index c72029f..c21bd69 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -534,15 +534,23 @@
     }
 }
 
-bool CameraProviderManager::ProviderInfo::DeviceInfo3::isPublicallyHiddenSecureCamera() {
+SystemCameraKind CameraProviderManager::ProviderInfo::DeviceInfo3::getSystemCameraKind() {
     camera_metadata_entry_t entryCap;
     entryCap = mCameraCharacteristics.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
-    if (entryCap.count != 1) {
-        // Do NOT hide this camera device if the capabilities specify anything more
-        // than ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA.
-        return false;
+    if (entryCap.count == 1 &&
+            entryCap.data.u8[0] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA) {
+        return SystemCameraKind::HIDDEN_SECURE_CAMERA;
     }
-    return entryCap.data.u8[0] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA;
+
+    // Go through the capabilities and check if it has
+    // ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA
+    for (size_t i = 0; i < entryCap.count; ++i) {
+        uint8_t capability = entryCap.data.u8[i];
+        if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA) {
+            return SystemCameraKind::SYSTEM_ONLY_CAMERA;
+        }
+    }
+    return SystemCameraKind::PUBLIC;
 }
 
 void CameraProviderManager::ProviderInfo::DeviceInfo3::getSupportedSizes(
@@ -1046,14 +1054,14 @@
     return deviceInfo->mIsLogicalCamera;
 }
 
-bool CameraProviderManager::isPublicallyHiddenSecureCamera(const std::string& id) {
+SystemCameraKind CameraProviderManager::getSystemCameraKind(const std::string& id) {
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
 
     auto deviceInfo = findDeviceInfoLocked(id);
     if (deviceInfo == nullptr) {
-        return false;
+        return SystemCameraKind::PUBLIC;
     }
-    return deviceInfo->mIsPublicallyHiddenSecureCamera;
+    return deviceInfo->mSystemCameraKind;
 }
 
 bool CameraProviderManager::isHiddenPhysicalCamera(const std::string& cameraId) {
@@ -1937,7 +1945,7 @@
         return;
     }
 
-    mIsPublicallyHiddenSecureCamera = isPublicallyHiddenSecureCamera();
+    mSystemCameraKind = getSystemCameraKind();
 
     status_t res = fixupMonochromeTags();
     if (OK != res) {
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 8cdfc24..801e978 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -54,6 +54,26 @@
             sp<VendorTagDescriptor>& descriptor);
 };
 
+enum SystemCameraKind {
+   /**
+    * These camera devices are visible to all apps and system components alike
+    */
+   PUBLIC = 0,
+
+   /**
+    * These camera devices are visible only to processes having the
+    * android.permission.SYSTEM_CAMERA permission. They are not exposed to 3P
+    * apps.
+    */
+   SYSTEM_ONLY_CAMERA,
+
+   /**
+    * These camera devices are visible only to HAL clients (that try to connect
+    * on a hwbinder thread).
+    */
+   HIDDEN_SECURE_CAMERA
+};
+
 /**
  * A manager for all camera providers available on an Android device.
  *
@@ -272,7 +292,7 @@
      */
     bool isLogicalCamera(const std::string& id, std::vector<std::string>* physicalCameraIds);
 
-    bool isPublicallyHiddenSecureCamera(const std::string& id);
+    SystemCameraKind getSystemCameraKind(const std::string& id);
     bool isHiddenPhysicalCamera(const std::string& cameraId);
 
     static const float kDepthARTolerance;
@@ -379,7 +399,7 @@
             std::vector<std::string> mPhysicalIds;
             hardware::CameraInfo mInfo;
             sp<IBase> mSavedInterface;
-            bool mIsPublicallyHiddenSecureCamera = false;
+            SystemCameraKind mSystemCameraKind = SystemCameraKind::PUBLIC;
 
             const hardware::camera::common::V1_0::CameraResourceCost mResourceCost;
 
@@ -497,7 +517,7 @@
             CameraMetadata mCameraCharacteristics;
             std::unordered_map<std::string, CameraMetadata> mPhysicalCameraCharacteristics;
             void queryPhysicalCameraIds();
-            bool isPublicallyHiddenSecureCamera();
+            SystemCameraKind getSystemCameraKind();
             status_t fixupMonochromeTags();
             status_t addDynamicDepthTags();
             static void getSupportedSizes(const CameraMetadata& ch, uint32_t tag,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 4227a3b..dd5a62b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -3679,7 +3679,7 @@
         // Did we get the (final) result metadata for this capture?
         if (result->result != NULL && !isPartialResult) {
             if (request.physicalCameraIds.size() != result->num_physcam_metadata) {
-                SET_ERR("Requested physical Camera Ids %d not equal to number of metadata %d",
+                SET_ERR("Expected physical Camera metadata count %d not equal to actual count %d",
                         request.physicalCameraIds.size(), result->num_physcam_metadata);
                 return;
             }
@@ -3873,12 +3873,14 @@
                             errorCode) {
                         if (physicalCameraId.size() > 0) {
                             String8 cameraId(physicalCameraId);
-                            if (r.physicalCameraIds.find(cameraId) == r.physicalCameraIds.end()) {
+                            auto iter = r.physicalCameraIds.find(cameraId);
+                            if (iter == r.physicalCameraIds.end()) {
                                 ALOGE("%s: Reported result failure for physical camera device: %s "
                                         " which is not part of the respective request!",
                                         __FUNCTION__, cameraId.string());
                                 break;
                             }
+                            r.physicalCameraIds.erase(iter);
                             resultExtras.errorPhysicalCameraId = physicalCameraId;
                         } else {
                             logicalDeviceResultError = true;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index fd9b4b0..f707ef8 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -70,7 +70,7 @@
     mFormatOverridden(false),
     mOriginalFormat(format),
     mDataSpaceOverridden(false),
-    mOriginalDataSpace(HAL_DATASPACE_UNKNOWN),
+    mOriginalDataSpace(dataSpace),
     mPhysicalCameraId(physicalCameraId),
     mLastTimestamp(0) {
 
@@ -137,9 +137,6 @@
 
 void Camera3Stream::setDataSpaceOverride(bool dataSpaceOverridden) {
     mDataSpaceOverridden = dataSpaceOverridden;
-    if (dataSpaceOverridden && mOriginalDataSpace == HAL_DATASPACE_UNKNOWN) {
-        mOriginalDataSpace = camera3_stream::data_space;
-    }
 }
 
 bool Camera3Stream::isDataSpaceOverridden() const {
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 67afd0f..805df82 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -588,7 +588,7 @@
 
     //Keep track of original dataSpace in case it gets overridden
     bool mDataSpaceOverridden;
-    android_dataspace mOriginalDataSpace;
+    const android_dataspace mOriginalDataSpace;
 
     String8 mPhysicalCameraId;
     nsecs_t mLastTimestamp;
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 0e7edfd..988c06b 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -169,6 +169,7 @@
     ALOGV("caller has uid=%d, embedded uid=%d", uid, uid_given);
 
     switch (uid)  {
+        case AID_DRM:
         case AID_MEDIA:
         case AID_MEDIA_CODEC:
         case AID_MEDIA_EX:
diff --git a/services/mediaanalytics/iface_statsd.cpp b/services/mediaanalytics/iface_statsd.cpp
index 6845f06..6fd4415 100644
--- a/services/mediaanalytics/iface_statsd.cpp
+++ b/services/mediaanalytics/iface_statsd.cpp
@@ -60,6 +60,7 @@
     { "audiotrack", statsd_audiotrack },
     { "codec", statsd_codec},
     { "drm.vendor.Google.WidevineCDM", statsd_widevineCDM },
+    { "drmmanager", statsd_drmmanager },
     { "extractor", statsd_extractor },
     { "mediadrm", statsd_mediadrm },
     { "nuplayer", statsd_nuplayer },
diff --git a/services/mediaanalytics/iface_statsd.h b/services/mediaanalytics/iface_statsd.h
index f85d303..014929b 100644
--- a/services/mediaanalytics/iface_statsd.h
+++ b/services/mediaanalytics/iface_statsd.h
@@ -30,5 +30,6 @@
 
 extern bool statsd_mediadrm(MediaAnalyticsItem *);
 extern bool statsd_widevineCDM(MediaAnalyticsItem *);
+extern bool statsd_drmmanager(MediaAnalyticsItem *);
 
 } // namespace android
diff --git a/services/mediaanalytics/statsd_drm.cpp b/services/mediaanalytics/statsd_drm.cpp
index 902483a..845383d 100644
--- a/services/mediaanalytics/statsd_drm.cpp
+++ b/services/mediaanalytics/statsd_drm.cpp
@@ -104,4 +104,38 @@
     return true;
 }
 
+// drmmanager
+bool statsd_drmmanager(MediaAnalyticsItem *item)
+{
+    if (item == NULL) return false;
+
+    nsecs_t timestamp = item->getTimestamp();
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+    int64_t mediaApexVersion = 0;
+
+    char *plugin_id = NULL;
+    (void) item->getCString("plugin_id", &plugin_id);
+    char *description = NULL;
+    (void) item->getCString("description", &description);
+    int32_t method_id = -1;
+    (void) item->getInt32("method_id", &method_id);
+    char *mime_types = NULL;
+    (void) item->getCString("mime_types", &mime_types);
+
+    if (enabled_statsd) {
+        android::util::stats_write(android::util::MEDIAMETRICS_DRMMANAGER_REPORTED,
+                                   timestamp, pkgName.c_str(), pkgVersionCode,
+                                   mediaApexVersion,
+                                   plugin_id, description,
+                                   method_id, mime_types);
+    } else {
+        ALOGV("NOT sending: drmmanager data");
+    }
+
+    free(plugin_id);
+    free(description);
+    free(mime_types);
+    return true;
+}
 } // namespace android
diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp
index e3893e5..fa5bc4a 100644
--- a/services/mediacodec/registrant/Android.bp
+++ b/services/mediacodec/registrant/Android.bp
@@ -42,8 +42,8 @@
         "libcodec2_soft_opusenc",
         "libcodec2_soft_vp8dec",
         "libcodec2_soft_vp9dec",
-        "libcodec2_soft_av1dec",
-        "libcodec2_soft_gav1dec",
+        // "libcodec2_soft_av1dec_aom",  // replaced by the gav1 implementation
+        "libcodec2_soft_av1dec_gav1",
         "libcodec2_soft_vp8enc",
         "libcodec2_soft_vp9enc",
         "libcodec2_soft_rawdec",
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 117a211..bdcd5e4 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -71,9 +71,9 @@
     return itemsStr;
 }
 
-static bool hasResourceType(MediaResource::Type type, const Vector<MediaResource>& resources) {
-    for (size_t i = 0; i < resources.size(); ++i) {
-        if (resources[i].mType == type) {
+static bool hasResourceType(MediaResource::Type type, const ResourceList& resources) {
+    for (auto it = resources.begin(); it != resources.end(); it++) {
+        if (it->second.mType == type) {
             return true;
         }
     }
@@ -107,19 +107,18 @@
         int64_t clientId,
         const sp<IResourceManagerClient>& client,
         ResourceInfos& infos) {
-    for (size_t i = 0; i < infos.size(); ++i) {
-        if (infos[i].clientId == clientId) {
-            return infos.editItemAt(i);
-        }
+    ssize_t index = infos.indexOfKey(clientId);
+
+    if (index < 0) {
+        ResourceInfo info;
+        info.uid = uid;
+        info.clientId = clientId;
+        info.client = client;
+
+        index = infos.add(clientId, info);
     }
-    ResourceInfo info;
-    info.uid = uid;
-    info.clientId = clientId;
-    info.client = client;
-    info.cpuBoost = false;
-    info.batteryNoted = false;
-    infos.push_back(info);
-    return infos.editItemAt(infos.size() - 1);
+
+    return infos.editValueAt(index);
 }
 
 static void notifyResourceGranted(int pid, const Vector<MediaResource> &resources) {
@@ -186,10 +185,10 @@
             snprintf(buffer, SIZE, "        Name: %s\n", infos[j].client->getName().string());
             result.append(buffer);
 
-            Vector<MediaResource> resources = infos[j].resources;
+            const ResourceList &resources = infos[j].resources;
             result.append("        Resources:\n");
-            for (size_t k = 0; k < resources.size(); ++k) {
-                snprintf(buffer, SIZE, "          %s\n", resources[k].toString().string());
+            for (auto it = resources.begin(); it != resources.end(); it++) {
+                snprintf(buffer, SIZE, "          %s\n", it->second.toString().string());
                 result.append(buffer);
             }
         }
@@ -201,16 +200,44 @@
     return OK;
 }
 
-ResourceManagerService::ResourceManagerService()
-    : ResourceManagerService(new ProcessInfo()) {}
+struct SystemCallbackImpl :
+        public ResourceManagerService::SystemCallbackInterface {
+    SystemCallbackImpl() {}
 
-ResourceManagerService::ResourceManagerService(sp<ProcessInfoInterface> processInfo)
+    virtual void noteStartVideo(int uid) override {
+        BatteryNotifier::getInstance().noteStartVideo(uid);
+    }
+    virtual void noteStopVideo(int uid) override {
+        BatteryNotifier::getInstance().noteStopVideo(uid);
+    }
+    virtual void noteResetVideo() override {
+        BatteryNotifier::getInstance().noteResetVideo();
+    }
+    virtual bool requestCpusetBoost(
+            bool enable, const sp<IInterface> &client) override {
+        return android::requestCpusetBoost(enable, client);
+    }
+
+protected:
+    virtual ~SystemCallbackImpl() {}
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(SystemCallbackImpl);
+};
+
+ResourceManagerService::ResourceManagerService()
+    : ResourceManagerService(new ProcessInfo(), new SystemCallbackImpl()) {}
+
+ResourceManagerService::ResourceManagerService(
+        const sp<ProcessInfoInterface> &processInfo,
+        const sp<SystemCallbackInterface> &systemResource)
     : mProcessInfo(processInfo),
+      mSystemCB(systemResource),
       mServiceLog(new ServiceLog()),
       mSupportsMultipleSecureCodecs(true),
       mSupportsSecureWithNonSecureCodec(true),
       mCpuBoostCount(0) {
-    BatteryNotifier::getInstance().noteResetVideo();
+    mSystemCB->noteResetVideo();
 }
 
 ResourceManagerService::~ResourceManagerService() {}
@@ -231,6 +258,38 @@
     }
 }
 
+void ResourceManagerService::onFirstAdded(
+        const MediaResource& resource, const ResourceInfo& clientInfo) {
+    // first time added
+    if (resource.mType == MediaResource::kCpuBoost
+     && resource.mSubType == MediaResource::kUnspecifiedSubType) {
+        // Request it on every new instance of kCpuBoost, as the media.codec
+        // could have died, if we only do it the first time subsequent instances
+        // never gets the boost.
+        if (mSystemCB->requestCpusetBoost(true, this) != OK) {
+            ALOGW("couldn't request cpuset boost");
+        }
+        mCpuBoostCount++;
+    } else if (resource.mType == MediaResource::kBattery
+            && resource.mSubType == MediaResource::kVideoCodec) {
+        mSystemCB->noteStartVideo(clientInfo.uid);
+    }
+}
+
+void ResourceManagerService::onLastRemoved(
+        const MediaResource& resource, const ResourceInfo& clientInfo) {
+    if (resource.mType == MediaResource::kCpuBoost
+            && resource.mSubType == MediaResource::kUnspecifiedSubType
+            && mCpuBoostCount > 0) {
+        if (--mCpuBoostCount == 0) {
+            mSystemCB->requestCpusetBoost(false, this);
+        }
+    } else if (resource.mType == MediaResource::kBattery
+            && resource.mSubType == MediaResource::kVideoCodec) {
+        mSystemCB->noteStopVideo(clientInfo.uid);
+    }
+}
+
 void ResourceManagerService::addResource(
         int pid,
         int uid,
@@ -248,24 +307,14 @@
     }
     ResourceInfos& infos = getResourceInfosForEdit(pid, mMap);
     ResourceInfo& info = getResourceInfoForEdit(uid, clientId, client, infos);
-    // TODO: do the merge instead of append.
-    info.resources.appendVector(resources);
 
     for (size_t i = 0; i < resources.size(); ++i) {
-        if (resources[i].mType == MediaResource::kCpuBoost && !info.cpuBoost) {
-            info.cpuBoost = true;
-            // Request it on every new instance of kCpuBoost, as the media.codec
-            // could have died, if we only do it the first time subsequent instances
-            // never gets the boost.
-            if (requestCpusetBoost(true, this) != OK) {
-                ALOGW("couldn't request cpuset boost");
-            }
-            mCpuBoostCount++;
-        } else if (resources[i].mType == MediaResource::kBattery
-                && resources[i].mSubType == MediaResource::kVideoCodec
-                && !info.batteryNoted) {
-            info.batteryNoted = true;
-            BatteryNotifier::getInstance().noteStartVideo(info.uid);
+        const auto resType = std::make_pair(resources[i].mType, resources[i].mSubType);
+        if (info.resources.find(resType) == info.resources.end()) {
+            onFirstAdded(resources[i], info);
+            info.resources[resType] = resources[i];
+        } else {
+            info.resources[resType].mValue += resources[i].mValue;
         }
     }
     if (info.deathNotifier == nullptr) {
@@ -275,7 +324,48 @@
     notifyResourceGranted(pid, resources);
 }
 
-void ResourceManagerService::removeResource(int pid, int64_t clientId) {
+void ResourceManagerService::removeResource(int pid, int64_t clientId,
+        const Vector<MediaResource> &resources) {
+    String8 log = String8::format("removeResource(pid %d, clientId %lld, resources %s)",
+            pid, (long long) clientId, getString(resources).string());
+    mServiceLog->add(log);
+
+    Mutex::Autolock lock(mLock);
+    if (!mProcessInfo->isValidPid(pid)) {
+        ALOGE("Rejected removeResource call with invalid pid.");
+        return;
+    }
+    ssize_t index = mMap.indexOfKey(pid);
+    if (index < 0) {
+        ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId);
+        return;
+    }
+    ResourceInfos &infos = mMap.editValueAt(index);
+
+    index = infos.indexOfKey(clientId);
+    if (index < 0) {
+        ALOGV("removeResource: didn't find clientId %lld", (long long) clientId);
+        return;
+    }
+
+    ResourceInfo &info = infos.editValueAt(index);
+
+    for (size_t i = 0; i < resources.size(); ++i) {
+        const auto resType = std::make_pair(resources[i].mType, resources[i].mSubType);
+        // ignore if we don't have it
+        if (info.resources.find(resType) != info.resources.end()) {
+            MediaResource &resource = info.resources[resType];
+            if (resource.mValue > resources[i].mValue) {
+                resource.mValue -= resources[i].mValue;
+            } else {
+                onLastRemoved(resources[i], info);
+                info.resources.erase(resType);
+            }
+        }
+    }
+}
+
+void ResourceManagerService::removeClient(int pid, int64_t clientId) {
     removeResource(pid, clientId, true);
 }
 
@@ -295,27 +385,22 @@
         ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId);
         return;
     }
-    bool found = false;
     ResourceInfos &infos = mMap.editValueAt(index);
-    for (size_t j = 0; j < infos.size(); ++j) {
-        if (infos[j].clientId == clientId) {
-            if (infos[j].cpuBoost && mCpuBoostCount > 0) {
-                if (--mCpuBoostCount == 0) {
-                    requestCpusetBoost(false, this);
-                }
-            }
-            if (infos[j].batteryNoted) {
-                BatteryNotifier::getInstance().noteStopVideo(infos[j].uid);
-            }
-            IInterface::asBinder(infos[j].client)->unlinkToDeath(infos[j].deathNotifier);
-            j = infos.removeAt(j);
-            found = true;
-            break;
-        }
+
+    index = infos.indexOfKey(clientId);
+    if (index < 0) {
+        ALOGV("removeResource: didn't find clientId %lld", (long long) clientId);
+        return;
     }
-    if (!found) {
-        ALOGV("didn't find client");
+
+    const ResourceInfo &info = infos[index];
+    for (auto it = info.resources.begin(); it != info.resources.end(); it++) {
+        onLastRemoved(it->second, info);
     }
+
+    IInterface::asBinder(info.client)->unlinkToDeath(info.deathNotifier);
+
+    infos.removeItemsAt(index);
 }
 
 void ResourceManagerService::getClientForResource_l(
@@ -426,7 +511,7 @@
             ResourceInfos &infos = mMap.editValueAt(i);
             for (size_t j = 0; j < infos.size();) {
                 if (infos[j].client == failedClient) {
-                    j = infos.removeAt(j);
+                    j = infos.removeItemsAt(j);
                     found = true;
                 } else {
                     ++j;
@@ -554,11 +639,12 @@
     uint64_t largestValue = 0;
     const ResourceInfos &infos = mMap.valueAt(index);
     for (size_t i = 0; i < infos.size(); ++i) {
-        Vector<MediaResource> resources = infos[i].resources;
-        for (size_t j = 0; j < resources.size(); ++j) {
-            if (resources[j].mType == type) {
-                if (resources[j].mValue > largestValue) {
-                    largestValue = resources[j].mValue;
+        const ResourceList &resources = infos[i].resources;
+        for (auto it = resources.begin(); it != resources.end(); it++) {
+            const MediaResource &resource = it->second;
+            if (resource.mType == type) {
+                if (resource.mValue > largestValue) {
+                    largestValue = resource.mValue;
                     clientTemp = infos[i].client;
                 }
             }
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index 741ef73..f086dc3 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -33,17 +33,17 @@
 class ServiceLog;
 struct ProcessInfoInterface;
 
+typedef std::map<std::pair<MediaResource::Type, MediaResource::SubType>, MediaResource> ResourceList;
 struct ResourceInfo {
     int64_t clientId;
     uid_t uid;
     sp<IResourceManagerClient> client;
     sp<IBinder::DeathRecipient> deathNotifier;
-    Vector<MediaResource> resources;
-    bool cpuBoost;
-    bool batteryNoted;
+    ResourceList resources;
 };
 
-typedef Vector<ResourceInfo> ResourceInfos;
+// TODO: convert these to std::map
+typedef KeyedVector<int64_t, ResourceInfo> ResourceInfos;
 typedef KeyedVector<int, ResourceInfos> PidResourceInfosMap;
 
 class ResourceManagerService
@@ -51,12 +51,22 @@
       public BnResourceManagerService
 {
 public:
+    struct SystemCallbackInterface : public RefBase {
+        virtual void noteStartVideo(int uid) = 0;
+        virtual void noteStopVideo(int uid) = 0;
+        virtual void noteResetVideo() = 0;
+        virtual bool requestCpusetBoost(
+                bool enable, const sp<IInterface> &client) = 0;
+    };
+
     static char const *getServiceName() { return "media.resource_manager"; }
 
     virtual status_t dump(int fd, const Vector<String16>& args);
 
     ResourceManagerService();
-    explicit ResourceManagerService(sp<ProcessInfoInterface> processInfo);
+    explicit ResourceManagerService(
+            const sp<ProcessInfoInterface> &processInfo,
+            const sp<SystemCallbackInterface> &systemResource);
 
     // IResourceManagerService interface
     virtual void config(const Vector<MediaResourcePolicy> &policies);
@@ -68,7 +78,10 @@
             const sp<IResourceManagerClient> client,
             const Vector<MediaResource> &resources);
 
-    virtual void removeResource(int pid, int64_t clientId);
+    virtual void removeResource(int pid, int64_t clientId,
+            const Vector<MediaResource> &resources);
+
+    virtual void removeClient(int pid, int64_t clientId);
 
     // Tries to reclaim resource from processes with lower priority than the calling process
     // according to the requested resources.
@@ -110,8 +123,12 @@
     void getClientForResource_l(
         int callingPid, const MediaResource *res, Vector<sp<IResourceManagerClient>> *clients);
 
+    void onFirstAdded(const MediaResource& res, const ResourceInfo& clientInfo);
+    void onLastRemoved(const MediaResource& res, const ResourceInfo& clientInfo);
+
     mutable Mutex mLock;
     sp<ProcessInfoInterface> mProcessInfo;
+    sp<SystemCallbackInterface> mSystemCB;
     sp<ServiceLog> mServiceLog;
     PidResourceInfosMap mMap;
     bool mSupportsMultipleSecureCodecs;
diff --git a/services/mediaresourcemanager/TEST_MAPPING b/services/mediaresourcemanager/TEST_MAPPING
new file mode 100644
index 0000000..418b159
--- /dev/null
+++ b/services/mediaresourcemanager/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+       "name": "ResourceManagerService_test"
+    },
+    {
+       "name": "ServiceLog_test"
+    }
+  ]
+}
diff --git a/services/mediaresourcemanager/test/Android.bp b/services/mediaresourcemanager/test/Android.bp
index 70e8833..543c87c 100644
--- a/services/mediaresourcemanager/test/Android.bp
+++ b/services/mediaresourcemanager/test/Android.bp
@@ -2,6 +2,7 @@
 cc_test {
     name: "ResourceManagerService_test",
     srcs: ["ResourceManagerService_test.cpp"],
+    test_suites: ["device-tests"],
     shared_libs: [
         "libbinder",
         "liblog",
@@ -23,6 +24,7 @@
 cc_test {
     name: "ServiceLog_test",
     srcs: ["ServiceLog_test.cpp"],
+    test_suites: ["device-tests"],
     shared_libs: [
         "liblog",
         "libmedia",
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index 8a3987a..ae97ec8 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -52,13 +52,69 @@
     DISALLOW_EVIL_CONSTRUCTORS(TestProcessInfo);
 };
 
+struct TestSystemCallback :
+        public ResourceManagerService::SystemCallbackInterface {
+    TestSystemCallback() :
+        mLastEvent({EventType::INVALID, 0}), mEventCount(0) {}
+
+    enum EventType {
+        INVALID          = -1,
+        VIDEO_ON         = 0,
+        VIDEO_OFF        = 1,
+        VIDEO_RESET      = 2,
+        CPUSET_ENABLE    = 3,
+        CPUSET_DISABLE   = 4,
+    };
+
+    struct EventEntry {
+        EventType type;
+        int arg;
+    };
+
+    virtual void noteStartVideo(int uid) override {
+        mLastEvent = {EventType::VIDEO_ON, uid};
+        mEventCount++;
+    }
+
+    virtual void noteStopVideo(int uid) override {
+        mLastEvent = {EventType::VIDEO_OFF, uid};
+        mEventCount++;
+    }
+
+    virtual void noteResetVideo() override {
+        mLastEvent = {EventType::VIDEO_RESET, 0};
+        mEventCount++;
+    }
+
+    virtual bool requestCpusetBoost(
+            bool enable, const sp<IInterface> &/*client*/) override {
+        mLastEvent = {enable ? EventType::CPUSET_ENABLE : EventType::CPUSET_DISABLE, 0};
+        mEventCount++;
+        return true;
+    }
+
+    size_t eventCount() { return mEventCount; }
+    EventType lastEventType() { return mLastEvent.type; }
+    EventEntry lastEvent() { return mLastEvent; }
+
+protected:
+    virtual ~TestSystemCallback() {}
+
+private:
+    EventEntry mLastEvent;
+    size_t mEventCount;
+
+    DISALLOW_EVIL_CONSTRUCTORS(TestSystemCallback);
+};
+
+
 struct TestClient : public BnResourceManagerClient {
     TestClient(int pid, sp<ResourceManagerService> service)
         : mReclaimed(false), mPid(pid), mService(service) {}
 
     virtual bool reclaimResource() {
         sp<IResourceManagerClient> client(this);
-        mService->removeResource(mPid, (int64_t) client.get());
+        mService->removeClient(mPid, (int64_t) client.get());
         mReclaimed = true;
         return true;
     }
@@ -95,10 +151,17 @@
 static const int kMidPriorityPid = 25;
 static const int kHighPriorityPid = 10;
 
+using EventType = TestSystemCallback::EventType;
+using EventEntry = TestSystemCallback::EventEntry;
+bool operator== (const EventEntry& lhs, const EventEntry& rhs) {
+    return lhs.type == rhs.type && lhs.arg == rhs.arg;
+}
+
 class ResourceManagerServiceTest : public ::testing::Test {
 public:
     ResourceManagerServiceTest()
-        : mService(new ResourceManagerService(new TestProcessInfo)),
+        : mSystemCB(new TestSystemCallback()),
+          mService(new ResourceManagerService(new TestProcessInfo, mSystemCB)),
           mTestClient1(new TestClient(kTestPid1, mService)),
           mTestClient2(new TestClient(kTestPid2, mService)),
           mTestClient3(new TestClient(kTestPid2, mService)) {
@@ -106,16 +169,14 @@
 
 protected:
     static bool isEqualResources(const Vector<MediaResource> &resources1,
-            const Vector<MediaResource> &resources2) {
-        if (resources1.size() != resources2.size()) {
-            return false;
-        }
+            const ResourceList &resources2) {
+        // convert resource1 to ResourceList
+        ResourceList r1;
         for (size_t i = 0; i < resources1.size(); ++i) {
-            if (resources1[i] != resources2[i]) {
-                return false;
-            }
+            const auto resType = std::make_pair(resources1[i].mType, resources1[i].mSubType);
+            r1[resType] = resources1[i];
         }
-        return true;
+        return r1 == resources2;
     }
 
     static void expectEqResourceInfo(const ResourceInfo &info,
@@ -184,14 +245,14 @@
         ASSERT_GE(index1, 0);
         const ResourceInfos &infos1 = map[index1];
         EXPECT_EQ(1u, infos1.size());
-        expectEqResourceInfo(infos1[0], kTestUid1, mTestClient1, resources1);
+        expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, resources1);
 
         ssize_t index2 = map.indexOfKey(kTestPid2);
         ASSERT_GE(index2, 0);
         const ResourceInfos &infos2 = map[index2];
         EXPECT_EQ(2u, infos2.size());
-        expectEqResourceInfo(infos2[0], kTestUid2, mTestClient2, resources2);
-        expectEqResourceInfo(infos2[1], kTestUid2, mTestClient3, resources3);
+        expectEqResourceInfo(infos2.valueFor(getId(mTestClient2)), kTestUid2, mTestClient2, resources2);
+        expectEqResourceInfo(infos2.valueFor(getId(mTestClient3)), kTestUid2, mTestClient3, resources3);
     }
 
     void testConfig() {
@@ -225,10 +286,84 @@
         EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec);
     }
 
+    void testCombineResource() {
+        // kTestPid1 mTestClient1
+        Vector<MediaResource> resources1;
+        resources1.push_back(MediaResource(MediaResource::kSecureCodec, 1));
+        mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+
+        Vector<MediaResource> resources11;
+        resources11.push_back(MediaResource(MediaResource::kGraphicMemory, 200));
+        mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
+
+        const PidResourceInfosMap &map = mService->mMap;
+        EXPECT_EQ(1u, map.size());
+        ssize_t index1 = map.indexOfKey(kTestPid1);
+        ASSERT_GE(index1, 0);
+        const ResourceInfos &infos1 = map[index1];
+        EXPECT_EQ(1u, infos1.size());
+
+        // test adding existing types to combine values
+        resources1.push_back(MediaResource(MediaResource::kGraphicMemory, 100));
+        mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+
+        Vector<MediaResource> expected;
+        expected.push_back(MediaResource(MediaResource::kSecureCodec, 2));
+        expected.push_back(MediaResource(MediaResource::kGraphicMemory, 300));
+        expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
+
+        // test adding new types (including types that differs only in subType)
+        resources11.push_back(MediaResource(MediaResource::kNonSecureCodec, 1));
+        resources11.push_back(MediaResource(MediaResource::kSecureCodec, MediaResource::kVideoCodec, 1));
+        mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
+
+        expected.clear();
+        expected.push_back(MediaResource(MediaResource::kSecureCodec, 2));
+        expected.push_back(MediaResource(MediaResource::kNonSecureCodec, 1));
+        expected.push_back(MediaResource(MediaResource::kSecureCodec, MediaResource::kVideoCodec, 1));
+        expected.push_back(MediaResource(MediaResource::kGraphicMemory, 500));
+        expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
+    }
+
     void testRemoveResource() {
+        // kTestPid1 mTestClient1
+        Vector<MediaResource> resources1;
+        resources1.push_back(MediaResource(MediaResource::kSecureCodec, 1));
+        mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+
+        Vector<MediaResource> resources11;
+        resources11.push_back(MediaResource(MediaResource::kGraphicMemory, 200));
+        mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
+
+        const PidResourceInfosMap &map = mService->mMap;
+        EXPECT_EQ(1u, map.size());
+        ssize_t index1 = map.indexOfKey(kTestPid1);
+        ASSERT_GE(index1, 0);
+        const ResourceInfos &infos1 = map[index1];
+        EXPECT_EQ(1u, infos1.size());
+
+        // test partial removal
+        resources11.editItemAt(0).mValue = 100;
+        mService->removeResource(kTestPid1, getId(mTestClient1), resources11);
+
+        Vector<MediaResource> expected;
+        expected.push_back(MediaResource(MediaResource::kSecureCodec, 1));
+        expected.push_back(MediaResource(MediaResource::kGraphicMemory, 100));
+        expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
+
+        // test complete removal with overshoot value
+        resources11.editItemAt(0).mValue = 1000;
+        mService->removeResource(kTestPid1, getId(mTestClient1), resources11);
+
+        expected.clear();
+        expected.push_back(MediaResource(MediaResource::kSecureCodec, 1));
+        expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
+    }
+
+    void testRemoveClient() {
         addResource();
 
-        mService->removeResource(kTestPid2, getId(mTestClient2));
+        mService->removeClient(kTestPid2, getId(mTestClient2));
 
         const PidResourceInfosMap &map = mService->mMap;
         EXPECT_EQ(2u, map.size());
@@ -237,6 +372,7 @@
         EXPECT_EQ(1u, infos1.size());
         EXPECT_EQ(1u, infos2.size());
         // mTestClient2 has been removed.
+        // (OK to use infos2[0] as there is only 1 entry)
         EXPECT_EQ(mTestClient3, infos2[0].client);
     }
 
@@ -252,6 +388,7 @@
         EXPECT_TRUE(mService->getAllClients_l(kHighPriorityPid, type, &clients));
 
         EXPECT_EQ(2u, clients.size());
+        // (OK to require ordering in clients[], as the pid map is sorted)
         EXPECT_EQ(mTestClient3, clients[0]);
         EXPECT_EQ(mTestClient1, clients[1]);
     }
@@ -444,7 +581,7 @@
             verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
 
             // clean up client 3 which still left
-            mService->removeResource(kTestPid2, getId(mTestClient3));
+            mService->removeClient(kTestPid2, getId(mTestClient3));
         }
     }
 
@@ -504,6 +641,84 @@
         EXPECT_TRUE(mService->isCallingPriorityHigher_l(99, 100));
     }
 
+    void testBatteryStats() {
+        // reset should always be called when ResourceManagerService is created (restarted)
+        EXPECT_EQ(1u, mSystemCB->eventCount());
+        EXPECT_EQ(EventType::VIDEO_RESET, mSystemCB->lastEventType());
+
+        // new client request should cause VIDEO_ON
+        Vector<MediaResource> resources1;
+        resources1.push_back(MediaResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1));
+        mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+        EXPECT_EQ(2u, mSystemCB->eventCount());
+        EXPECT_EQ(EventEntry({EventType::VIDEO_ON, kTestUid1}), mSystemCB->lastEvent());
+
+        // each client should only cause 1 VIDEO_ON
+        mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+        EXPECT_EQ(2u, mSystemCB->eventCount());
+
+        // new client request should cause VIDEO_ON
+        Vector<MediaResource> resources2;
+        resources2.push_back(MediaResource(MediaResource::kBattery, MediaResource::kVideoCodec, 2));
+        mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), mTestClient2, resources2);
+        EXPECT_EQ(3u, mSystemCB->eventCount());
+        EXPECT_EQ(EventEntry({EventType::VIDEO_ON, kTestUid2}), mSystemCB->lastEvent());
+
+        // partially remove mTestClient1's request, shouldn't be any VIDEO_OFF
+        mService->removeResource(kTestPid1, getId(mTestClient1), resources1);
+        EXPECT_EQ(3u, mSystemCB->eventCount());
+
+        // remove mTestClient1's request, should be VIDEO_OFF for kTestUid1
+        // (use resource2 to test removing more instances than previously requested)
+        mService->removeResource(kTestPid1, getId(mTestClient1), resources2);
+        EXPECT_EQ(4u, mSystemCB->eventCount());
+        EXPECT_EQ(EventEntry({EventType::VIDEO_OFF, kTestUid1}), mSystemCB->lastEvent());
+
+        // remove mTestClient2, should be VIDEO_OFF for kTestUid2
+        mService->removeClient(kTestPid2, getId(mTestClient2));
+        EXPECT_EQ(5u, mSystemCB->eventCount());
+        EXPECT_EQ(EventEntry({EventType::VIDEO_OFF, kTestUid2}), mSystemCB->lastEvent());
+    }
+
+    void testCpusetBoost() {
+        // reset should always be called when ResourceManagerService is created (restarted)
+        EXPECT_EQ(1u, mSystemCB->eventCount());
+        EXPECT_EQ(EventType::VIDEO_RESET, mSystemCB->lastEventType());
+
+        // new client request should cause CPUSET_ENABLE
+        Vector<MediaResource> resources1;
+        resources1.push_back(MediaResource(MediaResource::kCpuBoost, 1));
+        mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+        EXPECT_EQ(2u, mSystemCB->eventCount());
+        EXPECT_EQ(EventType::CPUSET_ENABLE, mSystemCB->lastEventType());
+
+        // each client should only cause 1 CPUSET_ENABLE
+        mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
+        EXPECT_EQ(2u, mSystemCB->eventCount());
+
+        // new client request should cause CPUSET_ENABLE
+        Vector<MediaResource> resources2;
+        resources2.push_back(MediaResource(MediaResource::kCpuBoost, 2));
+        mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), mTestClient2, resources2);
+        EXPECT_EQ(3u, mSystemCB->eventCount());
+        EXPECT_EQ(EventType::CPUSET_ENABLE, mSystemCB->lastEventType());
+
+        // remove mTestClient2 should not cause CPUSET_DISABLE, mTestClient1 still active
+        mService->removeClient(kTestPid2, getId(mTestClient2));
+        EXPECT_EQ(3u, mSystemCB->eventCount());
+
+        // remove 1 cpuboost from mTestClient1, should not be CPUSET_DISABLE (still 1 left)
+        mService->removeResource(kTestPid1, getId(mTestClient1), resources1);
+        EXPECT_EQ(3u, mSystemCB->eventCount());
+
+        // remove 2 cpuboost from mTestClient1, should be CPUSET_DISABLE
+        // (use resource2 to test removing more than previously requested)
+        mService->removeResource(kTestPid1, getId(mTestClient1), resources2);
+        EXPECT_EQ(4u, mSystemCB->eventCount());
+        EXPECT_EQ(EventType::CPUSET_DISABLE, mSystemCB->lastEventType());
+    }
+
+    sp<TestSystemCallback> mSystemCB;
     sp<ResourceManagerService> mService;
     sp<IResourceManagerClient> mTestClient1;
     sp<IResourceManagerClient> mTestClient2;
@@ -518,10 +733,18 @@
     addResource();
 }
 
+TEST_F(ResourceManagerServiceTest, combineResource) {
+    testCombineResource();
+}
+
 TEST_F(ResourceManagerServiceTest, removeResource) {
     testRemoveResource();
 }
 
+TEST_F(ResourceManagerServiceTest, removeClient) {
+    testRemoveClient();
+}
+
 TEST_F(ResourceManagerServiceTest, reclaimResource) {
     testReclaimResourceSecure();
     testReclaimResourceNonSecure();
@@ -547,4 +770,12 @@
     testIsCallingPriorityHigher();
 }
 
+TEST_F(ResourceManagerServiceTest, testBatteryStats) {
+    testBatteryStats();
+}
+
+TEST_F(ResourceManagerServiceTest, testCpusetBoost) {
+    testCpusetBoost();
+}
+
 } // namespace android