From 7bdf28d2b83e527f474e96b0984d6a3f5eb457f7 Mon Sep 17 00:00:00 2001 From: Edwin Wong Date: Thu, 15 May 2014 17:24:57 -0700 Subject: JsonWebKey support. Parses JSON Web Key Set in the response data; extracts and base64 decode key id(s) and key(s). bug: 12035506 Change-Id: Ib71bce942d6eca1e569dfad0a9adb6dee1cdf75e --- drm/mediadrm/plugins/clearkey/JsonWebKey.cpp | 269 +++++++++++++++++ drm/mediadrm/plugins/clearkey/JsonWebKey.h | 62 ++++ drm/mediadrm/plugins/clearkey/tests/Android.mk | 26 ++ .../plugins/clearkey/tests/JsonWebKeyUnittest.cpp | 321 +++++++++++++++++++++ drm/mediadrm/plugins/clearkey/tests/unit-test.mk | 51 ++++ 5 files changed, 729 insertions(+) create mode 100644 drm/mediadrm/plugins/clearkey/JsonWebKey.cpp create mode 100644 drm/mediadrm/plugins/clearkey/JsonWebKey.h create mode 100644 drm/mediadrm/plugins/clearkey/tests/Android.mk create mode 100644 drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp create mode 100644 drm/mediadrm/plugins/clearkey/tests/unit-test.mk (limited to 'drm') diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp new file mode 100644 index 0000000..53ffae4 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2014 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 "JsonWebKey" + +#include +#include +#include +#include + +#include "JsonWebKey.h" + +namespace { +const android::String8 kKeysTag("keys"); +const android::String8 kKeyTypeTag("kty"); +const android::String8 kSymmetricKeyValue("oct"); +const android::String8 kKeyTag("k"); +const android::String8 kKeyIdTag("kid"); +const android::String8 kBase64Padding("="); +} + +namespace clearkeydrm { + +using android::ABuffer; +using android::AString; + +JsonWebKey::JsonWebKey() { +} + +JsonWebKey::~JsonWebKey() { +} + +/* + * Parses a JSON Web Key Set string, initializes a KeyMap with key id:key + * pairs from the JSON Web Key Set. Both key ids and keys are base64url + * encoded. The KeyMap contains base64url decoded key id:key pairs. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet, + KeyMap* keys) { + + keys->clear(); + if (!parseJsonWebKeySet(jsonWebKeySet, &mJsonObjects)) { + return false; + } + + // mJsonObjects[0] contains the entire JSON Web Key Set, including + // all the base64 encoded keys. Each key is also stored separately as + // a JSON object in mJsonObjects[1..n] where n is the total + // number of keys in the set. + if (!isJsonWebKeySet(mJsonObjects[0])) { + return false; + } + + String8 encodedKey, encodedKeyId; + Vector decodedKey, decodedKeyId; + + // mJsonObjects[1] contains the first JSON Web Key in the set + for (size_t i = 1; i < mJsonObjects.size(); ++i) { + encodedKeyId.clear(); + encodedKey.clear(); + + if (!parseJsonObject(mJsonObjects[i], &mTokens)) + return false; + + if (findKey(mJsonObjects[i], &encodedKeyId, &encodedKey)) { + if (encodedKeyId.isEmpty() || encodedKey.isEmpty()) { + ALOGE("Must have both key id and key in the JsonWebKey set."); + continue; + } + + if (!decodeBase64String(encodedKeyId, &decodedKeyId)) { + ALOGE("Failed to decode key id(%s)", encodedKeyId.string()); + continue; + } + + if (!decodeBase64String(encodedKey, &decodedKey)) { + ALOGE("Failed to decode key(%s)", encodedKey.string()); + continue; + } + + keys->add(decodedKeyId, decodedKey); + } + } + return true; +} + +bool JsonWebKey::decodeBase64String(const String8& encodedText, + Vector* decodedText) { + + decodedText->clear(); + + // encodedText should not contain padding characters as per EME spec. + if (encodedText.find(kBase64Padding) != -1) { + return false; + } + + // Since android::decodeBase64() requires padding characters, + // add them so length of encodedText is exactly a multiple of 4. + int remainder = encodedText.length() % 4; + String8 paddedText(encodedText); + if (remainder > 0) { + for (int i = 0; i < 4 - remainder; ++i) { + paddedText.append(kBase64Padding); + } + } + + android::sp buffer = + android::decodeBase64(AString(paddedText.string())); + if (buffer == NULL) { + ALOGE("Malformed base64 encoded content found."); + return false; + } + + decodedText->appendArray(buffer->base(), buffer->size()); + return true; +} + +bool JsonWebKey::findKey(const String8& jsonObject, String8* keyId, + String8* encodedKey) { + + String8 key, value; + + // Only allow symmetric key, i.e. "kty":"oct" pair. + if (jsonObject.find(kKeyTypeTag) >= 0) { + findValue(kKeyTypeTag, &value); + if (0 != value.compare(kSymmetricKeyValue)) + return false; + } + + if (jsonObject.find(kKeyIdTag) >= 0) { + findValue(kKeyIdTag, keyId); + } + + if (jsonObject.find(kKeyTag) >= 0) { + findValue(kKeyTag, encodedKey); + } + return true; +} + +void JsonWebKey::findValue(const String8 &key, String8* value) { + value->clear(); + const char* valueToken; + for (Vector::const_iterator nextToken = mTokens.begin(); + nextToken != mTokens.end(); ++nextToken) { + if (0 == (*nextToken).compare(key)) { + if (nextToken + 1 == mTokens.end()) + break; + valueToken = (*(nextToken + 1)).string(); + value->setTo(valueToken); + nextToken++; + break; + } + } +} + +bool JsonWebKey::isJsonWebKeySet(const String8& jsonObject) const { + if (jsonObject.find(kKeysTag) == -1) { + ALOGE("JSON Web Key does not contain keys."); + return false; + } + return true; +} + +/* + * Parses a JSON objects string and initializes a vector of tokens. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::parseJsonObject(const String8& jsonObject, + Vector* tokens) { + jsmn_parser parser; + + jsmn_init(&parser); + int numTokens = jsmn_parse(&parser, + jsonObject.string(), jsonObject.size(), NULL, 0); + if (numTokens < 0) { + ALOGE("Parser returns error code=%d", numTokens); + return false; + } + + unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); + mJsmnTokens.clear(); + mJsmnTokens.setCapacity(jsmnTokensSize); + + jsmn_init(&parser); + int status = jsmn_parse(&parser, jsonObject.string(), + jsonObject.size(), mJsmnTokens.editArray(), numTokens); + if (status < 0) { + ALOGE("Parser returns error code=%d", status); + return false; + } + + tokens->clear(); + String8 token; + const char *pjs; + for (int j = 0; j < numTokens; ++j) { + pjs = jsonObject.string() + mJsmnTokens[j].start; + if (mJsmnTokens[j].type == JSMN_STRING || + mJsmnTokens[j].type == JSMN_PRIMITIVE) { + token.setTo(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start); + tokens->add(token); + } + } + return true; +} + +/* + * Parses JSON Web Key Set string and initializes a vector of JSON objects. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::parseJsonWebKeySet(const String8& jsonWebKeySet, + Vector* jsonObjects) { + if (jsonWebKeySet.isEmpty()) { + ALOGE("Empty JSON Web Key"); + return false; + } + + // The jsmn parser only supports unicode encoding. + jsmn_parser parser; + + // Computes number of tokens. A token marks the type, offset in + // the original string. + jsmn_init(&parser); + int numTokens = jsmn_parse(&parser, + jsonWebKeySet.string(), jsonWebKeySet.size(), NULL, 0); + if (numTokens < 0) { + ALOGE("Parser returns error code=%d", numTokens); + return false; + } + + unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); + mJsmnTokens.setCapacity(jsmnTokensSize); + + jsmn_init(&parser); + int status = jsmn_parse(&parser, jsonWebKeySet.string(), + jsonWebKeySet.size(), mJsmnTokens.editArray(), numTokens); + if (status < 0) { + ALOGE("Parser returns error code=%d", status); + return false; + } + + String8 token; + const char *pjs; + for (int i = 0; i < numTokens; ++i) { + pjs = jsonWebKeySet.string() + mJsmnTokens[i].start; + if (mJsmnTokens[i].type == JSMN_OBJECT) { + token.setTo(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start); + jsonObjects->add(token); + } + } + return true; +} + +} // clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/JsonWebKey.h new file mode 100644 index 0000000..6ae50ee --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/JsonWebKey.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 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 CLEARKEY_JSON_WEB_KEY_H_ +#define CLEARKEY_JSON_WEB_KEY_H_ + +#include +#include +#include +#include + +#include "jsmn.h" +#include "Utils.h" +#include "ClearKeyTypes.h" + +namespace clearkeydrm { + +using android::KeyedVector; +using android::sp; +using android::String8; +using android::Vector; + +class JsonWebKey { + public: + JsonWebKey(); + virtual ~JsonWebKey(); + + bool extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet, + KeyMap* keys); + + private: + Vector mJsmnTokens; + Vector mJsonObjects; + Vector mTokens; + + bool decodeBase64String(const String8& encodedText, + Vector* decodedText); + bool findKey(const String8& jsonObject, String8* keyId, + String8* encodedKey); + void findValue(const String8 &key, String8* value); + bool isJsonWebKeySet(const String8& jsonObject) const; + bool parseJsonObject(const String8& jsonObject, Vector* tokens); + bool parseJsonWebKeySet(const String8& jsonWebKeySet, Vector* jsonObjects); + + DISALLOW_EVIL_CONSTRUCTORS(JsonWebKey); +}; + +} // namespace clearkeydrm + +#endif // CLEARKEY_JSON_WEB_KEY_H_ diff --git a/drm/mediadrm/plugins/clearkey/tests/Android.mk b/drm/mediadrm/plugins/clearkey/tests/Android.mk new file mode 100644 index 0000000..c12cd9f --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/tests/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright (C) 2014 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. +# +# ---------------------------------------------------------------- +# Builds ClearKey Drm Tests +# +LOCAL_PATH := $(call my-dir) + +test_name := JsonWebKeyUnittest +test_src_dir := . +include $(LOCAL_PATH)/unit-test.mk + +test_name := +test_src_dir := diff --git a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp new file mode 100644 index 0000000..c3b0d84 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include "JsonWebKey.h" + +#include "gtest/gtest.h" +#include "Utils.h" + +namespace clearkeydrm { +using android::String8; +using android::Vector; + +class JsonWebKeyTest : public ::testing::Test { + protected: + JsonWebKey* jwk; + + JsonWebKeyTest() { + jwk = new JsonWebKey; + } + + virtual ~JsonWebKeyTest() { + if (jwk) + delete jwk; + } +}; + +void stringFromVector(const Vector& input, + String8* converted) { + converted->clear(); + if (input.isEmpty()) { + return; + } + + for (size_t i = 0; i < input.size(); ++i) { + converted->appendFormat("%c", input.itemAt(i)); + } +} + +void verifyKeys(const KeyMap& keys, const String8* clearKeys) { + if (keys.isEmpty()) { + return; + } + + String8 keyString; + for (size_t i = 0; i < keys.size(); ++i) { + stringFromVector(keys.valueAt(i), &keyString); + EXPECT_EQ(keyString, clearKeys[i]); + } +} + +TEST_F(JsonWebKeyTest, NoSymmetricKey) { + const String8 js( + "{" + "[{" + "\"kty\":\"rsa\"," + "\"alg\":\"A128KW1\"," + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\"," + "\"k\":\"1-GawgguFyGrWKav7AX4VKUg\"" + "}]" + "}"); + + KeyMap keys; + EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); + EXPECT_TRUE(keys.isEmpty()); +} + +TEST_F(JsonWebKeyTest, NoKeysTag) { + const String8 js( + "{" + "[{" + "\"kty\":\"oct\"," + "\"alg\":\"A128KW1\"," + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\"," + "\"k\":\"1-GawgguFyGrWKav7AX4VKUg\"" + "}," + "{" + "\"kty\":\"oct\"," + "\"alg\":\"A128KW2\"," + "\"k\":\"R29vZCBkYXkh\"," + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\"" + "}]" + "}"); + + KeyMap keys; + EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); + EXPECT_TRUE(keys.isEmpty()); +} + +TEST_F(JsonWebKeyTest, NoKeyId) { + const String8 js( + "{" + "\"keys\":" + "[{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW1\"" + "\"k\":\"SGVsbG8gRnJpZW5kISE=\"" + "}" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW2\"" + "\"k\":\"R29vZCBkYXkh\"" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\"" + "}]" + "}"); + + KeyMap keys; + EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); + EXPECT_TRUE(keys.size() == 1); + + const String8 clearKeys("Good day!"); + verifyKeys(keys, &clearKeys); +} + +TEST_F(JsonWebKeyTest, NoKey) { + const String8 js( + "{" + "\"keys\":" + "[{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW1\"" + "\"kid\":\"`\"" + "}" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW2\"" + "\"k\":\"R29vZCBkYXkh\"" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\"" + "}]" + "}"); + + KeyMap keys; + EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); + EXPECT_TRUE(keys.size() == 1); + + const String8 clearKeys("Good day!"); + verifyKeys(keys, &clearKeys); +} + +TEST_F(JsonWebKeyTest, MalformedKey) { + const String8 js( + "{" + "\"keys\":" + "[{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW1\"" + "\"k\":\"GawgguFyGrWKav7AX4V???\"" + "\"kid\":\"67ef0gd8pvfd0=\"" + "}" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW1\"" + "\"k\":\"GawgguFyGrWKav7AX4V???\"" + "\"kid\":" + "}" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW1\"" + ":\"GawgguFyGrWKav7AX4V???\"" + "\"kid\":\"67ef0gd8pvfd0=\"" + "}" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW3\"" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\"" + "\"k\":\"R29vZCBkYXkh\"" + "}]" + "}"); + + KeyMap keys; + EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); + EXPECT_TRUE(keys.size() == 1); + + const String8 clearKeys("Good day!"); + verifyKeys(keys, &clearKeys); +} + +TEST_F(JsonWebKeyTest, EmptyJsonWebKey) { + const String8 js; + KeyMap keys; + EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); + EXPECT_TRUE(keys.isEmpty()); +} + +TEST_F(JsonWebKeyTest, MalformedJsonWebKey) { + // Missing begin array '[' + const String8 js( + "{" + "\"keys\":" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW1\"" + "\"k\":\"GawgguFyGrWKav7AX4VKUg\"" + "\"kid\":\"67ef0gd8pvfd0=\"" + "}" + "]" + "}"); + + KeyMap keys; + EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); + EXPECT_TRUE(keys.isEmpty()); +} + +TEST_F(JsonWebKeyTest, SameKeyId) { + const String8 js( + "{" + "\"keys\":" + "[{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW1\"" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\"" + "\"k\":\"SGVsbG8gRnJpZW5kISE\"" + "}" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW1\"" + "\"k\":\"SGVsbG8gRnJpZW5kIQ\"" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\"" + "}" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW3\"" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\"" + "\"k\":\"R29vZCBkYXkh\"" + "}]" + "}"); + + KeyMap keys; + jwk->extractKeysFromJsonWebKeySet(js, &keys); + EXPECT_TRUE(keys.size() == 2); + + const String8 clearKeys[] = + { String8("Hello Friend!"), String8("Good day!") }; + verifyKeys(keys, clearKeys); +} + +TEST_F(JsonWebKeyTest, ExtractWellFormedKeys) { + const String8 js( + "{" + "\"keys\":" + "[{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW1\"" + "}" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW2\"" + "\"k\":\"SGVsbG8gRnJpZW5kIQ\"" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\"" + "}" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW3\"" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\"" + "\"k\":\"R29vZCBkYXkh\"" + "}]" + "}"); + + KeyMap keys; + jwk->extractKeysFromJsonWebKeySet(js, &keys); + EXPECT_TRUE(keys.size() == 2); + + const String8 clearKeys[] = + { String8("Hello Friend!"), String8("Good day!") }; + verifyKeys(keys, clearKeys); +} + +TEST_F(JsonWebKeyTest, ExtractKeys) { + const String8 js( + "{" + "\"keys\":" + "[{" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\"" + "\"k\":\"SGVsbG8gRnJpZW5kISE\"" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW1\"" + "}" + "{" + "\"kty\":\"oct\"" + "\"alg\":\"A128KW2\"" + "\"k\":\"SGVsbG8gRnJpZW5kIQ\"" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\"" + "}" + "{" + "\"kty\":\"rsa\"" + "\"alg\":\"A128KW-rsa\"" + "\"k\":\"R29vZCBkYXkh\"" + "\"kid\":\"rsa-67ef0gd8pvfd0=\"" + "}" + "{" + "\"alg\":\"A128KW3\"" + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\"" + "\"k\":\"R29vZCBkYXkh\"" + "\"kty\":\"oct\"" + "}]" + "}"); + + KeyMap keys; + jwk->extractKeysFromJsonWebKeySet(js, &keys); + EXPECT_TRUE(keys.size() == 3); + + const String8 clearKeys[] = + { String8("Hello Friend!!"), String8("Hello Friend!"), + String8("Good day!") }; + verifyKeys(keys, clearKeys); +} + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/tests/unit-test.mk b/drm/mediadrm/plugins/clearkey/tests/unit-test.mk new file mode 100644 index 0000000..6afb5a3 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/tests/unit-test.mk @@ -0,0 +1,51 @@ +# +# Copyright (C) 2014 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. +# +# ------------------------------------------------------------------- +# Makes a unit or end to end test. +# test_name must be passed in as the base filename(without the .cpp). +# +$(call assert-not-null,test_name) + +include $(CLEAR_VARS) + +LOCAL_MODULE := $(test_name) +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + $(test_src_dir)/$(test_name).cpp + +LOCAL_C_INCLUDES := \ + bionic \ + external/gtest/include \ + external/jsmn \ + external/openssl/include \ + external/stlport/stlport \ + frameworks/av/drm/mediadrm/plugins/clearkey \ + frameworks/av/include \ + frameworks/native/include \ + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main \ + +LOCAL_SHARED_LIBRARIES := \ + libcrypto \ + libdrmclearkeyplugin \ + liblog \ + libstlport \ + libutils \ + +include $(BUILD_EXECUTABLE) -- cgit v1.1