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/media/ICas.h> 22#include <android/media/IDescrambler.h> 23#include <android/media/IMediaCasService.h> 24#include <binder/IServiceManager.h> 25#include <media/stagefright/foundation/ABitReader.h> 26#include <utils/Log.h> 27 28namespace android { 29using binder::Status; 30 31struct ATSParser::CasManager::ProgramCasManager : public RefBase { 32 ProgramCasManager(unsigned programNumber, const CADescriptor &descriptor); 33 ProgramCasManager(unsigned programNumber); 34 35 bool addStream(unsigned elementaryPID, const CADescriptor &descriptor); 36 37 status_t setMediaCas(const sp<ICas> &cas, PidToSessionMap &sessionMap); 38 39 bool getCasSession(unsigned elementaryPID, 40 sp<IDescrambler> *descrambler, std::vector<uint8_t> *sessionId) const; 41 42 void closeAllSessions(const sp<ICas>& cas); 43 44private: 45 struct CasSession { 46 CasSession() {} 47 CasSession(const CADescriptor &descriptor) : 48 mCADescriptor(descriptor) {} 49 50 CADescriptor mCADescriptor; 51 std::vector<uint8_t> mSessionId; 52 sp<IDescrambler> mDescrambler; 53 }; 54 status_t initSession( 55 const sp<ICas>& cas, 56 PidToSessionMap &sessionMap, 57 CasSession *session); 58 void closeSession(const sp<ICas>& cas, const CasSession &casSession); 59 60 unsigned mProgramNumber; 61 bool mHasProgramCas; 62 CasSession mProgramCas; 63 KeyedVector<unsigned, CasSession> mStreamPidToCasMap; 64}; 65 66ATSParser::CasManager::ProgramCasManager::ProgramCasManager( 67 unsigned programNumber, const CADescriptor &descriptor) : 68 mProgramNumber(programNumber), 69 mHasProgramCas(true), 70 mProgramCas(descriptor) {} 71 72ATSParser::CasManager::ProgramCasManager::ProgramCasManager( 73 unsigned programNumber) : 74 mProgramNumber(programNumber), 75 mHasProgramCas(false) {} 76 77bool ATSParser::CasManager::ProgramCasManager::addStream( 78 unsigned elementaryPID, const CADescriptor &descriptor) { 79 ssize_t index = mStreamPidToCasMap.indexOfKey(elementaryPID); 80 if (index >= 0) { 81 return false; 82 } 83 ALOGV("addStream: program=%d, elementaryPID=%d, CA_system_ID=0x%x", 84 mProgramNumber, elementaryPID, descriptor.mSystemID); 85 mStreamPidToCasMap.add(elementaryPID, CasSession(descriptor)); 86 return true; 87} 88 89status_t ATSParser::CasManager::ProgramCasManager::setMediaCas( 90 const sp<ICas> &cas, PidToSessionMap &sessionMap) { 91 if (mHasProgramCas) { 92 return initSession(cas, sessionMap, &mProgramCas); 93 } 94 // TODO: share session among streams that has identical CA_descriptors. 95 // For now, we open one session for each stream that has CA_descriptor. 96 for (size_t index = 0; index < mStreamPidToCasMap.size(); index++) { 97 status_t err = initSession( 98 cas, sessionMap, &mStreamPidToCasMap.editValueAt(index)); 99 if (err != OK) { 100 return err; 101 } 102 } 103 return OK; 104} 105 106bool ATSParser::CasManager::ProgramCasManager::getCasSession( 107 unsigned elementaryPID, sp<IDescrambler> *descrambler, 108 std::vector<uint8_t> *sessionId) const { 109 if (mHasProgramCas) { 110 *descrambler = mProgramCas.mDescrambler; 111 *sessionId = mProgramCas.mSessionId; 112 return true; 113 } 114 ssize_t index = mStreamPidToCasMap.indexOfKey(elementaryPID); 115 if (index < 0) { 116 return false; 117 } 118 119 *descrambler = mStreamPidToCasMap[index].mDescrambler; 120 *sessionId = mStreamPidToCasMap[index].mSessionId; 121 return true; 122} 123 124status_t ATSParser::CasManager::ProgramCasManager::initSession( 125 const sp<ICas>& cas, 126 PidToSessionMap &sessionMap, 127 CasSession *session) { 128 sp<IServiceManager> sm = defaultServiceManager(); 129 sp<IBinder> casServiceBinder = sm->getService(String16("media.cas")); 130 sp<IMediaCasService> casService = 131 interface_cast<IMediaCasService>(casServiceBinder); 132 133 if (casService == NULL) { 134 ALOGE("Cannot obtain IMediaCasService"); 135 return NO_INIT; 136 } 137 138 sp<IDescrambler> descrambler; 139 std::vector<uint8_t> sessionId; 140 const CADescriptor &descriptor = session->mCADescriptor; 141 142 Status status = cas->openSession(&sessionId); 143 if (!status.isOk()) { 144 ALOGE("Failed to open session: exception=%d, error=%d", 145 status.exceptionCode(), status.serviceSpecificErrorCode()); 146 goto l_fail; 147 } 148 149 cas->setSessionPrivateData(sessionId, descriptor.mPrivateData); 150 if (!status.isOk()) { 151 ALOGE("Failed to set private data: exception=%d, error=%d", 152 status.exceptionCode(), status.serviceSpecificErrorCode()); 153 goto l_fail; 154 } 155 156 status = casService->createDescrambler(descriptor.mSystemID, &descrambler); 157 if (!status.isOk() || descrambler == NULL) { 158 ALOGE("Failed to create descrambler: : exception=%d, error=%d", 159 status.exceptionCode(), status.serviceSpecificErrorCode()); 160 goto l_fail; 161 } 162 163 status = descrambler->setMediaCasSession(sessionId); 164 if (!status.isOk()) { 165 ALOGE("Failed to init descrambler: : exception=%d, error=%d", 166 status.exceptionCode(), status.serviceSpecificErrorCode()); 167 goto l_fail; 168 } 169 170 session->mSessionId = sessionId; 171 session->mDescrambler = descrambler; 172 sessionMap.add(descriptor.mPID, sessionId); 173 174 return OK; 175 176l_fail: 177 if (!sessionId.empty()) { 178 cas->closeSession(sessionId); 179 } 180 if (descrambler != NULL) { 181 descrambler->release(); 182 } 183 return NO_INIT; 184} 185 186void ATSParser::CasManager::ProgramCasManager::closeSession( 187 const sp<ICas>& cas, const CasSession &casSession) { 188 if (casSession.mDescrambler != NULL) { 189 casSession.mDescrambler->release(); 190 } 191 if (!casSession.mSessionId.empty()) { 192 cas->closeSession(casSession.mSessionId); 193 } 194} 195 196void ATSParser::CasManager::ProgramCasManager::closeAllSessions( 197 const sp<ICas>& cas) { 198 if (mHasProgramCas) { 199 closeSession(cas, mProgramCas); 200 } 201 for (size_t index = 0; index < mStreamPidToCasMap.size(); index++) { 202 closeSession(cas, mStreamPidToCasMap.editValueAt(index)); 203 } 204} 205 206//////////////////////////////////////////////////////////////////////////////// 207 208ATSParser::CasManager::CasManager() : mSystemId(-1) {} 209 210ATSParser::CasManager::~CasManager() { 211 // Explictly close the sessions opened by us, since the CAS object is owned 212 // by the app and may not go away after the parser is destroyed, and the app 213 // may not have information about the sessions. 214 if (mICas != NULL) { 215 for (size_t index = 0; index < mProgramCasMap.size(); index++) { 216 mProgramCasMap.editValueAt(index)->closeAllSessions(mICas); 217 } 218 } 219} 220 221bool ATSParser::CasManager::setSystemId(int32_t CA_system_ID) { 222 if (mSystemId == -1) { 223 // Verify the CA_system_ID is within range on the first program 224 if (CA_system_ID < 0 || CA_system_ID > 0xffff) { 225 ALOGE("Invalid CA_system_id: %d", CA_system_ID); 226 return false; 227 } 228 mSystemId = CA_system_ID; 229 } else if (mSystemId != CA_system_ID) { 230 // All sessions need to be under the same CA system 231 ALOGE("Multiple CA systems not allowed: %d vs %d", 232 mSystemId, CA_system_ID); 233 return false; 234 } 235 return true; 236} 237 238status_t ATSParser::CasManager::setMediaCas(const sp<ICas> &cas) { 239 if (cas == NULL) { 240 ALOGE("setMediaCas: received NULL object"); 241 return BAD_VALUE; 242 } 243 if (mICas != NULL) { 244 ALOGW("setMediaCas: already set"); 245 return ALREADY_EXISTS; 246 } 247 for (size_t index = 0; index < mProgramCasMap.size(); index++) { 248 status_t err; 249 if ((err = mProgramCasMap.editValueAt( 250 index)->setMediaCas(cas, mCAPidToSessionIdMap)) != OK) { 251 return err; 252 } 253 } 254 mICas = cas; 255 return OK; 256} 257 258bool ATSParser::CasManager::addProgram( 259 unsigned programNumber, const CADescriptor &descriptor) { 260 if (!setSystemId(descriptor.mSystemID)) { 261 return false; 262 } 263 264 ssize_t index = mProgramCasMap.indexOfKey(programNumber); 265 if (index < 0) { 266 ALOGV("addProgram: programNumber=%d, CA_system_ID=0x%x", 267 programNumber, descriptor.mSystemID); 268 mProgramCasMap.add(programNumber, 269 new ProgramCasManager(programNumber, descriptor)); 270 mCAPidSet.insert(descriptor.mPID); 271 } 272 return true; 273} 274 275bool ATSParser::CasManager::addStream( 276 unsigned programNumber, unsigned elementaryPID, 277 const CADescriptor &descriptor) { 278 if (!setSystemId(descriptor.mSystemID)) { 279 return false; 280 } 281 282 ssize_t index = mProgramCasMap.indexOfKey(programNumber); 283 sp<ProgramCasManager> programCasManager; 284 if (index < 0) { 285 ALOGV("addProgram (no CADescriptor): programNumber=%d", programNumber); 286 programCasManager = new ProgramCasManager(programNumber); 287 mProgramCasMap.add(programNumber, programCasManager); 288 } else { 289 programCasManager = mProgramCasMap.editValueAt(index); 290 } 291 if (programCasManager->addStream(elementaryPID, descriptor)) { 292 mCAPidSet.insert(descriptor.mPID); 293 } 294 return true; 295} 296 297bool ATSParser::CasManager::getCasInfo( 298 unsigned programNumber, unsigned elementaryPID, 299 int32_t *systemId, sp<IDescrambler> *descrambler, 300 std::vector<uint8_t> *sessionId) const { 301 ssize_t index = mProgramCasMap.indexOfKey(programNumber); 302 if (index < 0) { 303 return false; 304 } 305 *systemId = mSystemId; 306 return mProgramCasMap[index]->getCasSession( 307 elementaryPID, descrambler, sessionId); 308} 309 310bool ATSParser::CasManager::isCAPid(unsigned pid) { 311 return mCAPidSet.find(pid) != mCAPidSet.end(); 312} 313 314bool ATSParser::CasManager::parsePID(ABitReader *br, unsigned pid) { 315 ssize_t index = mCAPidToSessionIdMap.indexOfKey(pid); 316 if (index < 0) { 317 return false; 318 } 319 MediaCas::ParcelableCasData ecm(br->data(), br->numBitsLeft() / 8); 320 Status status = mICas->processEcm(mCAPidToSessionIdMap[index], ecm); 321 if (!status.isOk()) { 322 ALOGE("Failed to process ECM: exception=%d, error=%d", 323 status.exceptionCode(), status.serviceSpecificErrorCode()); 324 } 325 return true; // handled 326} 327 328} // namespace android 329