1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //#define LOG_NDEBUG 0 18 #define LOG_TAG "CasManager" 19 #include "CasManager.h" 20 21 #include <android/hardware/cas/1.0/ICas.h> 22 #include <android/hardware/cas/1.0/IMediaCasService.h> 23 #include <android/hardware/cas/native/1.0/IDescrambler.h> 24 #include <hidl/HidlSupport.h> 25 #include <media/stagefright/foundation/ABitReader.h> 26 #include <utils/Log.h> 27 28 namespace android { 29 30 using hardware::hidl_vec; 31 using hardware::Return; 32 using namespace hardware::cas::V1_0; 33 using namespace hardware::cas::native::V1_0; 34 35 struct ATSParser::CasManager::ProgramCasManager : public RefBase { 36 ProgramCasManager(unsigned programNumber, const CADescriptor &descriptor); 37 ProgramCasManager(unsigned programNumber); 38 39 bool addStream(unsigned elementaryPID, const CADescriptor &descriptor); 40 41 status_t setMediaCas(const sp<ICas> &cas, PidToSessionMap &sessionMap); 42 43 bool getCasSession(unsigned elementaryPID, 44 sp<IDescrambler> *descrambler, std::vector<uint8_t> *sessionId) const; 45 46 void closeAllSessions(const sp<ICas>& cas); 47 48 private: 49 struct CasSession { 50 CasSession() {} 51 CasSession(const CADescriptor &descriptor) : 52 mCADescriptor(descriptor) {} 53 54 CADescriptor mCADescriptor; 55 std::vector<uint8_t> mSessionId; 56 sp<IDescrambler> mDescrambler; 57 }; 58 status_t initSession( 59 const sp<ICas>& cas, 60 PidToSessionMap &sessionMap, 61 CasSession *session); 62 void closeSession(const sp<ICas>& cas, const CasSession &casSession); 63 64 unsigned mProgramNumber; 65 bool mHasProgramCas; 66 CasSession mProgramCas; 67 KeyedVector<unsigned, CasSession> mStreamPidToCasMap; 68 }; 69 70 ATSParser::CasManager::ProgramCasManager::ProgramCasManager( 71 unsigned programNumber, const CADescriptor &descriptor) : 72 mProgramNumber(programNumber), 73 mHasProgramCas(true), 74 mProgramCas(descriptor) {} 75 76 ATSParser::CasManager::ProgramCasManager::ProgramCasManager( 77 unsigned programNumber) : 78 mProgramNumber(programNumber), 79 mHasProgramCas(false) {} 80 81 bool ATSParser::CasManager::ProgramCasManager::addStream( 82 unsigned elementaryPID, const CADescriptor &descriptor) { 83 ssize_t index = mStreamPidToCasMap.indexOfKey(elementaryPID); 84 if (index >= 0) { 85 return false; 86 } 87 ALOGV("addStream: program=%d, elementaryPID=%d, CA_system_ID=0x%x", 88 mProgramNumber, elementaryPID, descriptor.mSystemID); 89 mStreamPidToCasMap.add(elementaryPID, CasSession(descriptor)); 90 return true; 91 } 92 93 status_t ATSParser::CasManager::ProgramCasManager::setMediaCas( 94 const sp<ICas> &cas, PidToSessionMap &sessionMap) { 95 if (mHasProgramCas) { 96 return initSession(cas, sessionMap, &mProgramCas); 97 } 98 // TODO: share session among streams that has identical CA_descriptors. 99 // For now, we open one session for each stream that has CA_descriptor. 100 for (size_t index = 0; index < mStreamPidToCasMap.size(); index++) { 101 status_t err = initSession( 102 cas, sessionMap, &mStreamPidToCasMap.editValueAt(index)); 103 if (err != OK) { 104 return err; 105 } 106 } 107 return OK; 108 } 109 110 bool ATSParser::CasManager::ProgramCasManager::getCasSession( 111 unsigned elementaryPID, sp<IDescrambler> *descrambler, 112 std::vector<uint8_t> *sessionId) const { 113 if (mHasProgramCas) { 114 *descrambler = mProgramCas.mDescrambler; 115 *sessionId = mProgramCas.mSessionId; 116 return true; 117 } 118 ssize_t index = mStreamPidToCasMap.indexOfKey(elementaryPID); 119 if (index < 0) { 120 return false; 121 } 122 123 *descrambler = mStreamPidToCasMap[index].mDescrambler; 124 *sessionId = mStreamPidToCasMap[index].mSessionId; 125 return true; 126 } 127 128 status_t ATSParser::CasManager::ProgramCasManager::initSession( 129 const sp<ICas>& cas, 130 PidToSessionMap &sessionMap, 131 CasSession *session) { 132 sp<IMediaCasService> casService = IMediaCasService::getService("default"); 133 if (casService == NULL) { 134 ALOGE("Cannot obtain IMediaCasService"); 135 return NO_INIT; 136 } 137 138 Status status; 139 sp<IDescrambler> descrambler; 140 sp<IDescramblerBase> descramblerBase; 141 Return<Status> returnStatus(Status::OK); 142 Return<sp<IDescramblerBase> > returnDescrambler(NULL); 143 std::vector<uint8_t> sessionId; 144 const CADescriptor &descriptor = session->mCADescriptor; 145 146 auto returnVoid = cas->openSession( 147 [&status, &sessionId] (Status _status, const hidl_vec<uint8_t>& _sessionId) { 148 status = _status; 149 sessionId = _sessionId; 150 }); 151 if (!returnVoid.isOk() || status != Status::OK) { 152 ALOGE("Failed to open session: trans=%s, status=%d", 153 returnVoid.description().c_str(), status); 154 goto l_fail; 155 } 156 157 returnStatus = cas->setSessionPrivateData(sessionId, descriptor.mPrivateData); 158 if (!returnStatus.isOk() || returnStatus != Status::OK) { 159 ALOGE("Failed to set private data: trans=%s, status=%d", 160 returnStatus.description().c_str(), (Status)returnStatus); 161 goto l_fail; 162 } 163 164 returnDescrambler = casService->createDescrambler(descriptor.mSystemID); 165 if (!returnDescrambler.isOk()) { 166 ALOGE("Failed to create descrambler: trans=%s", 167 returnDescrambler.description().c_str()); 168 goto l_fail; 169 } 170 descramblerBase = (sp<IDescramblerBase>) returnDescrambler; 171 if (descramblerBase == NULL) { 172 ALOGE("Failed to create descrambler: null ptr"); 173 goto l_fail; 174 } 175 176 returnStatus = descramblerBase->setMediaCasSession(sessionId); 177 if (!returnStatus.isOk() || (Status) returnStatus != Status::OK) { 178 ALOGE("Failed to init descrambler: : trans=%s, status=%d", 179 returnStatus.description().c_str(), (Status) returnStatus); 180 goto l_fail; 181 } 182 183 descrambler = IDescrambler::castFrom(descramblerBase); 184 if (descrambler == NULL) { 185 ALOGE("Failed to cast from IDescramblerBase to IDescrambler"); 186 goto l_fail; 187 } 188 189 session->mSessionId = sessionId; 190 session->mDescrambler = descrambler; 191 sessionMap.add(descriptor.mPID, sessionId); 192 193 return OK; 194 195 l_fail: 196 if (!sessionId.empty()) { 197 cas->closeSession(sessionId); 198 } 199 if (descramblerBase != NULL) { 200 descramblerBase->release(); 201 } 202 return NO_INIT; 203 } 204 205 void ATSParser::CasManager::ProgramCasManager::closeSession( 206 const sp<ICas>& cas, const CasSession &casSession) { 207 if (casSession.mDescrambler != NULL) { 208 casSession.mDescrambler->release(); 209 } 210 if (!casSession.mSessionId.empty()) { 211 cas->closeSession(casSession.mSessionId); 212 } 213 } 214 215 void ATSParser::CasManager::ProgramCasManager::closeAllSessions( 216 const sp<ICas>& cas) { 217 if (mHasProgramCas) { 218 closeSession(cas, mProgramCas); 219 } 220 for (size_t index = 0; index < mStreamPidToCasMap.size(); index++) { 221 closeSession(cas, mStreamPidToCasMap.editValueAt(index)); 222 } 223 } 224 225 //////////////////////////////////////////////////////////////////////////////// 226 227 ATSParser::CasManager::CasManager() : mSystemId(-1) {} 228 229 ATSParser::CasManager::~CasManager() { 230 // Explictly close the sessions opened by us, since the CAS object is owned 231 // by the app and may not go away after the parser is destroyed, and the app 232 // may not have information about the sessions. 233 if (mICas != NULL) { 234 for (size_t index = 0; index < mProgramCasMap.size(); index++) { 235 mProgramCasMap.editValueAt(index)->closeAllSessions(mICas); 236 } 237 } 238 } 239 240 bool ATSParser::CasManager::setSystemId(int32_t CA_system_ID) { 241 if (mSystemId == -1) { 242 // Verify the CA_system_ID is within range on the first program 243 if (CA_system_ID < 0 || CA_system_ID > 0xffff) { 244 ALOGE("Invalid CA_system_id: %d", CA_system_ID); 245 return false; 246 } 247 mSystemId = CA_system_ID; 248 } else if (mSystemId != CA_system_ID) { 249 // All sessions need to be under the same CA system 250 ALOGE("Multiple CA systems not allowed: %d vs %d", 251 mSystemId, CA_system_ID); 252 return false; 253 } 254 return true; 255 } 256 257 status_t ATSParser::CasManager::setMediaCas(const sp<ICas> &cas) { 258 if (cas == NULL) { 259 ALOGE("setMediaCas: received NULL object"); 260 return BAD_VALUE; 261 } 262 if (mICas != NULL) { 263 ALOGW("setMediaCas: already set"); 264 return ALREADY_EXISTS; 265 } 266 for (size_t index = 0; index < mProgramCasMap.size(); index++) { 267 status_t err; 268 if ((err = mProgramCasMap.editValueAt( 269 index)->setMediaCas(cas, mCAPidToSessionIdMap)) != OK) { 270 return err; 271 } 272 } 273 mICas = cas; 274 return OK; 275 } 276 277 bool ATSParser::CasManager::addProgram( 278 unsigned programNumber, const CADescriptor &descriptor) { 279 if (!setSystemId(descriptor.mSystemID)) { 280 return false; 281 } 282 283 ssize_t index = mProgramCasMap.indexOfKey(programNumber); 284 if (index < 0) { 285 ALOGV("addProgram: programNumber=%d, CA_system_ID=0x%x", 286 programNumber, descriptor.mSystemID); 287 mProgramCasMap.add(programNumber, 288 new ProgramCasManager(programNumber, descriptor)); 289 mCAPidSet.insert(descriptor.mPID); 290 } 291 return true; 292 } 293 294 bool ATSParser::CasManager::addStream( 295 unsigned programNumber, unsigned elementaryPID, 296 const CADescriptor &descriptor) { 297 if (!setSystemId(descriptor.mSystemID)) { 298 return false; 299 } 300 301 ssize_t index = mProgramCasMap.indexOfKey(programNumber); 302 sp<ProgramCasManager> programCasManager; 303 if (index < 0) { 304 ALOGV("addProgram (no CADescriptor): programNumber=%d", programNumber); 305 programCasManager = new ProgramCasManager(programNumber); 306 mProgramCasMap.add(programNumber, programCasManager); 307 } else { 308 programCasManager = mProgramCasMap.editValueAt(index); 309 } 310 if (programCasManager->addStream(elementaryPID, descriptor)) { 311 mCAPidSet.insert(descriptor.mPID); 312 } 313 return true; 314 } 315 316 bool ATSParser::CasManager::getCasInfo( 317 unsigned programNumber, unsigned elementaryPID, 318 int32_t *systemId, sp<IDescrambler> *descrambler, 319 std::vector<uint8_t> *sessionId) const { 320 ssize_t index = mProgramCasMap.indexOfKey(programNumber); 321 if (index < 0) { 322 return false; 323 } 324 *systemId = mSystemId; 325 return mProgramCasMap[index]->getCasSession( 326 elementaryPID, descrambler, sessionId); 327 } 328 329 bool ATSParser::CasManager::isCAPid(unsigned pid) { 330 return mCAPidSet.find(pid) != mCAPidSet.end(); 331 } 332 333 bool ATSParser::CasManager::parsePID(ABitReader *br, unsigned pid) { 334 ssize_t index = mCAPidToSessionIdMap.indexOfKey(pid); 335 if (index < 0) { 336 return false; 337 } 338 hidl_vec<uint8_t> ecm; 339 ecm.setToExternal((uint8_t*)br->data(), br->numBitsLeft() / 8); 340 auto returnStatus = mICas->processEcm(mCAPidToSessionIdMap[index], ecm); 341 if (!returnStatus.isOk() || (Status) returnStatus != Status::OK) { 342 ALOGE("Failed to process ECM: trans=%s, status=%d", 343 returnStatus.description().c_str(), (Status) returnStatus); 344 } 345 return true; // handled 346 } 347 348 } // namespace android 349