Merge "AudioClient: Fix clang-tidy warnings" into rvc-dev
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index aea28c0..54f1fa2 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -113,6 +113,7 @@
     const Trex *mTrex;
     off64_t mFirstMoofOffset;
     off64_t mCurrentMoofOffset;
+    off64_t mCurrentMoofSize;
     off64_t mNextMoofOffset;
     uint32_t mCurrentTime; // in media timescale ticks
     int32_t mLastParsedTrackId;
@@ -165,8 +166,9 @@
     status_t parseTrackFragmentRun(off64_t offset, off64_t size);
     status_t parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size);
     status_t parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size);
-    status_t parseClearEncryptedSizes(off64_t offset, bool isSubsampleEncryption, uint32_t flags);
-    status_t parseSampleEncryption(off64_t offset);
+    status_t parseClearEncryptedSizes(off64_t offset, bool isSampleEncryption,
+            uint32_t flags, off64_t size);
+    status_t parseSampleEncryption(off64_t offset, off64_t size);
     // returns -1 for invalid layer ID
     int32_t parseHEVCLayerId(const uint8_t *data, size_t size);
 
@@ -4859,6 +4861,7 @@
       mTrex(trex),
       mFirstMoofOffset(firstMoofOffset),
       mCurrentMoofOffset(firstMoofOffset),
+      mCurrentMoofSize(0),
       mNextMoofOffset(-1),
       mCurrentTime(0),
       mDefaultEncryptedByteBlock(0),
