| /* |
| * Copyright (C) 2008 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 <string> |
| #include <memory> |
| #include <iostream> |
| #include <list> |
| #include <algorithm> |
| using namespace std; |
| |
| #include "BearerData.h" |
| #include "UserData.h" |
| #include "SmsConstants.h" |
| #include "HexDump.h" |
| #include "IccUtils.h" |
| #include "BitwiseInputStream.h" |
| #include "CdmaSmsCbProgramData.h"; |
| #include "SmsHeader.h" |
| #include "GsmAlphabet.h" |
| #include "SmsEnvelope.h" |
| |
| extern "C" { |
| #include <glib.h> |
| } |
| |
| BearerData::BearerData() { |
| // TODO Auto-generated constructor stub |
| |
| } |
| |
| BearerData::~BearerData() { |
| // TODO Auto-generated destructor stub |
| } |
| |
| /** |
| * Decodes a CDMA style BCD byte like {@link #gsmBcdByteToInt}, but |
| * opposite nibble format. The least significant BCD digit |
| * is in the least significant nibble and the most significant |
| * is in the most significant nibble. |
| */ |
| int BearerData::cdmaBcdByteToInt(uint8_t b) { |
| int ret = 0; |
| |
| // treat out-of-range BCD values as 0 |
| if ((b & 0xf0) <= 0x90) { |
| ret = ((b >> 4) & 0xf) * 10; |
| } |
| |
| if ((b & 0x0f) <= 0x09) { |
| ret += (b & 0xf); |
| } |
| |
| return ret; |
| } |
| |
| void BearerData::fromByteArray(std::vector<uint8_t> data, struct tm& ts) { |
| // C.S0015-B v2.0, 4.5.4: range is 1996-2095 |
| int year = cdmaBcdByteToInt(data[0]); |
| if (year > 99 || year < 0) { |
| ts = {0}; |
| return; |
| } |
| ts.tm_year = year /*>= 96 ? year + 1900 : year + 2000*/; //TBD |
| int month = cdmaBcdByteToInt(data[1]); |
| if (month < 1 || month > 12) { |
| ts = {0}; |
| return; |
| } |
| ts.tm_mon = month - 1; |
| int day = cdmaBcdByteToInt(data[2]); |
| if (day < 1 || day > 31) { |
| ts = {0}; |
| return; |
| } |
| ts.tm_mday = day; |
| int hour = cdmaBcdByteToInt(data[3]); |
| if (hour < 0 || hour > 23) { |
| ts = {0}; |
| return; |
| } |
| ts.tm_hour = hour; |
| int minute = cdmaBcdByteToInt(data[4]); |
| if (minute < 0 || minute > 59) { |
| ts = {0}; |
| return; |
| } |
| ts.tm_min = minute; |
| int second = cdmaBcdByteToInt(data[5]); |
| if (second < 0 || second > 59) { |
| ts = {0}; |
| return; |
| } |
| ts.tm_sec = second; |
| } |
| |
| void BearerData::encodeMessageId(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| outStream->write(8, 3); |
| outStream->write(4, bData->messageType); |
| outStream->write(8, bData->messageId >> 8); |
| outStream->write(8, bData->messageId); |
| outStream->write(1, bData->hasUserDataHeader ? 1 : 0); |
| outStream->skip(3); |
| } |
| |
| uint32_t BearerData::countAsciiSeptets(char* msg, bool force) { |
| string strmsg(msg); |
| int msgLen = strmsg.length(); |
| if (force) |
| return msgLen; |
| for (int i = 0; i < msgLen; i++) { |
| if (UserData::charToAscii.find(msg[i]) == UserData::charToAscii.end()) { |
| return -1; |
| } |
| } |
| return msgLen; |
| } |
| |
| std::vector<uint8_t> BearerData::encodeUtf16(std::string msg) { |
| GError *error = NULL; |
| gsize bytes_written = 0; |
| std::vector<uint8_t> data; |
| char *ret = g_convert (msg.c_str(), -1, "UCS-2BE", "UTF-8", NULL, &bytes_written, &error); |
| if (!ret) { |
| if (error) { |
| cout << "failed to convert UTF-8 to UCS-2BE character set: " << (error->code) << error->message << endl; |
| g_error_free (error); |
| } |
| return data; |
| } |
| for (int i = 0; i < bytes_written; i++) { |
| data.push_back(ret[i]); |
| } |
| g_free (ret); |
| return data; |
| } |
| |
| //release byte[] |
| std::vector<uint8_t> BearerData::encode7bitAscii(std::string msgs, bool force) { |
| string msg(msgs); |
| shared_ptr<BitwiseOutputStream> outStream = make_shared<BitwiseOutputStream>( |
| msg.length()); |
| int msgLen = msg.length(); |
| for (int i = 0; i < msgLen; i++) { |
| uint8_t charCode; |
| try { |
| charCode = UserData::charToAscii.at(msg[i]); |
| outStream->write(7, charCode); |
| } catch (const out_of_range &e) { |
| if (force) { |
| outStream->write(7, UserData::UNENCODABLE_7_BIT_CHAR); |
| } |
| //TBD log |
| } |
| } |
| return outStream->toByteVector(); |
| } |
| |
| BearerData::Gsm7bitCodingResult BearerData::encode7bitGsm(std::string msg, int septetOffset, bool force) { |
| std::vector<uint8_t> fullData = GsmAlphabet::stringToGsm7BitPacked(msg, septetOffset, !force, 0, 0); |
| Gsm7bitCodingResult result; |
| result.data.insert(result.data.begin(), fullData.begin() + 1, fullData.end()); |
| result.septets = fullData[0] & 0x00FF; |
| return result; |
| } |
| |
| void BearerData::encode7bitEms(std::shared_ptr<UserData> uData, std::vector<uint8_t> udhData, bool force) { |
| int udhBytes = udhData.size() + 1; // Add length octet. |
| int udhSeptets = ((udhBytes * 8) + 6) / 7; |
| BearerData::Gsm7bitCodingResult gcr = encode7bitGsm(uData->payloadStr, udhSeptets, force); |
| uData->msgEncoding = UserData::ENCODING_GSM_7BIT_ALPHABET; |
| uData->msgEncodingSet = true; |
| uData->numFields = gcr.septets; |
| uData->payload = gcr.data; |
| uData->payload.insert(uData->payload.begin()+1, udhData.begin(), udhData.end()); |
| uData->payload[0] = (uint8_t)udhData.size(); |
| } |
| |
| void BearerData::encode16bitEms(std::shared_ptr<UserData> uData, std::vector<uint8_t> udhData) { |
| std::vector<uint8_t> payload = encodeUtf16(uData->payloadStr); |
| int udhBytes = udhData.size() + 1; // Add length octet. |
| int udhCodeUnits = (udhBytes + 1) / 2; |
| int payloadCodeUnits = payload.size() / 2; |
| uData->msgEncoding = UserData::ENCODING_UNICODE_16; |
| uData->msgEncodingSet = true; |
| uData->numFields = udhCodeUnits + payloadCodeUnits; |
| uData->payload.push_back(udhData.size()); |
| (uData->payload).insert((uData->payload).begin() + 1, udhData.begin(), udhData.end()); |
| (uData->payload).insert((uData->payload).begin() + udhBytes, payload.begin(), payload.end()); |
| } |
| |
| void BearerData::encodeOctetEms(std::shared_ptr<UserData> uData, std::vector<uint8_t> udhData) { |
| int udhBytes = udhData.size() + 1; |
| uData->msgEncoding = UserData::ENCODING_OCTET; |
| uData->msgEncodingSet = true; |
| uData->numFields = udhBytes + uData->payload.size(); |
| //byte[] payload = new byte[uData.numFields]; |
| std::vector<uint8_t> payload; |
| uData->payload.push_back(udhData.size()); |
| (uData->payload).insert((uData->payload).begin() + 1, udhData.begin(), udhData.end()); |
| (uData->payload).insert((uData->payload).begin() + udhBytes, payload.begin(), payload.end()); |
| } |
| |
| void BearerData::encodeEmsUserDataPayload(std::shared_ptr<UserData> uData) { |
| std::vector<uint8_t> headerData = SmsHeader::toByteArray(uData->userDataHeader); |
| if (uData->msgEncodingSet) { |
| if (uData->msgEncoding == UserData::ENCODING_GSM_7BIT_ALPHABET) { |
| encode7bitEms(uData, headerData, true); |
| } else if (uData->msgEncoding == UserData::ENCODING_OCTET) { |
| encodeOctetEms(uData, headerData); |
| } else if (uData->msgEncoding == UserData::ENCODING_UNICODE_16) { |
| encode16bitEms(uData, headerData); |
| } else { |
| cout << "unsupported EMS user data encoding (" << uData->msgEncoding << ")" <<endl; |
| } |
| } else { |
| encode7bitEms(uData, headerData, false); |
| } |
| } |
| //maybe free memory |
| void BearerData::encodeUserDataPayload(std::shared_ptr<UserData> uData) { |
| if ((uData->payloadStr.empty()) |
| && (uData->msgEncoding != UserData::ENCODING_OCTET)) { |
| //Rlog.e(LOG_TAG, "user data with null payloadStr"); |
| uData->payloadStr = string(""); |
| } |
| |
| if (uData->userDataHeader != nullptr) { |
| encodeEmsUserDataPayload(uData); |
| return; |
| } |
| |
| if (uData->msgEncodingSet) { |
| if (uData->msgEncoding == UserData::ENCODING_OCTET) { |
| if (uData->payload.empty()) { |
| //Rlog.e(LOG_TAG, "user data with octet encoding but null payload"); |
| uData->payload.clear(); |
| uData->numFields = 0; |
| } else { |
| uData->numFields = uData->payload.size(); //maybe wrong? |
| } |
| } else { |
| if (uData->payloadStr.empty()) { |
| //Rlog.e(LOG_TAG, "non-octet user data with null payloadStr"); |
| uData->payloadStr = string(""); |
| } |
| // if (uData.msgEncoding == UserData::ENCODING_GSM_7BIT_ALPHABET) { |
| // Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, 0, true); |
| // uData.payload = gcr.data; |
| // uData.numFields = gcr.septets; |
| // } else |
| |
| if (uData->msgEncoding == UserData::ENCODING_7BIT_ASCII) { |
| uData->payload = encode7bitAscii(uData->payloadStr, true); |
| uData->numFields = uData->payloadStr.size(); |
| } else if (uData->msgEncoding == UserData::ENCODING_UNICODE_16) { |
| uData->payload = encodeUtf16(uData->payloadStr); |
| uData->numFields = string(uData->payloadStr).length(); |
| // } else if (uData->msgEncoding == UserData::ENCODING_SHIFT_JIS) { |
| // uData->payload = encodeShiftJis(uData->payloadStr); |
| // uData->numFields = string(uData->payloadStr).length(); |
| } else { |
| cout << "unsupported user data encoding (" << uData->msgEncoding << ")" |
| << endl; |
| } |
| } |
| } else { |
| uData->payload = encode7bitAscii(uData->payloadStr, false); |
| string str1 = HexDump::toHexString(uData->payload); |
| //std::cout << "uData->payload: " << str1 << endl; |
| uData->msgEncoding = UserData::ENCODING_7BIT_ASCII; |
| // try { |
| // } catch (CodingException ex) { |
| // uData.payload = encodeUtf16(uData.payloadStr); |
| // uData.msgEncoding = UserData.ENCODING_UNICODE_16; |
| // } |
| uData->numFields = uData->payloadStr.size(); |
| uData->msgEncodingSet = true; |
| } |
| } |
| |
| void BearerData::encodeUserData(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| /* |
| * TODO(cleanup): Do we really need to set userData.payload as |
| * a side effect of encoding? If not, we could avoid data |
| * copies by passing outStream directly. |
| */ |
| encodeUserDataPayload(bData->userData); |
| bData->hasUserDataHeader = false; //bData->userData->userDataHeader != null; |
| |
| int length = bData->userData->payload.size(); |
| if (length > SmsConstants::MAX_USER_DATA_BYTES) { |
| cout << "encoded user data too large (" << bData->userData->payload.size() |
| << " > " << SmsConstants::MAX_USER_DATA_BYTES << " bytes)" << endl; |
| return; |
| } |
| |
| /* |
| * TODO(cleanup): figure out what the right answer is WRT paddingBits field |
| * |
| * userData->pad->ingBits = (userData->payload.length * 8) - (userData.numFields * 7); |
| * userData.paddingBits = 0; // XXX this seems better, but why? |
| * |
| */ |
| int dataBits = (length * 8) - (bData->userData->paddingBits); |
| int paramBits = dataBits + 13; |
| if (((bData->userData->msgEncoding) |
| == UserData::ENCODING_IS91_EXTENDED_PROTOCOL) |
| || ((bData->userData->msgEncoding) == UserData::ENCODING_GSM_DCS)) { |
| paramBits += 8; |
| } |
| int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0); |
| int paddingBits = (paramBytes * 8) - paramBits; |
| outStream->write(8, paramBytes); |
| outStream->write(5, bData->userData->msgEncoding); |
| if ((bData->userData->msgEncoding == UserData::ENCODING_IS91_EXTENDED_PROTOCOL) |
| || (bData->userData->msgEncoding == UserData::ENCODING_GSM_DCS)) { |
| outStream->write(8, bData->userData->msgType); |
| } |
| outStream->write(8, (bData->userData)->numFields); |
| outStream->writeByteVector(dataBits, (bData->userData)->payload); |
| if (paddingBits > 0) |
| outStream->write(paddingBits, 0); |
| } |
| |
| void BearerData::encodeReplyOption(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| outStream->write(8, 1); |
| outStream->write(1, bData->userAckReq ? 1 : 0); |
| outStream->write(1, bData->deliveryAckReq ? 1 : 0); |
| outStream->write(1, bData->readAckReq ? 1 : 0); |
| outStream->write(1, bData->reportReq ? 1 : 0); |
| outStream->write(4, 0); |
| } |
| |
| //free memory |
| std::vector<uint8_t> BearerData::encodeDtmfSmsAddress(std::string address) { |
| int digits = address.length(); |
| int dataBits = digits * 4; |
| int dataBytes = (dataBits / 8); |
| dataBytes += (dataBits % 8) > 0 ? 1 : 0; |
| //uint8_t* rawData = new uint8_t[dataBytes]; |
| std::vector<uint8_t> rawData; |
| for (int i = 0; i < digits; i++) { |
| char c = address[i]; |
| int val = 0; |
| if ((c >= '1') && (c <= '9')) |
| val = c - '0'; |
| else if (c == '0') |
| val = 10; |
| else if (c == '*') |
| val = 11; |
| else if (c == '#') |
| val = 12; |
| else |
| return rawData; |
| int size = rawData.size(); |
| if ((i / 2) < size) { |
| rawData[i / 2] |= val << (4 - ((i % 2) * 4)); |
| } else { |
| rawData.push_back(val << (4 - ((i % 2) * 4))); |
| } |
| } |
| return rawData; |
| } |
| |
| void BearerData::encodeCdmaSmsAddress(std::shared_ptr<CdmaSmsAddress> addr) { |
| if (addr->digitMode == CdmaSmsAddress::DIGIT_MODE_8BIT_CHAR) { |
| //TBD |
| // try { |
| // addr.origBytes = addr.address.getBytes("US-ASCII"); |
| // } catch (java.io.UnsupportedEncodingException ex) { |
| // throw new CodingException("invalid SMS address, cannot convert to ASCII"); |
| // } |
| } else { |
| addr->origBytes = encodeDtmfSmsAddress(addr->address); |
| } |
| } |
| |
| void BearerData::encodeCallbackNumber(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| auto addr = bData->callbackNumber; |
| encodeCdmaSmsAddress(addr); |
| int paramBits = 9; |
| int dataBits = 0; |
| if (addr->digitMode == CdmaSmsAddress::DIGIT_MODE_8BIT_CHAR) { |
| paramBits += 7; |
| dataBits = addr->numberOfDigits * 8; |
| } else { |
| dataBits = addr->numberOfDigits * 4; |
| } |
| paramBits += dataBits; |
| int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0); |
| int paddingBits = (paramBytes * 8) - paramBits; |
| outStream->write(8, paramBytes); |
| outStream->write(1, addr->digitMode); |
| if (addr->digitMode == CdmaSmsAddress::DIGIT_MODE_8BIT_CHAR) { |
| outStream->write(3, addr->ton); |
| outStream->write(4, addr->numberPlan); |
| } |
| outStream->write(8, addr->numberOfDigits); |
| outStream->writeByteVector(dataBits, addr->origBytes); |
| if (paddingBits > 0) |
| outStream->write(paddingBits, 0); |
| } |
| |
| void BearerData::encodeMsgStatus(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| outStream->write(8, 1); |
| outStream->write(2, bData->errorClass); |
| outStream->write(6, bData->messageStatus); |
| } |
| void BearerData::encodeMsgCount(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| outStream->write(8, 1); |
| outStream->write(8, bData->numberOfMessages); |
| } |
| void BearerData::encodeValidityPeriodRel(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| outStream->write(8, 1); |
| outStream->write(8, bData->validityPeriodRelative); |
| } |
| void BearerData::encodePrivacyIndicator(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| outStream->write(8, 1); |
| outStream->write(2, bData->privacy); |
| outStream->skip(6); |
| } |
| void BearerData::encodeLanguageIndicator(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| outStream->write(8, 1); |
| outStream->write(8, bData->language); |
| } |
| void BearerData::encodeDisplayMode(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| outStream->write(8, 1); |
| outStream->write(2, bData->displayMode); |
| outStream->skip(6); |
| } |
| void BearerData::encodePriorityIndicator(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| outStream->write(8, 1); |
| outStream->write(2, bData->priority); |
| outStream->skip(6); |
| } |
| void BearerData::encodeMsgDeliveryAlert(BearerData* bData, |
| shared_ptr<BitwiseOutputStream> outStream) { |
| outStream->write(8, 1); |
| outStream->write(2, bData->alert); |
| outStream->skip(6); |
| } |
| std::vector<uint8_t> BearerData::encode(BearerData* bData) { |
| bData->hasUserDataHeader = false; //((bData->userData != nullptr) && (bData->userData->userDataHeader != null)); |
| shared_ptr<BitwiseOutputStream> outStream = make_shared<BitwiseOutputStream>( |
| 200); |
| outStream->write(8, SUBPARAM_MESSAGE_IDENTIFIER); |
| encodeMessageId(bData, outStream); |
| if (bData->userData != nullptr) { |
| outStream->write(8, SUBPARAM_USER_DATA); |
| encodeUserData(bData, outStream); |
| //cout << "userData" << endl; |
| } |
| if (bData->callbackNumber != nullptr) { |
| //cout << "callbackNumber" << endl; |
| outStream->write(8, SUBPARAM_CALLBACK_NUMBER); |
| encodeCallbackNumber(bData, outStream); |
| } |
| if (bData->userAckReq || bData->deliveryAckReq || bData->readAckReq |
| || bData->reportReq) { |
| //cout << "userAckReq" << endl; |
| outStream->write(8, SUBPARAM_REPLY_OPTION); |
| encodeReplyOption(bData, outStream); |
| } |
| if (bData->numberOfMessages != 0) { |
| //cout << "numberOfMessages" << endl; |
| outStream->write(8, SUBPARAM_NUMBER_OF_MESSAGES); |
| encodeMsgCount(bData, outStream); |
| } |
| if (bData->validityPeriodRelativeSet) { |
| //cout << "validityPeriodRelativeSet" << endl; |
| outStream->write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE); |
| encodeValidityPeriodRel(bData, outStream); |
| } |
| if (bData->privacyIndicatorSet) { |
| //cout << "privacyIndicatorSet" << endl; |
| outStream->write(8, SUBPARAM_PRIVACY_INDICATOR); |
| encodePrivacyIndicator(bData, outStream); |
| } |
| if (bData->languageIndicatorSet) { |
| //cout << "languageIndicatorSet" << endl; |
| outStream->write(8, SUBPARAM_LANGUAGE_INDICATOR); |
| encodeLanguageIndicator(bData, outStream); |
| } |
| if (bData->displayModeSet) { |
| //cout << "displayModeSet" << endl; |
| outStream->write(8, SUBPARAM_MESSAGE_DISPLAY_MODE); |
| encodeDisplayMode(bData, outStream); |
| } |
| if (bData->priorityIndicatorSet) { |
| //cout << "priorityIndicatorSet" << endl; |
| outStream->write(8, SUBPARAM_PRIORITY_INDICATOR); |
| encodePriorityIndicator(bData, outStream); |
| } |
| if (bData->alertIndicatorSet) { |
| //cout << "alertIndicatorSet" << endl; |
| outStream->write(8, SUBPARAM_ALERT_ON_MESSAGE_DELIVERY); |
| encodeMsgDeliveryAlert(bData, outStream); |
| } |
| if (bData->messageStatusSet) { |
| //cout << "messageStatusSet" << endl; |
| outStream->write(8, SUBPARAM_MESSAGE_STATUS); |
| encodeMsgStatus(bData, outStream); |
| } |
| // if (bData->serviceCategoryProgramResults != null) { |
| // outStream->write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS); |
| // encodeScpResults(bData, outStream); |
| // } |
| return outStream->toByteVector(); |
| } |
| |
| std::shared_ptr<BearerData> BearerData::decode(std::vector<uint8_t> smsData) { |
| return decode(smsData, 0); |
| } |
| |
| bool BearerData::decodeMessageId(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 3 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->messageType = inStream->read(4); |
| bData->messageId = inStream->read(8) << 8; |
| bData->messageId |= inStream->read(8); |
| bData->hasUserDataHeader = (inStream->read(1) == 1); |
| inStream->skip(3); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| // Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " + |
| // (decodeSuccess ? "succeeded" : "failed") + |
| // " (extra bits = " + paramBits + ")"); |
| } |
| inStream->skip(paramBits); |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeUserData(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| uint32_t paramBits = inStream->read((uint32_t) 8) * 8; |
| bData->userData = std::make_shared<UserData>(); |
| bData->userData->msgEncoding = inStream->read(5); |
| bData->userData->msgEncodingSet = true; |
| bData->userData->msgType = 0; |
| int consumedBits = 5; |
| if ((bData->userData->msgEncoding == UserData::ENCODING_IS91_EXTENDED_PROTOCOL) |
| || (bData->userData->msgEncoding == UserData::ENCODING_GSM_DCS)) { |
| bData->userData->msgType = inStream->read(8); |
| consumedBits += 8; |
| } |
| bData->userData->numFields = inStream->read(8); |
| consumedBits += 8; |
| int dataBits = paramBits - consumedBits; |
| bData->userData->payload = inStream->readByteVector(dataBits); |
| return true; |
| } |
| |
| bool BearerData::decodeUserResponseCode(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const uint32_t EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| uint32_t paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->userResponseCode = inStream->read(8); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| // Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " + |
| // (decodeSuccess ? "succeeded" : "failed") + |
| // " (extra bits = " + paramBits + ")"); |
| } |
| inStream->skip(paramBits); |
| bData->userResponseCodeSet = decodeSuccess; |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeReplyOption(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->userAckReq = (inStream->read(1) == 1); |
| bData->deliveryAckReq = (inStream->read(1) == 1); |
| bData->readAckReq = (inStream->read(1) == 1); |
| bData->reportReq = (inStream->read(1) == 1); |
| inStream->skip(4); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| // Rlog->d(LOG_TAG, "REPLY_OPTION decode " + |
| // (decodeSuccess ? "succeeded" : "failed") + |
| // " (extra bits = " + paramBits + ")"); |
| } |
| inStream->skip(paramBits); |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeMsgCount(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->numberOfMessages = IccUtils::cdmaBcdByteToInt( |
| (uint8_t) inStream->read(8)); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| // Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " + |
| // (decodeSuccess ? "succeeded" : "failed") + |
| // " (extra bits = " + paramBits + ")"); |
| } |
| inStream->skip(paramBits); |
| return decodeSuccess; |
| } |
| |
| std::string BearerData::decodeDtmfSmsAddress(std::vector<uint8_t> rawData, |
| int numFields) { |
| /* DTMF 4-bit digit encoding, defined in at |
| * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */ |
| string strBuf; |
| for (int i = 0; i < numFields; i++) { |
| int val = 0x0F & (rawData[i / 2] >> (4 - ((i % 2) * 4))); |
| if ((val >= 1) && (val <= 9)) |
| strBuf.append(to_string(val)); |
| else if (val == 10) |
| strBuf.push_back('0'); |
| else if (val == 11) |
| strBuf.push_back('*'); |
| else if (val == 12) |
| strBuf.push_back('#'); |
| else |
| throw runtime_error( |
| "invalid SMS address DTMF code (" + std::to_string(val) + ")"); |
| } |
| return strBuf; |
| } |
| |
| void BearerData::decodeSmsAddress(std::shared_ptr<CdmaSmsAddress> addr) { |
| if (addr->digitMode == CdmaSmsAddress::DIGIT_MODE_8BIT_CHAR) { |
| //android not support |
| // try { |
| // /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually |
| // * just 7-bit ASCII encoding, with the MSB being zero. */ |
| // addr->address = new string(addr->origBytes, addr->origBytes_length); |
| // } catch (exception& ex) { |
| // throw runtime_error("invalid SMS address ASCII code"); |
| // } |
| } else { |
| addr->address = decodeDtmfSmsAddress(addr->origBytes, addr->numberOfDigits); |
| } |
| } |
| bool BearerData::decodeCallbackNumber(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; //at least |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits < EXPECTED_PARAM_SIZE) { |
| inStream->skip(paramBits); |
| return false; |
| } |
| std::shared_ptr<CdmaSmsAddress> addr = make_shared<CdmaSmsAddress>(); |
| addr->digitMode = inStream->read(1); |
| uint8_t fieldBits = 4; |
| uint8_t consumedBits = 1; |
| if (addr->digitMode == CdmaSmsAddress::DIGIT_MODE_8BIT_CHAR) { |
| addr->ton = inStream->read(3); |
| addr->numberPlan = inStream->read(4); |
| fieldBits = 8; |
| consumedBits += 7; |
| } |
| addr->numberOfDigits = inStream->read(8); |
| consumedBits += 8; |
| int remainingBits = paramBits - consumedBits; |
| int dataBits = addr->numberOfDigits * fieldBits; |
| int paddingBits = remainingBits - dataBits; |
| if (remainingBits < dataBits) { |
| throw runtime_error( |
| string("CALLBACK_NUMBER subparam encoding size error (") |
| + string("remainingBits + ") + std::to_string(remainingBits) |
| + string(", dataBits + ") + std::to_string(dataBits) |
| + string(", paddingBits + ") + std::to_string(paddingBits) |
| + string(")")); |
| } |
| addr->origBytes = inStream->readByteVector(dataBits); |
| inStream->skip(paddingBits); |
| decodeSmsAddress(addr); |
| bData->callbackNumber = addr; |
| return true; |
| } |
| |
| bool BearerData::decodeMsgStatus(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->errorClass = inStream->read(2); |
| bData->messageStatus = inStream->read(6); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| // Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " + |
| // (decodeSuccess ? "succeeded" : "failed") + |
| // " (extra bits = " + paramBits + ")"); |
| } |
| inStream->skip(paramBits); |
| bData->messageStatusSet = decodeSuccess; |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeMsgCenterTimeStamp(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 6 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| fromByteArray(inStream->readByteVector(6 * 8), bData->msgCenterTimeStamp); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| /* Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " + |
| (decodeSuccess ? "succeeded" : "failed") + |
| " (extra bits = " + paramBits + ")");*/ |
| } |
| inStream->skip(paramBits); |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeValidityAbs(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 6 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| fromByteArray(inStream->readByteVector(6 * 8), |
| bData->validityPeriodAbsolute); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| // Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " + |
| // (decodeSuccess ? "succeeded" : "failed") + |
| // " (extra bits = " + paramBits + ")"); |
| } |
| inStream->skip(paramBits); |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeValidityRel(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| //bData->deferredDeliveryTimeRelative = inStream->read(8); |
| bData->validityPeriodRelative = inStream->read(8); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| /* Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " + |
| (decodeSuccess ? "succeeded" : "failed") + |
| " (extra bits = " + paramBits + ")");*/ |
| } |
| inStream->skip(paramBits); |
| //bData->deferredDeliveryTimeRelativeSet = decodeSuccess; |
| bData->validityPeriodRelativeSet = decodeSuccess; |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeDeferredDeliveryAbs(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 6 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| fromByteArray(inStream->readByteVector(6 * 8), |
| bData->deferredDeliveryTimeAbsolute); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| /* Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " + |
| (decodeSuccess ? "succeeded" : "failed") + |
| " (extra bits = " + paramBits + ")");*/ |
| } |
| inStream->skip(paramBits); |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeDeferredDeliveryRel(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| //bData->validityPeriodRelative = inStream->read(8); |
| bData->deferredDeliveryTimeRelative = inStream->read(8); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| /* Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " + |
| (decodeSuccess ? "succeeded" : "failed") + |
| " (extra bits = " + paramBits + ")");*/ |
| } |
| inStream->skip(paramBits); |
| //bData->validityPeriodRelativeSet = decodeSuccess; |
| bData->deferredDeliveryTimeRelativeSet = decodeSuccess; |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodePrivacyIndicator(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->privacy = inStream->read(2); |
| inStream->skip(6); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| /* Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " + |
| (decodeSuccess ? "succeeded" : "failed") + |
| " (extra bits = " + paramBits + ")");*/ |
| } |
| inStream->skip(paramBits); |
| bData->privacyIndicatorSet = decodeSuccess; |
| return decodeSuccess; |
| } |
| bool BearerData::decodeLanguageIndicator(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->language = inStream->read(8); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| /* Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " + |
| (decodeSuccess ? "succeeded" : "failed") + |
| " (extra bits = " + paramBits + ")");*/ |
| } |
| inStream->skip(paramBits); |
| bData->languageIndicatorSet = decodeSuccess; |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeDisplayMode(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->displayMode = inStream->read(2); |
| inStream->skip(6); |
| } |
| // if ((! decodeSuccess) || (paramBits > 0)) { |
| // Rlog.d(LOG_TAG, "DISPLAY_MODE decode " + |
| // (decodeSuccess ? "succeeded" : "failed") + |
| // " (extra bits = " + paramBits + ")"); |
| // } |
| inStream->skip(paramBits); |
| bData->displayModeSet = decodeSuccess; |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodePriorityIndicator(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->priority = inStream->read(2); |
| inStream->skip(6); |
| } |
| // if ((! decodeSuccess) || (paramBits > 0)) { |
| // Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " + |
| // (decodeSuccess ? "succeeded" : "failed") + |
| // " (extra bits = " + paramBits + ")"); |
| // } |
| inStream->skip(paramBits); |
| bData->priorityIndicatorSet = decodeSuccess; |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeMsgDeliveryAlert(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 1 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->alert = inStream->read(2); |
| inStream->skip(6); |
| } |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| // Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " + |
| // (decodeSuccess ? "succeeded" : "failed") + |
| // " (extra bits = " + paramBits + ")"); |
| } |
| inStream->skip(paramBits); |
| bData->alertIndicatorSet = decodeSuccess; |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeDepositIndex(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| const int EXPECTED_PARAM_SIZE = 2 * 8; |
| bool decodeSuccess = false; |
| int paramBits = inStream->read(8) * 8; |
| if (paramBits >= EXPECTED_PARAM_SIZE) { |
| paramBits -= EXPECTED_PARAM_SIZE; |
| decodeSuccess = true; |
| bData->depositIndex = (inStream->read(8) << 8) | inStream->read(8); |
| } |
| /* if ((! decodeSuccess) || (paramBits > 0)) { |
| Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " + |
| (decodeSuccess ? "succeeded" : "failed") + |
| " (extra bits = " + paramBits + ")"); |
| }*/ |
| inStream->skip(paramBits); |
| return decodeSuccess; |
| } |
| |
| int BearerData::getBitsForNumFields(int msgEncoding, int numFields) { |
| switch (msgEncoding) { |
| case UserData::ENCODING_OCTET: |
| case UserData::ENCODING_SHIFT_JIS: |
| case UserData::ENCODING_KOREAN: |
| case UserData::ENCODING_LATIN: |
| case UserData::ENCODING_LATIN_HEBREW: |
| return numFields * 8; |
| |
| case UserData::ENCODING_IA5: |
| case UserData::ENCODING_7BIT_ASCII: |
| case UserData::ENCODING_GSM_7BIT_ALPHABET: |
| return numFields * 7; |
| |
| case UserData::ENCODING_UNICODE_16: |
| return numFields * 16; |
| |
| default: |
| throw runtime_error("unsupported message encoding (" + msgEncoding + ')'); |
| } |
| } |
| |
| std::string BearerData::decodeUtf8(std::vector<uint8_t> data, int offset, int numFields) |
| { |
| std::string str; |
| for (int i = offset; i < (offset + numFields); i++) |
| { |
| str.push_back((char)data[i]); |
| } |
| return str; |
| } |
| |
| std::string BearerData::decodeLatin(std::vector<uint8_t> data, int offset, int numFields) |
| { |
| std::string str; |
| for (int i = offset; i < (offset + numFields); i++) |
| { |
| str.push_back(data[i]); |
| } |
| char* text = g_convert (str.c_str(), numFields, "UTF-8", "ISO−8859−1", NULL, NULL, NULL); |
| if(!text) { |
| printf("convert (Latin) result is null\n"); |
| return std::string(""); |
| } |
| std::string ret(text); |
| g_free(text); |
| return ret; |
| } |
| |
| std::string BearerData::decodeUtf16(std::vector<uint8_t> data, int offset, int numFields) |
| { |
| std::string str; |
| for (int i = offset; i < (offset + numFields*2); i++) |
| { |
| str.push_back((char)data[i]); |
| } |
| char* text = g_convert (str.c_str(), numFields*2, "UTF-8", "UCS-2BE", NULL, NULL, NULL); |
| if(!text) { |
| printf("convert (Utf16) result is null\n"); |
| return std::string(""); |
| } |
| std::string ret(text); |
| g_free(text); |
| text = NULL; |
| return ret; |
| } |
| |
| std::string BearerData::decode7bitAscii(std::vector<uint8_t> data, int offset, |
| int numFields) { |
| int headerSeptets = (offset * 8 + 6) / 7; |
| numFields -= headerSeptets; |
| std::string strBuf; |
| auto inStream = std::make_shared<BitwiseInputStream>(data); |
| int wantedBits = (headerSeptets + numFields) * 7; |
| if (inStream->available() < wantedBits) { |
| throw runtime_error( |
| "insufficient data (wanted " + std::to_string(wantedBits) |
| + " bits, but only have " + std::to_string(inStream->available()) |
| + ")"); |
| } |
| inStream->skip(headerSeptets * 7); |
| for (int i = 0; i < numFields; i++) { |
| int charCode = inStream->read(7); |
| if ((charCode >= UserData::ASCII_MAP_BASE_INDEX) |
| && (charCode <= UserData::ASCII_MAP_MAX_INDEX)) { |
| strBuf.push_back( |
| UserData::ASCII_MAP[charCode - UserData::ASCII_MAP_BASE_INDEX]); |
| } else if (charCode == UserData::ASCII_NL_INDEX) { |
| strBuf.push_back('\n'); |
| } else if (charCode == UserData::ASCII_CR_INDEX) { |
| strBuf.push_back('\r'); |
| } else { |
| /* For other charCodes, they are unprintable, and so simply use SPACE. */ |
| strBuf.push_back(' '); |
| } |
| } |
| return strBuf; |
| } |
| |
| std::string BearerData::decode7bitGsm(std::vector<uint8_t> data, int offset, |
| int numFields) { |
| // Start reading from the next 7-bit aligned boundary after offset. |
| int offsetBits = offset * 8; |
| int offsetSeptets = (offsetBits + 6) / 7; |
| numFields -= offsetSeptets; |
| int paddingBits = (offsetSeptets * 7) - offsetBits; |
| string result = GsmAlphabet::gsm7BitPackedToString(data, offset, numFields, |
| paddingBits, 0, 0); |
| if (result.empty()) { |
| throw runtime_error("7bit GSM decoding failed"); |
| } |
| return result; |
| } |
| |
| std::string BearerData::decodeGsmDcs(std::vector<uint8_t> data, int offset, |
| int numFields, int msgType) { |
| if ((msgType & 0xC0) != 0) { |
| throw runtime_error( |
| "unsupported coding group (" + std::to_string(msgType) + ")"); |
| } |
| |
| switch ((msgType >> 2) & 0x3) { |
| case UserData::ENCODING_GSM_DCS_7BIT: |
| return decode7bitGsm(data, offset, numFields); |
| // case UserData.ENCODING_GSM_DCS_8BIT: |
| // return decodeUtf8(data, offset, numFields); |
| // case UserData.ENCODING_GSM_DCS_16BIT: |
| // return decodeUtf16(data, offset, numFields); |
| default: |
| throw runtime_error( |
| "unsupported user msgType encoding (" + std::to_string(msgType) + ")"); |
| } |
| } |
| |
| void BearerData::decodeUserDataPayload(std::shared_ptr<UserData> userData, |
| bool hasUserDataHeader) { |
| int offset = 0; |
| //printf("1st,%s\n",userData->toString().c_str()); |
| if (hasUserDataHeader) { |
| int udhLen = userData->payload[0] & 0x00FF; |
| offset += udhLen + 1; |
| /* byte[] headerData = new byte[udhLen]; |
| System.arraycopy(userData.payload, 1, headerData, 0, udhLen)*/; |
| std::vector<uint8_t> v; |
| v.insert(v.end(), userData->payload.begin() + 1, |
| userData->payload.begin() + udhLen); |
| userData->userDataHeader = SmsHeader::fromByteArray(v); |
| //for test cdma long sms, SmsHeader::fromByteArray get the sequence number,but it will always be the last sequence, |
| //maybe overwriten by the last sms part. we print here directly as a temporary solution. |
| if(udhLen == 5 && (userData->payload[1] & 0x00FF)==0x00)//0x00 is the concatenated SMS id |
| { |
| printf("udhLen=%d,refNumber=%d,msgCount=%d, seqNumber=%d\n", udhLen, |
| userData->payload[3] & 0x00FF,userData->payload[4] & 0x00FF,userData->payload[5] & 0x00FF); |
| } |
| else{ |
| printf("udhLen=%d, not for test format!\n",udhLen); |
| } |
| } |
| //add for long sms test begin |
| //printf("offset: %d\n",offset); |
| //printf("2nd,%s\n",userData->toString().c_str()); |
| //add for long sms test end |
| switch (userData->msgEncoding) { |
| case UserData::ENCODING_OCTET: { |
| /* |
| * Octet decoding depends on the carrier service. |
| */ |
| bool decodingtypeUTF8 = true; |
| |
| // Strip off any padding bytes, meaning any differences between the length of the |
| // array and the target length specified by numFields. This is to avoid any |
| // confusion by code elsewhere that only considers the payload array length. |
| int copyLen = userData->numFields < userData->payload.size() ? userData->numFields : userData->payload.size(); |
| std::vector<uint8_t> payload; |
| payload.insert(payload.end(), userData->payload.begin(), userData->payload.begin() + copyLen); |
| userData->payload = payload; |
| |
| if (!decodingtypeUTF8) { |
| // There are many devices in the market that send 8bit text sms (latin encoded) as |
| // octet encoded. |
| userData->payloadStr = decodeLatin(userData->payload, offset, userData->numFields); |
| } else { |
| userData->payloadStr = decodeUtf8(userData->payload, offset, userData->numFields); |
| } |
| break; |
| } |
| case UserData::ENCODING_IA5: |
| case UserData::ENCODING_7BIT_ASCII: { |
| userData->payloadStr = decode7bitAscii(userData->payload, offset, userData->numFields); |
| break; |
| case UserData::ENCODING_UNICODE_16: |
| userData->payloadStr = decodeUtf16(userData->payload, offset, userData->numFields); |
| break; |
| } |
| case UserData::ENCODING_GSM_7BIT_ALPHABET: { |
| userData->payloadStr = decode7bitGsm(userData->payload, offset, userData->numFields); |
| break; |
| } |
| case UserData::ENCODING_LATIN: |
| userData->payloadStr = decodeLatin(userData->payload, offset, userData->numFields); |
| break; |
| // case UserData::ENCODING_SHIFT_JIS: |
| // // userData->payloadStr = decodeShiftJis(userData->payload, offset, userData->numFields); |
| // //TBD "unsupported user data encoding" |
| // break; |
| case UserData::ENCODING_GSM_DCS: { |
| userData->payloadStr = decodeGsmDcs(userData->payload, offset, |
| userData->numFields, userData->msgType); |
| break; |
| } |
| default: |
| printf("unsupported user data encoding : %\n" ,std::to_string(userData->msgEncoding)); |
| // throw runtime_error( |
| // "unsupported user data encoding (" |
| // + std::to_string(userData->msgEncoding) + ")"); |
| } |
| } |
| bool BearerData::decodeServiceCategoryProgramData( |
| std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream) { |
| if (inStream->available() < 13) { |
| throw runtime_error( |
| string("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only ") |
| + std::to_string(inStream->available()) |
| + string(" bits available")); |
| } |
| |
| int paramBits = inStream->read(8) * 8; |
| int msgEncoding = inStream->read(5); |
| paramBits -= 5; |
| |
| if (inStream->available() < paramBits) { |
| throw runtime_error( |
| string("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only ") |
| + std::to_string(inStream->available()) |
| + string(" bits available (") + std::to_string(paramBits) |
| + string(" bits expected)")); |
| } |
| |
| auto programDataList = make_shared<list<shared_ptr<CdmaSmsCbProgramData>>>(); |
| const int CATEGORY_FIELD_MIN_SIZE = 6 * 8; |
| bool decodeSuccess = false; |
| while (paramBits >= CATEGORY_FIELD_MIN_SIZE) { |
| int operation = inStream->read(4); |
| int category = (inStream->read(8) << 8) | inStream->read(8); |
| int language = inStream->read(8); |
| int maxMessages = inStream->read(8); |
| int alertOption = inStream->read(4); |
| int numFields = inStream->read(8); |
| paramBits -= CATEGORY_FIELD_MIN_SIZE; |
| |
| int textBits = getBitsForNumFields(msgEncoding, numFields); |
| if (paramBits < textBits) { |
| throw runtime_error( |
| string("category name is ") + std::to_string(textBits) |
| + string(" bits in length,") + string(" but there are only ") |
| + std::to_string(paramBits) + string(" bits available")); |
| } |
| |
| auto userData = make_shared<UserData>(); |
| userData->msgEncoding = msgEncoding; |
| userData->msgEncodingSet = true; |
| userData->numFields = numFields; |
| userData->payload = inStream->readByteVector(textBits); |
| paramBits -= textBits; |
| |
| decodeUserDataPayload(userData, false); |
| string categoryName = userData->payloadStr; |
| auto programData = std::make_shared<CdmaSmsCbProgramData>(operation, |
| category, language, maxMessages, alertOption, categoryName); |
| programDataList->push_back(programData); |
| |
| decodeSuccess = true; |
| } |
| |
| if ((!decodeSuccess) || (paramBits > 0)) { |
| // Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " + |
| // (decodeSuccess ? "succeeded" : "failed") + |
| // " (extra bits = " + paramBits + ')'); |
| } |
| |
| inStream->skip(paramBits); |
| bData->serviceCategoryProgramData = programDataList; |
| return decodeSuccess; |
| } |
| |
| bool BearerData::decodeReserved(std::shared_ptr<BearerData> bData, |
| std::shared_ptr<BitwiseInputStream> inStream, int subparamId) { |
| bool decodeSuccess = false; |
| int subparamLen = inStream->read(8); // SUBPARAM_LEN |
| int paramBits = subparamLen * 8; |
| if (paramBits <= inStream->available()) { |
| decodeSuccess = true; |
| inStream->skip(paramBits); |
| } |
| // Rlog.d(LOG_TAG, "RESERVED bearer data subparameter " + subparamId + " decode " |
| // + (decodeSuccess ? "succeeded" : "failed") + " (param bits = " + paramBits + ")"); |
| if (!decodeSuccess) { |
| throw runtime_error( |
| "RESERVED bearer data subparameter " + std::to_string(subparamId) |
| + " had invalid SUBPARAM_LEN " + std::to_string(subparamLen)); |
| } |
| |
| return decodeSuccess; |
| } |
| bool BearerData::isCmasAlertCategory(int category) { |
| return category >= SmsEnvelope::SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT |
| && category <= SmsEnvelope::SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE; |
| } |
| |
| int BearerData::serviceCategoryToCmasMessageClass(int serviceCategory) { |
| switch (serviceCategory) { |
| case SmsEnvelope::SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT: |
| return SmsCbCmasInfo::CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT; |
| |
| case SmsEnvelope::SERVICE_CATEGORY_CMAS_EXTREME_THREAT: |
| return SmsCbCmasInfo::CMAS_CLASS_EXTREME_THREAT; |
| |
| case SmsEnvelope::SERVICE_CATEGORY_CMAS_SEVERE_THREAT: |
| return SmsCbCmasInfo::CMAS_CLASS_SEVERE_THREAT; |
| |
| case SmsEnvelope::SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: |
| return SmsCbCmasInfo::CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY; |
| |
| case SmsEnvelope::SERVICE_CATEGORY_CMAS_TEST_MESSAGE: |
| return SmsCbCmasInfo::CMAS_CLASS_REQUIRED_MONTHLY_TEST; |
| |
| default: |
| return SmsCbCmasInfo::CMAS_CLASS_UNKNOWN; |
| } |
| } |
| void BearerData::decodeCmasUserData(std::shared_ptr<BearerData> bData, |
| int serviceCategory) { |
| //uint8_t array[bData->userData->payload.size()]; |
| //std::copy(bData->userData->payload.begin(), bData->userData->payload.end(), array); |
| auto inStream = make_shared<BitwiseInputStream>(bData->userData->payload); |
| |
| if (inStream->available() < 8) { |
| throw runtime_error("emergency CB with no CMAE_protocol_version"); |
| } |
| int protocolVersion = inStream->read(8); |
| if (protocolVersion != 0) { |
| throw runtime_error("unsupported CMAE_protocol_version " + protocolVersion); |
| } |
| |
| int messageClass = serviceCategoryToCmasMessageClass(serviceCategory); |
| int category = SmsCbCmasInfo::CMAS_CATEGORY_UNKNOWN; |
| int responseType = SmsCbCmasInfo::CMAS_RESPONSE_TYPE_UNKNOWN; |
| int severity = SmsCbCmasInfo::CMAS_SEVERITY_UNKNOWN; |
| int urgency = SmsCbCmasInfo::CMAS_URGENCY_UNKNOWN; |
| int certainty = SmsCbCmasInfo::CMAS_CERTAINTY_UNKNOWN; |
| |
| while (inStream->available() >= 16) { |
| int recordType = inStream->read(8); |
| int recordLen = inStream->read(8); |
| switch (recordType) { |
| case 0: // Type 0 elements (Alert text) |
| { |
| auto alertUserData = make_shared<UserData>(); |
| alertUserData->msgEncoding = inStream->read(5); |
| alertUserData->msgEncodingSet = true; |
| alertUserData->msgType = 0; |
| |
| int numFields; // number of chars to decode |
| switch (alertUserData->msgEncoding) { |
| case UserData::ENCODING_OCTET: |
| case UserData::ENCODING_LATIN: |
| numFields = recordLen - 1; // subtract 1 byte for encoding |
| break; |
| |
| case UserData::ENCODING_IA5: |
| case UserData::ENCODING_7BIT_ASCII: |
| case UserData::ENCODING_GSM_7BIT_ALPHABET: |
| numFields = ((recordLen * 8) - 5) / 7; // subtract 5 bits for encoding |
| break; |
| |
| case UserData::ENCODING_UNICODE_16: |
| numFields = (recordLen - 1) / 2; |
| break; |
| |
| default: |
| numFields = 0; // unsupported encoding |
| } |
| |
| alertUserData->numFields = numFields; |
| alertUserData->payload = inStream->readByteVector(recordLen * 8 - 5); |
| decodeUserDataPayload(alertUserData, false); |
| bData->userData = alertUserData; |
| break; |
| } |
| case 1: // Type 1 elements |
| { |
| category = inStream->read(8); |
| responseType = inStream->read(8); |
| severity = inStream->read(4); |
| urgency = inStream->read(4); |
| certainty = inStream->read(4); |
| inStream->skip(recordLen * 8 - 28); |
| break; |
| } |
| default: |
| //Rlog.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType); |
| inStream->skip(recordLen * 8); |
| break; |
| } |
| } |
| |
| bData->cmasWarningInfo = make_shared<SmsCbCmasInfo>(messageClass, category, |
| responseType, severity, urgency, certainty); |
| } |
| |
| void BearerData::decodeIs91VoicemailStatus(std::shared_ptr<BearerData> bData) { |
| //uint8_t array[bData->userData->payload.size()]; |
| //std::copy(bData->userData->payload.begin(), bData->userData->payload.end(), array); |
| auto inStream = make_shared<BitwiseInputStream>(bData->userData->payload); |
| |
| int dataLen = inStream->available() / 6; // 6-bit packed character encoding. |
| int numFields = bData->userData->numFields; |
| if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { |
| throw runtime_error("IS-91 voicemail status decoding failed"); |
| } |
| string strbuf; |
| while (inStream->available() >= 6) { |
| strbuf.push_back(UserData::ASCII_MAP[inStream->read(6)]); |
| } |
| string data = strbuf; |
| bData->numberOfMessages = std::stoi(data.substr(0, 2)); |
| char prioCode = data.at(2); |
| if (prioCode == ' ') { |
| bData->priority = PRIORITY_NORMAL; |
| } else if (prioCode == '!') { |
| bData->priority = PRIORITY_URGENT; |
| } else { |
| throw runtime_error( |
| string("IS-91 voicemail status decoding failed: ") |
| + string("illegal priority setting (") + std::to_string(prioCode) |
| + string(")")); |
| } |
| bData->priorityIndicatorSet = true; |
| bData->userData->payloadStr = data.substr(3, numFields - 6); |
| } |
| void BearerData::decodeIs91Cli(std::shared_ptr<BearerData> bData) { |
| //uint8_t array[bData->userData->payload.size()]; |
| //std::copy(bData->userData->payload.begin(), bData->userData->payload.end(), array); |
| auto inStream = make_shared<BitwiseInputStream>(bData->userData->payload); |
| int dataLen = inStream->available() / 4; // 4-bit packed DTMF digit encoding. |
| int numFields = bData->userData->numFields; |
| if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { |
| throw runtime_error("IS-91 voicemail status decoding failed"); |
| } |
| auto addr = make_shared<CdmaSmsAddress>(); |
| addr->digitMode = CdmaSmsAddress::DIGIT_MODE_4BIT_DTMF; |
| addr->origBytes = bData->userData->payload; |
| addr->numberOfDigits = (uint8_t) numFields; |
| decodeSmsAddress(addr); |
| bData->callbackNumber = addr; |
| } |
| |
| void BearerData::decodeIs91ShortMessage(std::shared_ptr<BearerData> bData) { |
| //uint8_t array[bData->userData->payload.size()]; |
| //std::copy(bData->userData->payload.begin(), bData->userData->payload.end(), array); |
| auto inStream = make_shared<BitwiseInputStream>(bData->userData->payload); |
| int dataLen = inStream->available() / 6; // 6-bit packed character encoding. |
| int numFields = bData->userData->numFields; |
| // dataLen may be > 14 characters due to octet padding |
| if ((numFields > 14) || (dataLen < numFields)) { |
| throw runtime_error("IS-91 short message decoding failed"); |
| } |
| string strbuf; |
| for (int i = 0; i < numFields; i++) { |
| strbuf.push_back(UserData::ASCII_MAP[inStream->read(6)]); |
| } |
| bData->userData->payloadStr = strbuf; |
| } |
| |
| void BearerData::decodeIs91(std::shared_ptr<BearerData> bData) { |
| switch (bData->userData->msgType) { |
| case UserData::IS91_MSG_TYPE_VOICEMAIL_STATUS: |
| decodeIs91VoicemailStatus(bData); |
| break; |
| case UserData::IS91_MSG_TYPE_CLI: |
| decodeIs91Cli(bData); |
| break; |
| case UserData::IS91_MSG_TYPE_SHORT_MESSAGE_FULL: |
| case UserData::IS91_MSG_TYPE_SHORT_MESSAGE: |
| decodeIs91ShortMessage(bData); |
| break; |
| default: |
| throw runtime_error( |
| string("unsupported IS-91 message type (") |
| + std::to_string(bData->userData->msgType) + string(")")); |
| } |
| } |
| std::shared_ptr<BearerData> BearerData::decode(std::vector<uint8_t> smsData, |
| int serviceCategory) { |
| //uint8_t array[smsData.size()]; |
| //std::copy(smsData.begin(), smsData.end(), array); |
| auto inStream = make_shared<BitwiseInputStream>(smsData); |
| shared_ptr<BearerData> bData = make_shared<BearerData>(); |
| int foundSubparamMask = 0; |
| while (inStream->available() > 0) { |
| uint32_t subparamId = inStream->read((uint32_t) 8); |
| int subparamIdBit = 1 << subparamId; |
| // int is 4 bytes. This duplicate check has a limit to Id number up to 32 (4*8) |
| // as 32th bit is the max bit in int. |
| // Per 3GPP2 C.S0015-B Table 4.5-1 Bearer Data Subparameter Identifiers: |
| // last defined subparam ID is 23 (00010111 = 0x17 = 23). |
| // Only do duplicate subparam ID check if subparam is within defined value as |
| // reserved subparams are just skipped. |
| if ((foundSubparamMask & subparamIdBit) != 0 |
| && (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER |
| && subparamId <= SUBPARAM_ID_LAST_DEFINED)) { |
| throw runtime_error( |
| string("illegal duplicate subparameter (") |
| + std::to_string(subparamId) + string(")")); |
| } |
| bool decodeSuccess; |
| switch (subparamId) { |
| case SUBPARAM_MESSAGE_IDENTIFIER: |
| decodeSuccess = decodeMessageId(bData, inStream); |
| break; |
| case SUBPARAM_USER_DATA: |
| decodeSuccess = decodeUserData(bData, inStream); |
| break; |
| case SUBPARAM_USER_RESPONSE_CODE: |
| decodeSuccess = decodeUserResponseCode(bData, inStream); |
| break; |
| case SUBPARAM_REPLY_OPTION: |
| decodeSuccess = decodeReplyOption(bData, inStream); |
| break; |
| case SUBPARAM_NUMBER_OF_MESSAGES: |
| decodeSuccess = decodeMsgCount(bData, inStream); |
| break; |
| case SUBPARAM_CALLBACK_NUMBER: |
| decodeSuccess = decodeCallbackNumber(bData, inStream); |
| break; |
| case SUBPARAM_MESSAGE_STATUS: |
| decodeSuccess = decodeMsgStatus(bData, inStream); |
| break; |
| case SUBPARAM_MESSAGE_CENTER_TIME_STAMP: |
| decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream); |
| break; |
| case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE: |
| decodeSuccess = decodeValidityAbs(bData, inStream); |
| break; |
| case SUBPARAM_VALIDITY_PERIOD_RELATIVE: |
| decodeSuccess = decodeValidityRel(bData, inStream); |
| break; |
| case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE: |
| decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream); |
| break; |
| case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE: |
| decodeSuccess = decodeDeferredDeliveryRel(bData, inStream); |
| break; |
| case SUBPARAM_PRIVACY_INDICATOR: |
| decodeSuccess = decodePrivacyIndicator(bData, inStream); |
| break; |
| case SUBPARAM_LANGUAGE_INDICATOR: |
| decodeSuccess = decodeLanguageIndicator(bData, inStream); |
| break; |
| case SUBPARAM_MESSAGE_DISPLAY_MODE: |
| decodeSuccess = decodeDisplayMode(bData, inStream); |
| break; |
| case SUBPARAM_PRIORITY_INDICATOR: |
| decodeSuccess = decodePriorityIndicator(bData, inStream); |
| break; |
| case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY: |
| decodeSuccess = decodeMsgDeliveryAlert(bData, inStream); |
| break; |
| case SUBPARAM_MESSAGE_DEPOSIT_INDEX: |
| decodeSuccess = decodeDepositIndex(bData, inStream); |
| break; |
| case SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA: |
| decodeSuccess = decodeServiceCategoryProgramData(bData, inStream); |
| break; |
| default: |
| decodeSuccess = decodeReserved(bData, inStream, subparamId); |
| } |
| if (decodeSuccess |
| && (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER |
| && subparamId <= SUBPARAM_ID_LAST_DEFINED)) { |
| foundSubparamMask |= subparamIdBit; |
| } |
| } |
| if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) { |
| throw runtime_error("missing MESSAGE_IDENTIFIER subparam"); |
| } |
| if (bData->userData != nullptr) { |
| if (isCmasAlertCategory(serviceCategory)) { |
| decodeCmasUserData(bData, serviceCategory); |
| } else if (bData->userData->msgEncoding |
| == UserData::ENCODING_IS91_EXTENDED_PROTOCOL) { |
| if ((foundSubparamMask ^ (1 << SUBPARAM_MESSAGE_IDENTIFIER) |
| ^ (1 << SUBPARAM_USER_DATA)) != 0) { |
| // Rlog.e(LOG_TAG, "IS-91 must occur without extra subparams (" + |
| // foundSubparamMask + ")"); |
| } |
| decodeIs91(bData); |
| } else { |
| decodeUserDataPayload(bData->userData, bData->hasUserDataHeader); |
| } |
| } |
| return bData; |
| } |
| |
| std::string BearerData::toString() { |
| string builder; |
| builder.append("BearerData "); |
| builder.append("{ messageType=" + std::to_string(messageType)); |
| builder.append(", messageId=" + std::to_string(messageId)); |
| builder.append( |
| string(", priority=") |
| + (priorityIndicatorSet ? std::to_string(priority) : "unset")); |
| builder.append( |
| string(", privacy=") |
| + (privacyIndicatorSet ? std::to_string(privacy) : "unset")); |
| builder.append( |
| string(", alert=") |
| + (alertIndicatorSet ? std::to_string(alert) : "unset")); |
| builder.append( |
| string(", displayMode=") |
| + (displayModeSet ? std::to_string(displayMode) : "unset")); |
| builder.append( |
| string(", language=") |
| + (languageIndicatorSet ? std::to_string(language) : "unset")); |
| builder.append( |
| string(", errorClass=") |
| + (messageStatusSet ? std::to_string(errorClass) : "unset")); |
| builder.append( |
| string(", msgStatus=") |
| + (messageStatusSet ? std::to_string(messageStatus) : "unset")); |
| builder.append( |
| string( |
| ", msgCenterTimeStamp= unset") /*+ string (std::asctime(&msgCenterTimeStamp))*/); |
| builder.append( |
| string( |
| ", validityPeriodAbsolute= unset")/* + string(std::asctime(&validityPeriodAbsolute)))*/); |
| builder.append( |
| string(", validityPeriodRelative= ") |
| + ((validityPeriodRelativeSet) ? |
| std::to_string(validityPeriodRelative) : "unset")); |
| builder.append( |
| string( |
| ", deferredDeliveryTimeAbsolute= ") /*+ string(std::asctime(&deferredDeliveryTimeAbsolute))*/); |
| builder.append( |
| string(", deferredDeliveryTimeRelative=") |
| + ((deferredDeliveryTimeRelativeSet) ? |
| std::to_string(deferredDeliveryTimeRelative) : "unset")); |
| builder.append(", userAckReq=" + std::to_string(userAckReq)); |
| builder.append(", deliveryAckReq=" + std::to_string(deliveryAckReq)); |
| builder.append(", readAckReq=" + std::to_string(readAckReq)); |
| builder.append(", reportReq=" + std::to_string(reportReq)); |
| builder.append(", numberOfMessages=" + std::to_string(numberOfMessages)); |
| builder.append(string(", callbackNumber=") + string(callbackNumber ? callbackNumber->toString() : "unset")); |
| builder.append(", depositIndex=" + std::to_string(depositIndex)); |
| builder.append(", hasUserDataHeader=" + std::to_string(hasUserDataHeader)); |
| builder.append( |
| ", userData=" + string(userData ? userData->toString() : "unset")); |
| builder.append(" }"); |
| return builder; |
| } |