Add VTS test cases for CAS AIDL

Bug: 230377377, 227673974
Test: atest VtsHalCasAidlTargetTest
Change-Id: If0797975bec961c8388c75260f04c21eb09a169f
diff --git a/cas/aidl/vts/functional/VtsHalCasAidlTargetTest.cpp b/cas/aidl/vts/functional/VtsHalCasAidlTargetTest.cpp
new file mode 100644
index 0000000..266b55d
--- /dev/null
+++ b/cas/aidl/vts/functional/VtsHalCasAidlTargetTest.cpp
@@ -0,0 +1,809 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "mediacas_aidl_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/cas/BnCasListener.h>
+#include <aidl/android/hardware/cas/IMediaCasService.h>
+#include <aidl/android/hardware/cas/Status.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/ParcelFileDescriptor.h>
+#include <cutils/ashmem.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+#define CLEAR_KEY_SYSTEM_ID 0xF6D8
+#define INVALID_SYSTEM_ID 0
+#define WAIT_TIMEOUT 3000000000
+
+#define PROVISION_STR                                      \
+    "{                                                   " \
+    "  \"id\": 21140844,                                 " \
+    "  \"name\": \"Test Title\",                         " \
+    "  \"lowercase_organization_name\": \"Android\",     " \
+    "  \"asset_key\": {                                  " \
+    "  \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\"  " \
+    "  },                                                " \
+    "  \"cas_type\": 1,                                  " \
+    "  \"track_types\": [ ]                              " \
+    "}                                                   "
+
+using aidl::android::hardware::common::Ashmem;
+using android::Mutex;
+using namespace aidl::android::hardware::cas;
+using namespace ndk;
+using namespace std;
+using namespace testing;
+
+const uint8_t kEcmBinaryBuffer[] = {
+        0x00, 0x00, 0x01, 0xf0, 0x00, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x46, 0x00,
+        0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x27, 0x10, 0x02, 0x00,
+        0x01, 0x77, 0x01, 0x42, 0x95, 0x6c, 0x0e, 0xe3, 0x91, 0xbc, 0xfd, 0x05, 0xb1, 0x60, 0x4f,
+        0x17, 0x82, 0xa4, 0x86, 0x9b, 0x23, 0x56, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+        0x27, 0x10, 0x02, 0x00, 0x01, 0x77, 0x01, 0x42, 0x95, 0x6c, 0xd7, 0x43, 0x62, 0xf8, 0x1c,
+        0x62, 0x19, 0x05, 0xc7, 0x3a, 0x42, 0xcd, 0xfd, 0xd9, 0x13, 0x48,
+};
+
+const SubSample kSubSamples[] = {{162, 0}, {0, 184}, {0, 184}};
+
+const uint8_t kInBinaryBuffer[] = {
+        0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
+        0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
+        0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
+        0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8,
+        0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72,
+        0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d,
+        0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63,
+        0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30,
+        0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+        0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f,
+        0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x6e, 0x45, 0x21,
+        0x82, 0x38, 0xf0, 0x9d, 0x7d, 0x96, 0xe6, 0x94, 0xae, 0xe2, 0x87, 0x8f, 0x04, 0x49, 0xe5,
+        0xf6, 0x8c, 0x8b, 0x9a, 0x10, 0x18, 0xba, 0x94, 0xe9, 0x22, 0x31, 0x04, 0x7e, 0x60, 0x5b,
+        0xc4, 0x24, 0x00, 0x90, 0x62, 0x0d, 0xdc, 0x85, 0x74, 0x75, 0x78, 0xd0, 0x14, 0x08, 0xcb,
+        0x02, 0x1d, 0x7d, 0x9d, 0x34, 0xe8, 0x81, 0xb9, 0xf7, 0x09, 0x28, 0x79, 0x29, 0x8d, 0xe3,
+        0x14, 0xed, 0x5f, 0xca, 0xaf, 0xf4, 0x1c, 0x49, 0x15, 0xe1, 0x80, 0x29, 0x61, 0x76, 0x80,
+        0x43, 0xf8, 0x58, 0x53, 0x40, 0xd7, 0x31, 0x6d, 0x61, 0x81, 0x41, 0xe9, 0x77, 0x9f, 0x9c,
+        0xe1, 0x6d, 0xf2, 0xee, 0xd9, 0xc8, 0x67, 0xd2, 0x5f, 0x48, 0x73, 0xe3, 0x5c, 0xcd, 0xa7,
+        0x45, 0x58, 0xbb, 0xdd, 0x28, 0x1d, 0x68, 0xfc, 0xb4, 0xc6, 0xf6, 0x92, 0xf6, 0x30, 0x03,
+        0xaa, 0xe4, 0x32, 0xf6, 0x34, 0x51, 0x4b, 0x0f, 0x8c, 0xf9, 0xac, 0x98, 0x22, 0xfb, 0x49,
+        0xc8, 0xbf, 0xca, 0x8c, 0x80, 0x86, 0x5d, 0xd7, 0xa4, 0x52, 0xb1, 0xd9, 0xa6, 0x04, 0x4e,
+        0xb3, 0x2d, 0x1f, 0xb8, 0x35, 0xcc, 0x45, 0x6d, 0x9c, 0x20, 0xa7, 0xa4, 0x34, 0x59, 0x72,
+        0xe3, 0xae, 0xba, 0x49, 0xde, 0xd1, 0xaa, 0xee, 0x3d, 0x77, 0xfc, 0x5d, 0xc6, 0x1f, 0x9d,
+        0xac, 0xc2, 0x15, 0x66, 0xb8, 0xe1, 0x54, 0x4e, 0x74, 0x93, 0xdb, 0x9a, 0x24, 0x15, 0x6e,
+        0x20, 0xa3, 0x67, 0x3e, 0x5a, 0x24, 0x41, 0x5e, 0xb0, 0xe6, 0x35, 0x87, 0x1b, 0xc8, 0x7a,
+        0xf9, 0x77, 0x65, 0xe0, 0x01, 0xf2, 0x4c, 0xe4, 0x2b, 0xa9, 0x64, 0x96, 0x96, 0x0b, 0x46,
+        0xca, 0xea, 0x79, 0x0e, 0x78, 0xa3, 0x5f, 0x43, 0xfc, 0x47, 0x6a, 0x12, 0xfa, 0xc4, 0x33,
+        0x0e, 0x88, 0x1c, 0x19, 0x3a, 0x00, 0xc3, 0x4e, 0xb5, 0xd8, 0xfa, 0x8e, 0xf1, 0xbc, 0x3d,
+        0xb2, 0x7e, 0x50, 0x8d, 0x67, 0xc3, 0x6b, 0xed, 0xe2, 0xea, 0xa6, 0x1f, 0x25, 0x24, 0x7c,
+        0x94, 0x74, 0x50, 0x49, 0xe3, 0xc6, 0x58, 0x2e, 0xfd, 0x28, 0xb4, 0xc6, 0x73, 0xb1, 0x53,
+        0x74, 0x27, 0x94, 0x5c, 0xdf, 0x69, 0xb7, 0xa1, 0xd7, 0xf5, 0xd3, 0x8a, 0x2c, 0x2d, 0xb4,
+        0x5e, 0x8a, 0x16, 0x14, 0x54, 0x64, 0x6e, 0x00, 0x6b, 0x11, 0x59, 0x8a, 0x63, 0x38, 0x80,
+        0x76, 0xc3, 0xd5, 0x59, 0xf7, 0x3f, 0xd2, 0xfa, 0xa5, 0xca, 0x82, 0xff, 0x4a, 0x62, 0xf0,
+        0xe3, 0x42, 0xf9, 0x3b, 0x38, 0x27, 0x8a, 0x89, 0xaa, 0x50, 0x55, 0x4b, 0x29, 0xf1, 0x46,
+        0x7c, 0x75, 0xef, 0x65, 0xaf, 0x9b, 0x0d, 0x6d, 0xda, 0x25, 0x94, 0x14, 0xc1, 0x1b, 0xf0,
+        0xc5, 0x4c, 0x24, 0x0e, 0x65,
+};
+
+const uint8_t kOutRefBinaryBuffer[] = {
+        0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
+        0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
+        0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
+        0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8,
+        0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72,
+        0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d,
+        0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63,
+        0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30,
+        0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+        0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f,
+        0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20,
+        0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d,
+        0x30, 0x20, 0x72, 0x65, 0x66, 0x3d, 0x32, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+        0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x65, 0x3d,
+        0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, 0x6d, 0x65, 0x3d, 0x68, 0x65,
+        0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70, 0x73, 0x79, 0x3d, 0x31,
+        0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x30, 0x3a, 0x30, 0x2e,
+        0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x3d, 0x31, 0x20,
+        0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72,
+        0x6f, 0x6d, 0x61, 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69,
+        0x73, 0x3d, 0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x71,
+        0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31,
+        0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x73, 0x6b, 0x69, 0x70, 0x3d,
+        0x31, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66,
+        0x73, 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d,
+        0x36, 0x30, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x68,
+        0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x35, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x64, 0x5f,
+        0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20,
+        0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x69, 0x6e, 0x74, 0x65,
+        0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c, 0x75, 0x72, 0x61, 0x79,
+        0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74,
+        0x72, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x3d, 0x30, 0x20,
+        0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x30, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68,
+        0x74, 0x70, 0x3d, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x3d, 0x32, 0x35, 0x30,
+        0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d, 0x32, 0x35, 0x20,
+        0x73, 0x63, 0x65, 0x6e, 0x65,
+};
+
+class MediaCasListener : public BnCasListener {
+  public:
+    virtual ScopedAStatus onEvent(int32_t event, int32_t arg,
+                                  const vector<uint8_t>& data) override {
+        Mutex::Autolock autoLock(mMsgLock);
+        mEvent = event;
+        mEventArg = arg;
+        mEventData = data;
+
+        mEventReceived = true;
+        mMsgCondition.signal();
+        return ScopedAStatus::ok();
+    }
+
+    virtual ScopedAStatus onSessionEvent(const vector<uint8_t>& sessionId, int32_t event,
+                                         int32_t arg, const vector<uint8_t>& data) override {
+        Mutex::Autolock autoLock(mMsgLock);
+        mSessionId = sessionId;
+        mEvent = event;
+        mEventArg = arg;
+        mEventData = data;
+
+        mEventReceived = true;
+        mMsgCondition.signal();
+        return ScopedAStatus::ok();
+    }
+
+    virtual ScopedAStatus onStatusUpdate(StatusEvent event, int32_t arg) override {
+        Mutex::Autolock autoLock(mMsgLock);
+        mStatusEvent = event;
+        mEventArg = arg;
+
+        mEventReceived = true;
+        mMsgCondition.signal();
+        return ScopedAStatus::ok();
+    }
+
+    void testEventEcho(shared_ptr<ICas>& mediaCas, int32_t& event, int32_t& eventArg,
+                       vector<uint8_t>& eventData);
+
+    void testSessionEventEcho(shared_ptr<ICas>& mediaCas, const vector<uint8_t>& sessionId,
+                              int32_t& event, int32_t& eventArg, vector<uint8_t>& eventData);
+
+    void testStatusUpdate(shared_ptr<ICas>& mediaCas, vector<uint8_t>* sessionId,
+                          SessionIntent intent, ScramblingMode mode);
+
+  private:
+    int32_t mEvent = -1;
+    int32_t mEventArg = -1;
+    StatusEvent mStatusEvent;
+    bool mEventReceived = false;
+    vector<uint8_t> mEventData;
+    vector<uint8_t> mSessionId;
+    Mutex mMsgLock;
+    android::Condition mMsgCondition;
+};
+
+void MediaCasListener::testEventEcho(shared_ptr<ICas>& mediaCas, int32_t& event, int32_t& eventArg,
+                                     vector<uint8_t>& eventData) {
+    mEventReceived = false;
+    auto returnStatus = mediaCas->sendEvent(event, eventArg, eventData);
+    EXPECT_TRUE(returnStatus.isOk());
+
+    Mutex::Autolock autoLock(mMsgLock);
+    while (!mEventReceived) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            ADD_FAILURE() << "event not received within timeout";
+            return;
+        }
+    }
+
+    EXPECT_EQ(mEvent, event);
+    EXPECT_EQ(mEventArg, eventArg);
+    EXPECT_TRUE(mEventData == eventData);
+}
+
+void MediaCasListener::testSessionEventEcho(shared_ptr<ICas>& mediaCas,
+                                            const vector<uint8_t>& sessionId, int32_t& event,
+                                            int32_t& eventArg, vector<uint8_t>& eventData) {
+    mEventReceived = false;
+    EXPECT_TRUE(mediaCas->sendSessionEvent(sessionId, event, eventArg, eventData).isOk());
+
+    Mutex::Autolock autoLock(mMsgLock);
+    while (!mEventReceived) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            ADD_FAILURE() << "event not received within timeout";
+            return;
+        }
+    }
+
+    EXPECT_TRUE(mSessionId == sessionId);
+    EXPECT_EQ(mEvent, event);
+    EXPECT_EQ(mEventArg, eventArg);
+    EXPECT_TRUE(mEventData == eventData);
+}
+
+void MediaCasListener::testStatusUpdate(shared_ptr<ICas>& mediaCas, vector<uint8_t>* sessionId,
+                                        SessionIntent intent, ScramblingMode mode) {
+    mEventReceived = false;
+    EXPECT_TRUE(mediaCas->openSession(intent, mode, sessionId).isOk());
+
+    Mutex::Autolock autoLock(mMsgLock);
+    while (!mEventReceived) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            ADD_FAILURE() << "event not received within timeout";
+            return;
+        }
+    }
+    EXPECT_EQ(mStatusEvent, static_cast<StatusEvent>(intent));
+    EXPECT_EQ(mEventArg, static_cast<int32_t>(mode));
+}
+
+class MediaCasAidlTest : public testing::TestWithParam<string> {
+  public:
+    virtual void SetUp() override {
+        if (AServiceManager_isDeclared(GetParam().c_str())) {
+            SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+            mService = IMediaCasService::fromBinder(binder);
+        } else {
+            mService = nullptr;
+        }
+        ASSERT_NE(mService, nullptr);
+    }
+
+    shared_ptr<IMediaCasService> mService = nullptr;
+
+  protected:
+    static void description(const string& description) {
+        RecordProperty("description", description);
+    }
+
+    shared_ptr<ICas> mMediaCas;
+    shared_ptr<IDescrambler> mDescrambler;
+    shared_ptr<MediaCasListener> mCasListener;
+    typedef struct _OobInputTestParams {
+        const SubSample* subSamples;
+        int32_t numSubSamples;
+        int64_t imemSizeActual;
+        int64_t imemOffset;
+        int64_t imemSize;
+        int64_t srcOffset;
+        int64_t dstOffset;
+    } OobInputTestParams;
+
+    AssertionResult createCasPlugin(int32_t caSystemId);
+    AssertionResult openCasSession(vector<uint8_t>* sessionId, SessionIntent intent,
+                                   ScramblingMode mode);
+    AssertionResult descrambleTestInputBuffer(const shared_ptr<IDescrambler>& descrambler,
+                                              ScopedAStatus& descrambleStatus, uint8_t*& inMemory);
+    AssertionResult descrambleTestOobInput(const shared_ptr<IDescrambler>& descrambler,
+                                           ScopedAStatus& descrambleStatus,
+                                           const OobInputTestParams& params);
+};
+
+AssertionResult MediaCasAidlTest::createCasPlugin(int32_t caSystemId) {
+    bool isSystemIdSupported;
+    auto status = mService->isSystemIdSupported(caSystemId, &isSystemIdSupported);
+    bool skipDescrambler = false;
+    if (!status.isOk() || !isSystemIdSupported) {
+        return AssertionFailure();
+    }
+    bool isDescramblerSupported;
+    status = mService->isDescramblerSupported(caSystemId, &isDescramblerSupported);
+    if (!status.isOk() || !isDescramblerSupported) {
+        ALOGI("Skip Descrambler test since it's not required in cas.");
+        mDescrambler = nullptr;
+        skipDescrambler = true;
+    }
+
+    mCasListener = SharedRefBase::make<MediaCasListener>();
+    status = mService->createPlugin(caSystemId, mCasListener, &mMediaCas);
+    if (!status.isOk()) {
+        return AssertionFailure();
+    }
+    if (mMediaCas == nullptr) {
+        return AssertionFailure();
+    }
+
+    if (skipDescrambler) {
+        return AssertionSuccess();
+    }
+
+    status = mService->createDescrambler(caSystemId, &mDescrambler);
+    if (!status.isOk()) {
+        return AssertionFailure();
+    }
+
+    return AssertionResult(mDescrambler != nullptr);
+}
+
+AssertionResult MediaCasAidlTest::openCasSession(vector<uint8_t>* sessionId, SessionIntent intent,
+                                                 ScramblingMode mode) {
+    return AssertionResult(mMediaCas->openSession(intent, mode, sessionId).isOk());
+}
+
+AssertionResult MediaCasAidlTest::descrambleTestInputBuffer(
+        const shared_ptr<IDescrambler>& descrambler, ScopedAStatus& descrambleStatus,
+        uint8_t*& sharedMemory) {
+    vector<SubSample> subSample(kSubSamples,
+                                kSubSamples + (sizeof(kSubSamples) / sizeof(SubSample)));
+
+    int size = sizeof(kInBinaryBuffer);
+    auto fd = ashmem_create_region("vts-cas", size);
+    if (fd < 0) {
+        ALOGE("ashmem_create_region failed");
+        return AssertionFailure();
+    }
+
+    sharedMemory =
+            static_cast<uint8_t*>(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
+    if (sharedMemory == reinterpret_cast<uint8_t*>(MAP_FAILED)) {
+        ALOGE("mmap failed");
+        return AssertionFailure();
+    }
+
+    memcpy(sharedMemory, kInBinaryBuffer, size);
+
+    auto dupFd = dup(fd);
+
+    SharedBuffer srcBuffer = {.heapBase.fd = ScopedFileDescriptor(std::move(fd)),
+                              .heapBase.size = size,
+                              .offset = 0,
+                              .size = size};
+
+    SharedBuffer dupBuffer = {.heapBase.fd = ScopedFileDescriptor(dupFd),
+                              .heapBase.size = size,
+                              .offset = 0,
+                              .size = size};
+
+    DestinationBuffer dstBuffer;
+    dstBuffer.set<DestinationBuffer::nonsecureMemory>(std::move(dupBuffer));
+
+    int32_t outBytes;
+    descrambleStatus = descrambler->descramble(ScramblingControl::EVENKEY /*2*/, subSample,
+                                               srcBuffer, 0, dstBuffer, 0, &outBytes);
+    if (!descrambleStatus.isOk()) {
+        ALOGI("descramble failed, status=%d, outBytes=%u, error=%s", descrambleStatus.getStatus(),
+              outBytes, descrambleStatus.getDescription().c_str());
+    }
+    return AssertionResult(descrambleStatus.isOk());
+}
+
+AssertionResult MediaCasAidlTest::descrambleTestOobInput(
+        const shared_ptr<IDescrambler>& descrambler, ScopedAStatus& descrambleStatus,
+        const OobInputTestParams& params) {
+    vector<SubSample> subSample(params.subSamples, params.subSamples + params.numSubSamples);
+
+    auto fd = ashmem_create_region("vts-cas", params.imemSizeActual);
+    if (fd < 0) {
+        ALOGE("ashmem_create_region failed");
+        return AssertionFailure();
+    }
+
+    auto dupFd = dup(fd);
+
+    SharedBuffer srcBuffer = {.heapBase.fd = ScopedFileDescriptor(std::move(fd)),
+                              .heapBase.size = params.imemSizeActual,
+                              .offset = params.imemOffset,
+                              .size = params.imemSize};
+
+    SharedBuffer dupBuffer = {.heapBase.fd = ScopedFileDescriptor(dupFd),
+                              .heapBase.size = params.imemSizeActual,
+                              .offset = params.imemOffset,
+                              .size = params.imemSize};
+
+    DestinationBuffer dstBuffer;
+    dstBuffer.set<DestinationBuffer::nonsecureMemory>(std::move(dupBuffer));
+
+    int32_t outBytes;
+    descrambleStatus =
+            descrambler->descramble(ScramblingControl::EVENKEY /*2*/, subSample, srcBuffer,
+                                    params.srcOffset, dstBuffer, params.dstOffset, &outBytes);
+    if (!descrambleStatus.isOk()) {
+        ALOGI("descramble failed, status=%d, outBytes=%u, error=%s", descrambleStatus.getStatus(),
+              outBytes, descrambleStatus.getDescription().c_str());
+    }
+    return AssertionResult(descrambleStatus.isOk());
+}
+
+TEST_P(MediaCasAidlTest, EnumeratePlugins) {
+    description("Test enumerate plugins");
+
+    vector<AidlCasPluginDescriptor> descriptors;
+    EXPECT_TRUE(mService->enumeratePlugins(&descriptors).isOk());
+
+    if (descriptors.size() == 0) {
+        ALOGW("[   WARN   ] enumeratePlugins list empty");
+        return;
+    }
+
+    for (size_t i = 0; i < descriptors.size(); i++) {
+        int32_t caSystemId = descriptors[i].caSystemId;
+
+        ASSERT_TRUE(createCasPlugin(caSystemId));
+    }
+}
+
+TEST_P(MediaCasAidlTest, TestInvalidSystemIdFails) {
+    description("Test failure for invalid system ID");
+
+    bool isSystemIdSupported;
+    auto status = mService->isSystemIdSupported(INVALID_SYSTEM_ID, &isSystemIdSupported);
+
+    EXPECT_TRUE(status.isOk());
+    ASSERT_FALSE(isSystemIdSupported);
+
+    bool isDescramblerSupported;
+    status = mService->isDescramblerSupported(INVALID_SYSTEM_ID, &isDescramblerSupported);
+
+    EXPECT_TRUE(status.isOk());
+    ASSERT_FALSE(isDescramblerSupported);
+
+    shared_ptr<ICas> mediaCas;
+    shared_ptr<MediaCasListener> casListener = SharedRefBase::make<MediaCasListener>();
+    status = mService->createPlugin(INVALID_SYSTEM_ID, casListener, &mediaCas);
+    ASSERT_TRUE(status.isOk());
+    EXPECT_EQ(mediaCas, nullptr);
+
+    shared_ptr<IDescrambler> descrambler;
+    status = mService->createDescrambler(INVALID_SYSTEM_ID, &descrambler);
+    ASSERT_TRUE(status.isOk());
+    EXPECT_EQ(descrambler, nullptr);
+}
+
+TEST_P(MediaCasAidlTest, TestClearKeyPluginInstalled) {
+    description("Test if ClearKey plugin is installed");
+
+    vector<AidlCasPluginDescriptor> descriptors;
+    EXPECT_TRUE(mService->enumeratePlugins(&descriptors).isOk());
+
+    if (descriptors.size() == 0) {
+        ALOGW("[   WARN   ] enumeratePlugins list empty");
+    }
+
+    for (size_t i = 0; i < descriptors.size(); i++) {
+        int32_t caSystemId = descriptors[i].caSystemId;
+        if (CLEAR_KEY_SYSTEM_ID == caSystemId) {
+            return;
+        }
+    }
+
+    ADD_FAILURE() << "ClearKey plugin not installed";
+}
+
+TEST_P(MediaCasAidlTest, TestClearKeySessionClosedAfterRelease) {
+    description("Test that all sessions are closed after a MediaCas object is released");
+
+    ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID));
+
+    EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk());
+
+    SessionIntent intent = SessionIntent::LIVE;
+    ScramblingMode mode = ScramblingMode::DVB_CSA1;
+
+    vector<uint8_t> sessionId;
+    ASSERT_TRUE(openCasSession(&sessionId, intent, mode));
+
+    vector<uint8_t> streamSessionId;
+    ASSERT_TRUE(openCasSession(&streamSessionId, intent, mode));
+
+    EXPECT_TRUE(mMediaCas->release().isOk());
+
+    if (mDescrambler != nullptr) {
+        auto status = mDescrambler->setMediaCasSession(sessionId);
+        EXPECT_FALSE(status.isOk());
+        EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, status.getServiceSpecificError());
+
+        status = mDescrambler->setMediaCasSession(streamSessionId);
+        EXPECT_FALSE(status.isOk());
+        EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, status.getServiceSpecificError());
+    }
+}
+
+TEST_P(MediaCasAidlTest, TestClearKeyErrors) {
+    description("Test that invalid call sequences fail with expected error codes");
+
+    ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID));
+
+    /*
+     * Test MediaCas error codes
+     */
+    // Provision should fail with an invalid asset string
+    auto returnStatus = mMediaCas->provision("invalid asset string");
+    EXPECT_FALSE(returnStatus.isOk());
+    EXPECT_EQ(Status::ERROR_CAS_NO_LICENSE, returnStatus.getServiceSpecificError());
+
+    SessionIntent intent = SessionIntent::LIVE;
+    ScramblingMode mode = ScramblingMode::DVB_CSA1;
+
+    // Open a session, then close it so that it should become invalid
+    vector<uint8_t> invalidSessionId;
+    ASSERT_TRUE(openCasSession(&invalidSessionId, intent, mode));
+    EXPECT_TRUE(mMediaCas->closeSession(invalidSessionId).isOk());
+
+    // processEcm should fail with an invalid session id
+    vector<uint8_t> ecm(kEcmBinaryBuffer, kEcmBinaryBuffer + sizeof(kEcmBinaryBuffer));
+    returnStatus = mMediaCas->processEcm(invalidSessionId, ecm);
+    EXPECT_FALSE(returnStatus.isOk());
+    EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, returnStatus.getServiceSpecificError());
+
+    vector<uint8_t> sessionId;
+    ASSERT_TRUE(openCasSession(&sessionId, intent, mode));
+
+    // processEcm should fail without provisioning
+    returnStatus = mMediaCas->processEcm(sessionId, ecm);
+    EXPECT_FALSE(returnStatus.isOk());
+    EXPECT_EQ(Status::ERROR_CAS_NOT_PROVISIONED, returnStatus.getServiceSpecificError());
+
+    EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk());
+
+    // processEcm should fail with ecm with bad descriptor count
+    ecm[17] = 0x03;  // change the descriptor count field to 3 (invalid)
+    returnStatus = mMediaCas->processEcm(sessionId, ecm);
+    EXPECT_FALSE(returnStatus.isOk());
+    EXPECT_EQ(Status::ERROR_CAS_UNKNOWN, returnStatus.getServiceSpecificError());
+
+    // processEcm should fail with ecm buffer that's too short
+    ecm.resize(8);
+    returnStatus = mMediaCas->processEcm(sessionId, ecm);
+    EXPECT_FALSE(returnStatus.isOk());
+    EXPECT_EQ(Status::BAD_VALUE, returnStatus.getServiceSpecificError());
+
+    if (mDescrambler != nullptr) {
+        /*
+         * Test MediaDescrambler error codes
+         */
+        // setMediaCasSession should fail with an invalid session id
+        returnStatus = mDescrambler->setMediaCasSession(invalidSessionId);
+        EXPECT_FALSE(returnStatus.isOk());
+        EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, returnStatus.getServiceSpecificError());
+
+        // descramble should fail without a valid session
+        ScopedAStatus descrambleStatus = ScopedAStatus::ok();
+        uint8_t* sharedBuffer = nullptr;
+
+        ASSERT_FALSE(descrambleTestInputBuffer(mDescrambler, descrambleStatus, sharedBuffer));
+        EXPECT_EQ(Status::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED,
+                  descrambleStatus.getServiceSpecificError());
+
+        // Now set a valid session, should still fail because no valid ecm is processed
+        EXPECT_TRUE(mDescrambler->setMediaCasSession(sessionId).isOk());
+        ASSERT_FALSE(descrambleTestInputBuffer(mDescrambler, descrambleStatus, sharedBuffer));
+        EXPECT_EQ(Status::ERROR_CAS_DECRYPT, descrambleStatus.getServiceSpecificError());
+
+        // Verify that requiresSecureDecoderComponent handles empty mime
+        bool requiresSecureDecoderComponent = true;
+        EXPECT_TRUE(
+                mDescrambler->requiresSecureDecoderComponent("", &requiresSecureDecoderComponent)
+                        .isOk());
+        EXPECT_FALSE(requiresSecureDecoderComponent);
+
+        // Verify that requiresSecureDecoderComponent handles invalid mime
+        requiresSecureDecoderComponent = true;
+        EXPECT_TRUE(
+                mDescrambler->requiresSecureDecoderComponent("bad", &requiresSecureDecoderComponent)
+                        .isOk());
+        EXPECT_FALSE(requiresSecureDecoderComponent);
+    }
+}
+
+TEST_P(MediaCasAidlTest, TestClearKeyApisWithSession) {
+    description("Test that valid call sequences with SessionEvent send and receive");
+
+    ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID));
+
+    EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk());
+
+    vector<uint8_t> pvtData;
+    pvtData.resize(256);
+    EXPECT_TRUE(mMediaCas->setPrivateData(pvtData).isOk());
+
+    SessionIntent intent = SessionIntent::LIVE;
+    ScramblingMode mode = ScramblingMode::DVB_CSA1;
+
+    vector<uint8_t> sessionId;
+    ASSERT_TRUE(openCasSession(&sessionId, intent, mode));
+    EXPECT_TRUE(mMediaCas->setSessionPrivateData(sessionId, pvtData).isOk());
+
+    vector<uint8_t> streamSessionId;
+    ASSERT_TRUE(openCasSession(&streamSessionId, intent, mode));
+    EXPECT_TRUE(mMediaCas->setSessionPrivateData(streamSessionId, pvtData).isOk());
+
+    if (mDescrambler != nullptr) {
+        EXPECT_TRUE(mDescrambler->setMediaCasSession(sessionId).isOk());
+        EXPECT_TRUE(mDescrambler->setMediaCasSession(streamSessionId).isOk());
+    }
+
+    vector<uint8_t> nullPtrVector(0);
+    EXPECT_TRUE(mMediaCas->refreshEntitlements(3, nullPtrVector).isOk());
+
+    vector<uint8_t> refreshData{0, 1, 2, 3};
+    EXPECT_TRUE(mMediaCas->refreshEntitlements(10, refreshData).isOk());
+
+    int32_t eventID = 1;
+    int32_t eventArg = 2;
+    mCasListener->testEventEcho(mMediaCas, eventID, eventArg, nullPtrVector);
+    mCasListener->testSessionEventEcho(mMediaCas, sessionId, eventID, eventArg, nullPtrVector);
+
+    eventID = 3;
+    eventArg = 4;
+    vector<uint8_t> eventData{'e', 'v', 'e', 'n', 't', 'd', 'a', 't', 'a'};
+    mCasListener->testEventEcho(mMediaCas, eventID, eventArg, eventData);
+    mCasListener->testSessionEventEcho(mMediaCas, sessionId, eventID, eventArg, eventData);
+
+    mCasListener->testStatusUpdate(mMediaCas, &sessionId, intent, mode);
+
+    vector<uint8_t> clearKeyEmmData{'c', 'l', 'e', 'a', 'r', 'k', 'e', 'y', 'e', 'm', 'm'};
+    EXPECT_TRUE(mMediaCas->processEmm(clearKeyEmmData).isOk());
+
+    vector<uint8_t> ecm(kEcmBinaryBuffer, kEcmBinaryBuffer + sizeof(kEcmBinaryBuffer));
+    EXPECT_TRUE(mMediaCas->processEcm(sessionId, ecm).isOk());
+    EXPECT_TRUE(mMediaCas->processEcm(streamSessionId, ecm).isOk());
+
+    if (mDescrambler != nullptr) {
+        bool requiresSecureDecoderComponent = true;
+        EXPECT_TRUE(mDescrambler
+                            ->requiresSecureDecoderComponent("video/avc",
+                                                             &requiresSecureDecoderComponent)
+                            .isOk());
+        EXPECT_FALSE(requiresSecureDecoderComponent);
+
+        ScopedAStatus descrambleStatus = ScopedAStatus::ok();
+        uint8_t* sharedBuffer = nullptr;
+
+        ASSERT_TRUE(descrambleTestInputBuffer(mDescrambler, descrambleStatus, sharedBuffer));
+
+        int compareResult =
+                memcmp(static_cast<const void*>(sharedBuffer),
+                       static_cast<const void*>(kOutRefBinaryBuffer), sizeof(kOutRefBinaryBuffer));
+        EXPECT_EQ(0, compareResult);
+
+        EXPECT_TRUE(mDescrambler->release().isOk());
+    }
+
+    EXPECT_TRUE(mMediaCas->release().isOk());
+}
+
+TEST_P(MediaCasAidlTest, TestClearKeyOobFails) {
+    description("Test that oob descramble request fails with expected error");
+
+    ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID));
+    EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk());
+
+    SessionIntent intent = SessionIntent::LIVE;
+    ScramblingMode mode = ScramblingMode::DVB_CSA1;
+
+    vector<uint8_t> sessionId;
+    ASSERT_TRUE(openCasSession(&sessionId, intent, mode));
+
+    if (mDescrambler != nullptr) {
+        EXPECT_TRUE(mDescrambler->setMediaCasSession(sessionId).isOk());
+    }
+
+    vector<uint8_t> ecm(kEcmBinaryBuffer, kEcmBinaryBuffer + sizeof(kEcmBinaryBuffer));
+    EXPECT_TRUE(mMediaCas->processEcm(sessionId, ecm).isOk());
+
+    if (mDescrambler != nullptr) {
+        ScopedAStatus descrambleStatus = ScopedAStatus::ok();
+
+        // test invalid src buffer offset
+        ASSERT_FALSE(
+                descrambleTestOobInput(mDescrambler, descrambleStatus,
+                                       {.subSamples = kSubSamples,
+                                        .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample),
+                                        .imemSizeActual = sizeof(kInBinaryBuffer),
+                                        .imemOffset = 0xcccccc,
+                                        .imemSize = sizeof(kInBinaryBuffer),
+                                        .srcOffset = 0,
+                                        .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test invalid src buffer size
+        ASSERT_FALSE(
+                descrambleTestOobInput(mDescrambler, descrambleStatus,
+                                       {.subSamples = kSubSamples,
+                                        .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample),
+                                        .imemSizeActual = sizeof(kInBinaryBuffer),
+                                        .imemOffset = 0,
+                                        .imemSize = 0xcccccc,
+                                        .srcOffset = 0,
+                                        .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test invalid src buffer size
+        ASSERT_FALSE(
+                descrambleTestOobInput(mDescrambler, descrambleStatus,
+                                       {.subSamples = kSubSamples,
+                                        .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample),
+                                        .imemSizeActual = sizeof(kInBinaryBuffer),
+                                        .imemOffset = 1,
+                                        .imemSize = -1,
+                                        .srcOffset = 0,
+                                        .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test invalid srcOffset
+        ASSERT_FALSE(
+                descrambleTestOobInput(mDescrambler, descrambleStatus,
+                                       {.subSamples = kSubSamples,
+                                        .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample),
+                                        .imemSizeActual = sizeof(kInBinaryBuffer),
+                                        .imemOffset = 0,
+                                        .imemSize = sizeof(kInBinaryBuffer),
+                                        .srcOffset = 0xcccccc,
+                                        .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test invalid dstOffset
+        ASSERT_FALSE(
+                descrambleTestOobInput(mDescrambler, descrambleStatus,
+                                       {.subSamples = kSubSamples,
+                                        .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample),
+                                        .imemSizeActual = sizeof(kInBinaryBuffer),
+                                        .imemOffset = 0,
+                                        .imemSize = sizeof(kInBinaryBuffer),
+                                        .srcOffset = 0,
+                                        .dstOffset = 0xcccccc}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test detection of oob subsample sizes
+        const SubSample invalidSubSamples1[] = {{162, 0}, {0, 184}, {0, 0xdddddd}};
+
+        ASSERT_FALSE(descrambleTestOobInput(
+                mDescrambler, descrambleStatus,
+                {.subSamples = invalidSubSamples1,
+                 .numSubSamples = sizeof(invalidSubSamples1) / sizeof(SubSample),
+                 .imemSizeActual = sizeof(kInBinaryBuffer),
+                 .imemOffset = 0,
+                 .imemSize = sizeof(kInBinaryBuffer),
+                 .srcOffset = 0,
+                 .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        // test detection of overflowing subsample sizes
+        const SubSample invalidSubSamples2[] = {{162, 0}, {0, 184}, {2, -1}};
+
+        ASSERT_FALSE(descrambleTestOobInput(
+                mDescrambler, descrambleStatus,
+                {.subSamples = invalidSubSamples2,
+                 .numSubSamples = sizeof(invalidSubSamples2) / sizeof(SubSample),
+                 .imemSizeActual = sizeof(kInBinaryBuffer),
+                 .imemOffset = 0,
+                 .imemSize = sizeof(kInBinaryBuffer),
+                 .srcOffset = 0,
+                 .dstOffset = 0}));
+        EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError());
+
+        EXPECT_TRUE(mDescrambler->release().isOk());
+    }
+    EXPECT_TRUE(mMediaCas->release().isOk());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MediaCasAidlTest);
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, MediaCasAidlTest,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IMediaCasService::descriptor)),
+        android::PrintInstanceNameToString);
+
+// Start thread pool to receive callbacks from AIDL service.
+int main(int argc, char** argv) {
+    InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}