@@ -5109,6 +5112,9 @@
         case FOURCC("moof"): {
             off64_t stop_offset = *offset + chunk_size;
             *offset = data_offset;
+            if (chunk_type == FOURCC("moof")) {
+                mCurrentMoofSize = chunk_data_size;
+            }
             while (*offset < stop_offset) {
                 status_t err = parseChunk(offset);
                 if (err != OK) {
@@ -5197,7 +5203,7 @@
 
         case FOURCC("senc"): {
             status_t err;
-            if ((err = parseSampleEncryption(data_offset)) != OK) {
+            if ((err = parseSampleEncryption(data_offset, chunk_data_size)) != OK) {
                 return err;
             }
             *offset += chunk_size;
@@ -5390,11 +5396,11 @@
 
     drmoffset += mCurrentMoofOffset;
 
-    return parseClearEncryptedSizes(drmoffset, false, 0);
+    return parseClearEncryptedSizes(drmoffset, false, 0, mCurrentMoofSize);
 }
 
 status_t MPEG4Source::parseClearEncryptedSizes(
-        off64_t offset, bool isSubsampleEncryption, uint32_t flags) {
+        off64_t offset, bool isSampleEncryption, uint32_t flags, off64_t size) {
 
     int32_t ivlength;
     if (!AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, &ivlength)) {
@@ -5408,11 +5414,15 @@
     }
 
     uint32_t sampleCount = mCurrentSampleInfoCount;
-    if (isSubsampleEncryption) {
+    if (isSampleEncryption) {
+        if (size < 4) {
+            return ERROR_MALFORMED;
+        }
         if (!mDataSource->getUInt32(offset, &sampleCount)) {
             return ERROR_IO;
         }
         offset += 4;
+        size -= 4;
     }
 
     // read CencSampleAuxiliaryDataFormats
@@ -5427,14 +5437,18 @@
         }
 
         memset(smpl->iv, 0, 16);
+        if (size < ivlength) {
+            return ERROR_MALFORMED;
+        }
         if (mDataSource->readAt(offset, smpl->iv, ivlength) != ivlength) {
             return ERROR_IO;
         }
 
         offset += ivlength;
+        size -= ivlength;
 
         bool readSubsamples;
-        if (isSubsampleEncryption) {
+        if (isSampleEncryption) {
             readSubsamples = flags & 2;
         } else {
             int32_t smplinfosize = mCurrentDefaultSampleInfoSize;
@@ -5446,13 +5460,20 @@
 
         if (readSubsamples) {
             uint16_t numsubsamples;
+            if (size < 2) {
+                return ERROR_MALFORMED;
+            }
             if (!mDataSource->getUInt16(offset, &numsubsamples)) {
                 return ERROR_IO;
             }
             offset += 2;
+            size -= 2;
             for (size_t j = 0; j < numsubsamples; j++) {
                 uint16_t numclear;
                 uint32_t numencrypted;
+                if (size < 6) {
+                    return ERROR_MALFORMED;
+                }
                 if (!mDataSource->getUInt16(offset, &numclear)) {
                     return ERROR_IO;
                 }
@@ -5461,6 +5482,7 @@
                     return ERROR_IO;
                 }
                 offset += 4;
+                size -= 6;
                 smpl->clearsizes.add(numclear);
                 smpl->encryptedsizes.add(numencrypted);
             }
@@ -5473,12 +5495,15 @@
     return OK;
 }
 
-status_t MPEG4Source::parseSampleEncryption(off64_t offset) {
+status_t MPEG4Source::parseSampleEncryption(off64_t offset, off64_t chunk_data_size) {
     uint32_t flags;
+    if (chunk_data_size < 4) {
+        return ERROR_MALFORMED;
+    }
     if (!mDataSource->getUInt32(offset, &flags)) { // actually version + flags
         return ERROR_MALFORMED;
     }
-    return parseClearEncryptedSizes(offset + 4, true, flags);
+    return parseClearEncryptedSizes(offset + 4, true, flags, chunk_data_size - 4);
 }
 
 status_t MPEG4Source::parseTrackFragmentHeader(off64_t offset, off64_t size) {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index fcccf03..6723ec9 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -714,12 +714,8 @@
 aaudio_result_t AudioStreamInternal::setBufferSize(int32_t requestedFrames) {
     int32_t adjustedFrames = requestedFrames;
     const int32_t maximumSize = getBufferCapacity() - mFramesPerBurst;
-    // The buffer size can be set to zero.
-    // This means that the callback may be called when the internal buffer becomes empty.
-    // This will be fine on some devices in ideal circumstances and will result in the
-    // lowest possible latency.
-    // If there are glitches then they should be detected as XRuns and the size can be increased.
-    static const int32_t minimumSize = 0;
+    // Minimum size should be a multiple number of bursts.
+    const int32_t minimumSize = 1 * mFramesPerBurst;
 
     // Clip to minimum size so that rounding up will work better.
     adjustedFrames = std::max(minimumSize, adjustedFrames);
@@ -731,12 +727,14 @@
         // Round to the next highest burst size.
         int32_t numBursts = (adjustedFrames + mFramesPerBurst - 1) / mFramesPerBurst;
         adjustedFrames = numBursts * mFramesPerBurst;
+        // Clip just in case maximumSize is not a multiple of mFramesPerBurst.
+        adjustedFrames = std::min(maximumSize, adjustedFrames);
     }
 
     // Clip against the actual size from the endpoint.
     int32_t actualFrames = 0;
     mAudioEndpoint.setBufferSizeInFrames(maximumSize, &actualFrames);
-    // actualFrames should be <= maximumSize
+    // actualFrames should be <= actual maximum size of endpoint
     adjustedFrames = std::min(actualFrames, adjustedFrames);
 
     mBufferSizeInFrames = adjustedFrames;
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index bd46d05..f0dcd44 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -47,6 +47,7 @@
         , mMarkerNanoTime(0)
         , mSampleRate(48000)
         , mFramesPerBurst(48)
+        , mBurstPeriodNanos(0) // this will be updated before use
         , mMaxMeasuredLatenessNanos(0)
         , mLatenessForDriftNanos(kInitialLatenessForDriftNanos)
         , mState(STATE_STOPPED)
@@ -57,9 +58,6 @@
     }
 }
 
-IsochronousClockModel::~IsochronousClockModel() {
-}
-
 void IsochronousClockModel::setPositionAndTime(int64_t framePosition, int64_t nanoTime) {
     ALOGV("setPositionAndTime, %lld, %lld", (long long) framePosition, (long long) nanoTime);
     mMarkerFramePosition = framePosition;
@@ -186,7 +184,7 @@
             // Calculate upper region that will trigger a drift forwards.
             mLatenessForDriftNanos = mMaxMeasuredLatenessNanos - (mMaxMeasuredLatenessNanos >> 4);
         } else { // decrease
-            // If these is an outlier in lateness then mMaxMeasuredLatenessNanos can go high
+            // If this is an outlier in lateness then mMaxMeasuredLatenessNanos can go high
             // and stay there. So we slowly reduce mMaxMeasuredLatenessNanos for better
             // long term stability. The two opposing forces will keep mMaxMeasuredLatenessNanos
             // within a reasonable range.
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 40f066b..6280013 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -35,7 +35,7 @@
 
 public:
     IsochronousClockModel();
-    virtual ~IsochronousClockModel();
+    virtual ~IsochronousClockModel() = default;
 
     void start(int64_t nanoTime);
     void stop(int64_t nanoTime);
@@ -130,6 +130,7 @@
 private:
 
     int32_t getLateTimeOffsetNanos() const;
+    void update();
 
     enum clock_model_state_t {
         STATE_STOPPED,
@@ -164,7 +165,6 @@
     // distribution of timestamps relative to earliest
     std::unique_ptr<android::audio_utils::Histogram>   mHistogramMicros;
 
-    void update();
 };
 
 } /* namespace aaudio */
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 74fa343..6357da4 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -55,6 +55,7 @@
 {
     sp<IAudioFlinger> af;
     sp<AudioFlingerClient> afc;
+    bool reportNoError = false;
     {
         Mutex::Autolock _l(gLock);
         if (gAudioFlinger == 0) {
@@ -70,7 +71,7 @@
             if (gAudioFlingerClient == NULL) {
                 gAudioFlingerClient = new AudioFlingerClient();
             } else {
-                reportError(NO_ERROR);
+                reportNoError = true;
             }
             binder->linkToDeath(gAudioFlingerClient);
             gAudioFlinger = interface_cast<IAudioFlinger>(binder);
@@ -86,6 +87,7 @@
         af->registerClient(afc);
         IPCThreadState::self()->restoreCallingIdentity(token);
     }
+    if (reportNoError) reportError(NO_ERROR);
     return af;
 }
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 5c891e2..03c16f3 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1185,15 +1185,21 @@
     }
 
     AutoMutex lock(mHardwareLock);
+    sp<DeviceHalInterface> primaryDev = mPrimaryHardwareDev->hwDevice();
+    if (primaryDev == nullptr) {
+        ALOGW("%s: no primary HAL device", __func__);
+        return INVALID_OPERATION;
+    }
     mHardwareStatus = AUDIO_HW_SET_MIC_MUTE;
+    ret = primaryDev->setMicMute(state);
     for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
         sp<DeviceHalInterface> dev = mAudioHwDevs.valueAt(i)->hwDevice();
-        status_t result = dev->setMicMute(state);
-        if (result != NO_ERROR) {
-            ret = result;
+        if (dev != primaryDev) {
+            (void)dev->setMicMute(state);
         }
     }
     mHardwareStatus = AUDIO_HW_IDLE;
+    ALOGW_IF(ret != NO_ERROR, "%s: error %d setting state to HAL", __func__, ret);
     return ret;
 }
 
@@ -1203,20 +1209,18 @@
     if (ret != NO_ERROR) {
         return false;
     }
-    bool mute = true;
-    bool state = AUDIO_MODE_INVALID;
     AutoMutex lock(mHardwareLock);
-    mHardwareStatus = AUDIO_HW_GET_MIC_MUTE;
-    for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
-        sp<DeviceHalInterface> dev = mAudioHwDevs.valueAt(i)->hwDevice();
-        status_t result = dev->getMicMute(&state);
-        if (result == NO_ERROR) {
-            mute = mute && state;
-        }
+    sp<DeviceHalInterface> primaryDev = mPrimaryHardwareDev->hwDevice();
+    if (primaryDev == nullptr) {
+        ALOGW("%s: no primary HAL device", __func__);
+        return false;
     }
+    bool state;
+    mHardwareStatus = AUDIO_HW_GET_MIC_MUTE;
+    ret = primaryDev->getMicMute(&state);
     mHardwareStatus = AUDIO_HW_IDLE;
-
-    return mute;
+    ALOGE_IF(ret != NO_ERROR, "%s: error %d getting state from HAL", __func__, ret);
+    return (ret == NO_ERROR) && state;
 }
 
 void AudioFlinger::setRecordSilenced(audio_port_handle_t portId, bool silenced)
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index cc369fa..10b653e 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -841,13 +841,6 @@
         itDuration++; itSize++;
     }
 
-    c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS,
-            dynamicDepthEntries.data(), dynamicDepthEntries.size());
-    c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS,
-            dynamicDepthMinDurationEntries.data(), dynamicDepthMinDurationEntries.size());
-    c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS,
-            dynamicDepthStallDurationEntries.data(), dynamicDepthStallDurationEntries.size());
-
     std::vector<int32_t> supportedChTags;
     supportedChTags.reserve(chTags.count + 3);
     supportedChTags.insert(supportedChTags.end(), chTags.data.i32,
@@ -855,6 +848,12 @@
     supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS);
     supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS);
     supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS);
