1791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
2791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang/*
3791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * Copyright (C) 2017 The Android Open Source Project
4791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang *
5791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * Licensed under the Apache License, Version 2.0 (the "License");
6791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * you may not use this file except in compliance with the License.
7791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * You may obtain a copy of the License at
8791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang *
9791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang *      http://www.apache.org/licenses/LICENSE-2.0
10791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang *
11791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * Unless required by applicable law or agreed to in writing, software
12791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * distributed under the License is distributed on an "AS IS" BASIS,
13791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * See the License for the specific language governing permissions and
15791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * limitations under the License.
16791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang */
17791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang//#define LOG_NDEBUG 0
18791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang#define LOG_TAG "JsonAssetLoader"
19791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
20791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang#include <media/stagefright/foundation/ABuffer.h>
21791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang#include <media/stagefright/foundation/AString.h>
22791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang#include <media/stagefright/foundation/base64.h>
23791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang#include <media/stagefright/MediaErrors.h>
24791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang#include <utils/Log.h>
25791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
26791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang#include "JsonAssetLoader.h"
27791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang#include "protos/license_protos.pb.h"
28791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
29791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangnamespace android {
30791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangnamespace clearkeycas {
31791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
32791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangconst String8 kIdTag("id");
33791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangconst String8 kNameTag("name");
34791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangconst String8 kLowerCaseOgranizationNameTag("lowercase_organization_name");
35791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangconst String8 kEncryptionKeyTag("encryption_key");
36791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangconst String8 kCasTypeTag("cas_type");
37791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangconst String8 kBase64Padding("=");
38791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
39791a1a206b56be8601a6fffd2614926e67d64790Chong ZhangJsonAssetLoader::JsonAssetLoader() {
40791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang}
41791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
42791a1a206b56be8601a6fffd2614926e67d64790Chong ZhangJsonAssetLoader::~JsonAssetLoader() {
43791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang}
44791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
45791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang/*
46791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * Extract a clear key asset from a JSON string.
47791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang *
48791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * Returns OK if a clear key asset is extracted successfully,
49d5a416a49d5074e2966f5fe255561cbaaf31a325Chong Zhang * or ERROR_CAS_NO_LICENSE if the string doesn't contain a valid
50791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * clear key asset.
51791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang */
52791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangstatus_t JsonAssetLoader::extractAssetFromString(
53791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        const String8& jsonAssetString, Asset *asset) {
54791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (!parseJsonAssetString(jsonAssetString, &mJsonObjects)) {
55d5a416a49d5074e2966f5fe255561cbaaf31a325Chong Zhang        return ERROR_CAS_NO_LICENSE;
56791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
57791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
58791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (mJsonObjects.size() < 1) {
59d5a416a49d5074e2966f5fe255561cbaaf31a325Chong Zhang        return ERROR_CAS_NO_LICENSE;
60791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
61791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
62791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (!parseJsonObject(mJsonObjects[0], &mTokens))
63d5a416a49d5074e2966f5fe255561cbaaf31a325Chong Zhang        return ERROR_CAS_NO_LICENSE;
64791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
65791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (!findKey(mJsonObjects[0], asset)) {
66d5a416a49d5074e2966f5fe255561cbaaf31a325Chong Zhang        return ERROR_CAS_NO_LICENSE;
67791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
68791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    return OK;
69791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang}
70791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
71791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang//static
72791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangsp<ABuffer> JsonAssetLoader::decodeBase64String(const String8& encodedText) {
73791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    // Since android::decodeBase64() requires padding characters,
74791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    // add them so length of encodedText is exactly a multiple of 4.
75791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    int remainder = encodedText.length() % 4;
76791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    String8 paddedText(encodedText);
77791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (remainder > 0) {
78791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        for (int i = 0; i < 4 - remainder; ++i) {
79791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            paddedText.append(kBase64Padding);
80791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        }
81791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
82791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
83791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    return decodeBase64(AString(paddedText.string()));
84791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang}
85791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
86791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangbool JsonAssetLoader::findKey(const String8& jsonObject, Asset *asset) {
87791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
88791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    String8 value;
89791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
90791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (jsonObject.find(kIdTag) < 0) {
91791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        return false;
92791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
93791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    findValue(kIdTag, &value);
94791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    ALOGV("found %s=%s", kIdTag.string(), value.string());
95791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    asset->set_id(atoi(value.string()));
96791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
97791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (jsonObject.find(kNameTag) < 0) {
98791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        return false;
99791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
100791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    findValue(kNameTag, &value);
101791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    ALOGV("found %s=%s", kNameTag.string(), value.string());
102791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    asset->set_name(value.string());
103791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
104791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (jsonObject.find(kLowerCaseOgranizationNameTag) < 0) {
105791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        return false;
106791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
107791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    findValue(kLowerCaseOgranizationNameTag, &value);
108791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    ALOGV("found %s=%s", kLowerCaseOgranizationNameTag.string(), value.string());
109791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    asset->set_lowercase_organization_name(value.string());
110791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
111791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (jsonObject.find(kCasTypeTag) < 0) {
112791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        return false;
113791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
114791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    findValue(kCasTypeTag, &value);
115791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    ALOGV("found %s=%s", kCasTypeTag.string(), value.string());
116791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    // Asset_CasType_CLEARKEY_CAS = 1
117791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    asset->set_cas_type((Asset_CasType)atoi(value.string()));
118791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
119791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    return true;
120791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang}
121791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
122791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangvoid JsonAssetLoader::findValue(const String8 &key, String8* value) {
123791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    value->clear();
124791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    const char* valueToken;
125791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    for (Vector<String8>::const_iterator nextToken = mTokens.begin();
126791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        nextToken != mTokens.end(); ++nextToken) {
127791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        if (0 == (*nextToken).compare(key)) {
128791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            if (nextToken + 1 == mTokens.end())
129791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang                break;
130791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            valueToken = (*(nextToken + 1)).string();
131791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            value->setTo(valueToken);
132791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            nextToken++;
133791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            break;
134791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        }
135791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
136791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang}
137791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
138791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang/*
139791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * Parses a JSON objects string and initializes a vector of tokens.
140791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang *
141791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * @return Returns false for errors, true for success.
142791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang */
143791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangbool JsonAssetLoader::parseJsonObject(const String8& jsonObject,
144791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        Vector<String8>* tokens) {
145791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    jsmn_parser parser;
146791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
147791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    jsmn_init(&parser);
148791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    int numTokens = jsmn_parse(&parser,
149791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        jsonObject.string(), jsonObject.size(), NULL, 0);
150791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (numTokens < 0) {
151791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        ALOGE("Parser returns error code=%d", numTokens);
152791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        return false;
153791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
154791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
155791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
156791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    mJsmnTokens.clear();
157791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    mJsmnTokens.setCapacity(jsmnTokensSize);
158791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
159791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    jsmn_init(&parser);
160791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    int status = jsmn_parse(&parser, jsonObject.string(),
161791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        jsonObject.size(), mJsmnTokens.editArray(), numTokens);
162791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (status < 0) {
163791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        ALOGE("Parser returns error code=%d", status);
164791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        return false;
165791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
166791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
167791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    tokens->clear();
168791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    String8 token;
169791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    const char *pjs;
170791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    ALOGV("numTokens: %d", numTokens);
171791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    for (int j = 0; j < numTokens; ++j) {
172791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        pjs = jsonObject.string() + mJsmnTokens[j].start;
173791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        if (mJsmnTokens[j].type == JSMN_STRING ||
174791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang                mJsmnTokens[j].type == JSMN_PRIMITIVE) {
175791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            token.setTo(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start);
176791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            tokens->add(token);
177791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            ALOGV("add token: %s", token.string());
178791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        }
179791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
180791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    return true;
181791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang}
182791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
183791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang/*
184791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * Parses JSON asset string and initializes a vector of JSON objects.
185791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang *
186791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang * @return Returns false for errors, true for success.
187791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang */
188791a1a206b56be8601a6fffd2614926e67d64790Chong Zhangbool JsonAssetLoader::parseJsonAssetString(const String8& jsonAsset,
189791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        Vector<String8>* jsonObjects) {
190791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (jsonAsset.isEmpty()) {
191791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        ALOGE("Empty JSON Web Key");
192791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        return false;
193791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
194791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
195791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    // The jsmn parser only supports unicode encoding.
196791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    jsmn_parser parser;
197791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
198791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    // Computes number of tokens. A token marks the type, offset in
199791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    // the original string.
200791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    jsmn_init(&parser);
201791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    int numTokens = jsmn_parse(&parser,
202791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            jsonAsset.string(), jsonAsset.size(), NULL, 0);
203791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (numTokens < 0) {
204791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        ALOGE("Parser returns error code=%d", numTokens);
205791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        return false;
206791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
207791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
208791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
209791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    mJsmnTokens.setCapacity(jsmnTokensSize);
210791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
211791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    jsmn_init(&parser);
212791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    int status = jsmn_parse(&parser, jsonAsset.string(),
213791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            jsonAsset.size(), mJsmnTokens.editArray(), numTokens);
214791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    if (status < 0) {
215791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        ALOGE("Parser returns error code=%d", status);
216791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        return false;
217791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
218791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
219791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    String8 token;
220791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    const char *pjs;
221791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    for (int i = 0; i < numTokens; ++i) {
222791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        pjs = jsonAsset.string() + mJsmnTokens[i].start;
223791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        if (mJsmnTokens[i].type == JSMN_OBJECT) {
224791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            token.setTo(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start);
225791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang            jsonObjects->add(token);
226791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang        }
227791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    }
228791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang    return true;
229791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang}
230791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang
231791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang}  // namespace clearkeycas
232791a1a206b56be8601a6fffd2614926e67d64790Chong Zhang}  // namespace android
233