+    c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS,
+            dynamicDepthEntries.data(), dynamicDepthEntries.size());
+    c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS,
+            dynamicDepthMinDurationEntries.data(), dynamicDepthMinDurationEntries.size());
+    c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS,
+            dynamicDepthStallDurationEntries.data(), dynamicDepthStallDurationEntries.size());
     c.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, supportedChTags.data(),
             supportedChTags.size());
 
diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk
index fa5d69e..3ead715 100644
--- a/services/camera/libcameraservice/tests/Android.mk
+++ b/services/camera/libcameraservice/tests/Android.mk
@@ -48,6 +48,8 @@
 
 LOCAL_CFLAGS += -Wall -Wextra -Werror
 
+LOCAL_SANITIZE := address
+
 LOCAL_MODULE:= cameraservice_test
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_MODULE_TAGS := tests
diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
index a8f6889..855b5ab 100644
--- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
@@ -40,8 +40,15 @@
  */
 struct TestDeviceInterface : public device::V3_2::ICameraDevice {
     std::vector<hardware::hidl_string> mDeviceNames;
+    android::hardware::hidl_vec<uint8_t> mCharacteristics;
+
+    TestDeviceInterface(std::vector<hardware::hidl_string> deviceNames,
+            android::hardware::hidl_vec<uint8_t> chars) :
+        mDeviceNames(deviceNames), mCharacteristics(chars) {}
+
     TestDeviceInterface(std::vector<hardware::hidl_string> deviceNames) :
         mDeviceNames(deviceNames) {}
+
     using getResourceCost_cb = std::function<void(
             hardware::camera::common::V1_0::Status status,
             const hardware::camera::common::V1_0::CameraResourceCost& resourceCost)>;
@@ -58,8 +65,7 @@
             const hardware::hidl_vec<uint8_t>& cameraCharacteristics)>;
     hardware::Return<void> getCameraCharacteristics(
             getCameraCharacteristics_cb _hidl_cb) override {
-        hardware::hidl_vec<uint8_t> cameraCharacteristics;
-        _hidl_cb(Status::OK, cameraCharacteristics);
+        _hidl_cb(Status::OK, mCharacteristics);
         return hardware::Void();
     }
 
@@ -100,6 +106,13 @@
         mDeviceInterface(new TestDeviceInterface(devices)),
         mVendorTagSections (vendorSection) {}
 
+    TestICameraProvider(const std::vector<hardware::hidl_string> &devices,
+            const hardware::hidl_vec<common::V1_0::VendorTagSection> &vendorSection,
+            android::hardware::hidl_vec<uint8_t> chars) :
+        mDeviceNames(devices),
+        mDeviceInterface(new TestDeviceInterface(devices, chars)),
+        mVendorTagSections (vendorSection) {}
+
     virtual hardware::Return<Status> setCallback(
             const sp<provider::V2_4::ICameraProviderCallback>& callbacks) override {
         mCalledCounter[SET_CALLBACK]++;
@@ -243,6 +256,52 @@
     void onNewProviderRegistered() override {}
 };
 
+TEST(CameraProviderManagerTest, InitializeDynamicDepthTest) {
+    std::vector<hardware::hidl_string> deviceNames;
+    deviceNames.push_back("device@3.2/test/0");
+    hardware::hidl_vec<common::V1_0::VendorTagSection> vendorSection;
+    status_t res;
+    sp<CameraProviderManager> providerManager = new CameraProviderManager();
+    sp<TestStatusListener> statusListener = new TestStatusListener();
+    TestInteractionProxy serviceProxy;
+
+    android::hardware::hidl_vec<uint8_t> chars;
+    CameraMetadata meta;
+    int32_t charKeys[] = { ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE,
+            ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS };
+    meta.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, charKeys,
+            sizeof(charKeys) / sizeof(charKeys[0]));
+    uint8_t depthIsExclusive = ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE_FALSE;
+    meta.update(ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE, &depthIsExclusive, 1);
+    int32_t sizes[] = { HAL_PIXEL_FORMAT_BLOB,
+            640, 480, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT };
+    meta.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, sizes,
+            sizeof(sizes) / sizeof(sizes[0]));
+    sizes[0] = HAL_PIXEL_FORMAT_Y16;
+    meta.update(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, sizes,
+            sizeof(sizes) / sizeof(sizes[0]));
+    int64_t durations[] = { HAL_PIXEL_FORMAT_BLOB, 640, 480, 0 };
+    meta.update(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, durations,
+            sizeof(durations) / sizeof(durations[0]));
+    meta.update(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, durations,
+            sizeof(durations) / sizeof(durations[0]));
+    durations[0]= HAL_PIXEL_FORMAT_Y16;
+    meta.update(ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS, durations,
+            sizeof(durations) / sizeof(durations[0]));
+    meta.update(ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS, durations,
+            sizeof(durations) / sizeof(durations[0]));
+    camera_metadata_t* metaBuffer = const_cast<camera_metadata_t*>(meta.getAndLock());
+    chars.setToExternal(reinterpret_cast<uint8_t*>(metaBuffer),
+            get_camera_metadata_size(metaBuffer));
+
+    sp<TestICameraProvider> provider =  new TestICameraProvider(deviceNames,
+            vendorSection, chars);
+    serviceProxy.setProvider(provider);
+
+    res = providerManager->initialize(statusListener, &serviceProxy);
+    ASSERT_EQ(res, OK) << "Unable to initialize provider manager";
+}
+
 TEST(CameraProviderManagerTest, InitializeTest) {
     std::vector<hardware::hidl_string> deviceNames;
     deviceNames.push_back("device@3.2/test/0");
diff --git a/services/camera/libcameraservice/tests/RotateAndCropMapperTest.cpp b/services/camera/libcameraservice/tests/RotateAndCropMapperTest.cpp
index c638d40..3c187cd 100644
--- a/services/camera/libcameraservice/tests/RotateAndCropMapperTest.cpp
+++ b/services/camera/libcameraservice/tests/RotateAndCropMapperTest.cpp
@@ -38,10 +38,9 @@
 
 #define EXPECT_EQUAL_WITHIN_N(vec, array, N, msg)   \
 { \
-    std::vector<int32_t> vec_diff; \
-    std::transform(vec.begin(), vec.end(), array, \
-            std::back_inserter(vec_diff), std::minus()); \
-    EXPECT_THAT(vec_diff, Each(AllOf(Ge(-N), Le(N)))) << msg; \
+    for (size_t i = 0; i < vec.size(); i++) { \
+        EXPECT_THAT(vec[i] - array[i], AllOf(Ge(-N), Le(N))) << msg " failed at index:" << i; \
+    } \
 }
 
 int32_t testActiveArray[] = {100, 100, 4000, 3000};
diff --git a/services/minijail/Android.bp b/services/minijail/Android.bp
index 0713a87..5ea6d1e 100644
--- a/services/minijail/Android.bp
+++ b/services/minijail/Android.bp
@@ -39,4 +39,5 @@
     srcs: [
         "av_services_minijail_unittest.cpp",
     ],
+    test_suites: ["device-tests"],
 }
diff --git a/services/minijail/TEST_MAPPING b/services/minijail/TEST_MAPPING
new file mode 100644
index 0000000..0d89760
--- /dev/null
+++ b/services/minijail/TEST_MAPPING
@@ -0,0 +1,5 @@
+{
+  "presubmit": [
+    { "name": "libavservices_minijail_unittest" }
+  ]
+}
diff --git a/services/minijail/av_services_minijail_unittest.cpp b/services/minijail/av_services_minijail_unittest.cpp
index 31313f8..896a764 100644
--- a/services/minijail/av_services_minijail_unittest.cpp
+++ b/services/minijail/av_services_minijail_unittest.cpp
@@ -34,13 +34,32 @@
         "mmap: 1\n"
         "munmap: 1\n";
 
+    const std::string third_policy_ =
+        "open: 1\n"
+        "close: 1\n";
+
     const std::string full_policy_ = base_policy_ + std::string("\n") + additional_policy_;
+    const std::string triple_policy_ = base_policy_ +
+                                       std::string("\n") + additional_policy_ +
+                                       std::string("\n") + third_policy_;
 };
 
 TEST_F(WritePolicyTest, OneFile)
 {
     std::string final_string;
-    android::base::unique_fd fd(android::WritePolicyToPipe(base_policy_, std::string()));
+    // vector with an empty pathname
+    android::base::unique_fd fd(android::WritePolicyToPipe(base_policy_, {std::string()}));
+    EXPECT_LE(0, fd.get());
+    bool success = android::base::ReadFdToString(fd.get(), &final_string);
+    EXPECT_TRUE(success);
+    EXPECT_EQ(final_string, base_policy_);
+}
+
+TEST_F(WritePolicyTest, OneFileAlternate)
+{
+    std::string final_string;
+    // empty vector
+    android::base::unique_fd fd(android::WritePolicyToPipe(base_policy_, {}));
     EXPECT_LE(0, fd.get());
     bool success = android::base::ReadFdToString(fd.get(), &final_string);
     EXPECT_TRUE(success);
@@ -50,9 +69,19 @@
 TEST_F(WritePolicyTest, TwoFiles)
 {
     std::string final_string;
-    android::base::unique_fd fd(android::WritePolicyToPipe(base_policy_, additional_policy_));
+    android::base::unique_fd fd(android::WritePolicyToPipe(base_policy_, {additional_policy_}));
     EXPECT_LE(0, fd.get());
     bool success = android::base::ReadFdToString(fd.get(), &final_string);
     EXPECT_TRUE(success);
     EXPECT_EQ(final_string, full_policy_);
 }
+
+TEST_F(WritePolicyTest, ThreeFiles)
+{
+    std::string final_string;
+    android::base::unique_fd fd(android::WritePolicyToPipe(base_policy_, {additional_policy_, third_policy_}));
+    EXPECT_LE(0, fd.get());
+    bool success = android::base::ReadFdToString(fd.get(), &final_string);
+    EXPECT_TRUE(success);
+    EXPECT_EQ(final_string, triple_policy_);
+}
diff --git a/services/minijail/minijail.cpp b/services/minijail/minijail.cpp
index f213287..c7832b9 100644
--- a/services/minijail/minijail.cpp
+++ b/services/minijail/minijail.cpp
@@ -29,7 +29,7 @@
 namespace android {
 
 int WritePolicyToPipe(const std::string& base_policy_content,
-                      const std::string& additional_policy_content)
+                      const std::vector<std::string>& additional_policy_contents)
 {
     int pipefd[2];
     if (pipe(pipefd) == -1) {
@@ -40,9 +40,11 @@
     base::unique_fd write_end(pipefd[1]);
     std::string content = base_policy_content;
 
-    if (additional_policy_content.length() > 0) {
-        content += "\n";
-        content += additional_policy_content;
+    for (auto one_content : additional_policy_contents) {
+        if (one_content.length() > 0) {
+            content += "\n";
+            content += one_content;
+        }
     }
 
     if (!base::WriteStringToFd(content, write_end.get())) {
@@ -53,29 +55,34 @@
     return pipefd[0];
 }
 
-void SetUpMinijail(const std::string& base_policy_path, const std::string& additional_policy_path)
+void SetUpMinijail(const std::string& base_policy_path,
+                   const std::string& additional_policy_path)
 {
-    // No seccomp policy defined for this architecture.
-    if (access(base_policy_path.c_str(), R_OK) == -1) {
-        LOG(WARNING) << "No seccomp policy defined for this architecture.";
-        return;
-    }
+    SetUpMinijailList(base_policy_path, {additional_policy_path});
+}
 
+void SetUpMinijailList(const std::string& base_policy_path,
+                   const std::vector<std::string>& additional_policy_paths)
+{
     std::string base_policy_content;
-    std::string additional_policy_content;
+    std::vector<std::string> additional_policy_contents;
     if (!base::ReadFileToString(base_policy_path, &base_policy_content,
                                 false /* follow_symlinks */)) {
         LOG(FATAL) << "Could not read base policy file '" << base_policy_path << "'";
     }
 
-    if (additional_policy_path.length() > 0 &&
-        !base::ReadFileToString(additional_policy_path, &additional_policy_content,
-                                false /* follow_symlinks */)) {
-        LOG(WARNING) << "Could not read additional policy file '" << additional_policy_path << "'";
-        additional_policy_content = std::string();
+    for (auto one_policy_path : additional_policy_paths) {
+        std::string one_policy_content;
+        if (one_policy_path.length() > 0 &&
+                !base::ReadFileToString(one_policy_path, &one_policy_content,
+                    false /* follow_symlinks */)) {
+            // TODO: harder failure (fatal unless ENOENT?)
+            LOG(WARNING) << "Could not read additional policy file '" << one_policy_path << "'";
+        }
+        additional_policy_contents.push_back(one_policy_content);
     }
 
-    base::unique_fd policy_fd(WritePolicyToPipe(base_policy_content, additional_policy_content));
+    base::unique_fd policy_fd(WritePolicyToPipe(base_policy_content, additional_policy_contents));
     if (policy_fd.get() == -1) {
         LOG(FATAL) << "Could not write seccomp policy to fd";
     }
diff --git a/services/minijail/minijail.h b/services/minijail/minijail.h
index c8a2149..298af86 100644
--- a/services/minijail/minijail.h
+++ b/services/minijail/minijail.h
@@ -16,11 +16,15 @@
 #define AV_SERVICES_MINIJAIL_MINIJAIL
 
 #include <string>
+#include <vector>
 
 namespace android {
 int WritePolicyToPipe(const std::string& base_policy_content,
-                      const std::string& additional_policy_content);
-void SetUpMinijail(const std::string& base_policy_path, const std::string& additional_policy_path);
+                      const std::vector<std::string>& additional_policy_contents);
+void SetUpMinijail(const std::string& base_policy_path,
+                   const std::string& additional_policy_path);
+void SetUpMinijailList(const std::string& base_policy_path,
+                       const std::vector<std::string>& additional_policy_paths);
 }
 
 #endif  // AV_SERVICES_MINIJAIL_MINIJAIL